"""ORCH-5 (M-7): webhook delivery de-duplication helper.

Webhook providers (Gitea/Plane) retry deliveries on timeout, network reset, or
manual replay. Without idempotency a retried delivery re-enters the pipeline and
spawns a duplicate run (the ET-009 incident class: parallel conveyors on one
repo). This module computes a stable per-delivery id so the webhook handlers can
INSERT-OR-IGNORE into events and skip the dispatch on a repeat.

delivery_id format: ``f"{source}:{raw_or_hash}"`` where source prefixes
gitea/plane so their id-spaces never collide. ``raw`` is the provider's native
delivery header (a GUID) when present; otherwise we fall back to a sha256 of the
body (a retried identical body yields the same hash).
"""

import hashlib


def _sha256_hex(*parts: str) -> str:
    h = hashlib.sha256()
    for p in parts:
        h.update(p.encode("utf-8", "replace"))
    return h.hexdigest()


def gitea_delivery_id(headers, event_type: str, body: bytes) -> str:
    """Compute the delivery_id for a Gitea webhook.

    Prefers the ``X-Gitea-Delivery`` header (a per-delivery GUID). Falls back to
    sha256(source + event_type + body) so a retried identical body still maps to
    one id even if Gitea omitted the header.
    """
    raw = (headers.get("X-Gitea-Delivery") or "").strip()
    if not raw:
        raw = _sha256_hex("gitea", event_type or "", body.decode("utf-8", "replace"))
    return f"gitea:{raw}"


def plane_delivery_id(headers, body: bytes) -> str:
    """Compute the delivery_id for a Plane webhook.

    Plane does not reliably send a delivery header, so we try a couple of common
    names and otherwise fall back to sha256("plane" + body): a retried identical
    body yields the same id.
    """
    raw = (
        headers.get("X-Plane-Delivery")
        or headers.get("X-Hook-Delivery")
        or ""
    ).strip()
    if not raw:
        raw = _sha256_hex("plane", body.decode("utf-8", "replace"))
    return f"plane:{raw}"
