"""Unified webhook signature verification."""

from __future__ import annotations

import hashlib
import hmac
import json
import time as _time

from primitif._exceptions import InvalidSignature


def verify_webhook(
    *,
    secret: str,
    signature: str,
    body: str | bytes,
    timestamp: str | None = None,
    tolerance: int = 300,
) -> dict:
    """Verify a webhook payload and return the parsed data.

    Works for both Mail webhooks (with timestamp) and Approval webhooks (without).

    Args:
        secret: The webhook signing secret.
        signature: Value of the signature header (e.g. X-Webhook-Signature).
        body: Raw request body (str or bytes).
        timestamp: Value of the timestamp header (unix seconds). Required for
            Mail webhooks, optional for Approval webhooks.
        tolerance: Max age in seconds (default 300 = 5 min). Only used when
            timestamp is provided. Set to 0 to disable.

    Returns:
        Parsed payload dict.

    Raises:
        InvalidSignature: If the signature is invalid or timestamp is too old.
    """
    if not signature:
        raise InvalidSignature("Missing signature")
    if isinstance(body, bytes):
        body = body.decode("utf-8")

    # Replay protection (only when timestamp provided)
    if timestamp is not None and tolerance > 0:
        try:
            ts = int(timestamp)
        except (ValueError, TypeError):
            raise InvalidSignature("Invalid timestamp") from None
        if abs(_time.time() - ts) > tolerance:
            raise InvalidSignature("Timestamp too old")

    # Build the signed message
    if timestamp is not None:
        message = f"{timestamp}.{body}"
    else:
        message = body

    expected = hmac.new(
        secret.encode(), message.encode(), hashlib.sha256
    ).hexdigest()

    # Strip sha256= prefix if present
    sig = signature.removeprefix("sha256=")

    if not hmac.compare_digest(expected, sig):
        raise InvalidSignature("Signature mismatch")

    return json.loads(body)
