from __future__ import annotations

import hashlib
import hmac
import time
from typing import Optional


class WebhookVerifyResult:
    """Result of webhook signature verification."""

    __slots__ = ("valid", "reason")

    def __init__(self, valid: bool, reason: Optional[str] = None) -> None:
        self.valid = valid
        self.reason = reason

    def __repr__(self) -> str:
        return f"WebhookVerifyResult(valid={self.valid!r}, reason={self.reason!r})"

    def __bool__(self) -> bool:
        return self.valid


def verify_webhook_signature(
    payload: str,
    signature_header: str,
    secret: str,
    *,
    timestamp: Optional[str] = None,
    tolerance: int = 300,
) -> WebhookVerifyResult:
    """Verify the cryptographic signature on an incoming webhook request.

    Args:
        payload: The raw request body as a string.
        signature_header: The value of the ``X-Optropic-Signature`` header.
            Expected format: ``sha256=<hex>``.
        secret: The webhook signing secret provided when the endpoint was
            created.
        timestamp: The value of the ``X-Optropic-Timestamp`` header (unix
            seconds as a string). When provided, the function checks that
            the timestamp is within *tolerance* seconds of the current time.
        tolerance: Maximum allowed age of the timestamp in seconds.
            Defaults to 300 (5 minutes).  Ignored when *timestamp* is
            ``None``.

    Returns:
        A :class:`WebhookVerifyResult` with ``valid=True`` if the
        signature (and optional timestamp) check passed.
    """
    # ── 1. Validate signature header format ────────────────────────────
    if not signature_header or not signature_header.startswith("sha256="):
        return WebhookVerifyResult(False, "Invalid signature format")

    # ── 2. Validate timestamp freshness (if provided) ──────────────────
    if timestamp is not None:
        try:
            ts = int(timestamp)
        except (ValueError, TypeError):
            return WebhookVerifyResult(False, "Invalid timestamp")

        age = abs(int(time.time()) - ts)
        if age > tolerance:
            return WebhookVerifyResult(
                False,
                f"Timestamp too old ({age}s > {tolerance}s tolerance)",
            )

    # ── 3. Compute expected HMAC-SHA256 ────────────────────────────────
    if timestamp is not None:
        signed_payload = f"{timestamp}.{payload}"
    else:
        signed_payload = payload

    expected_hex = signature_header[len("sha256="):]
    computed = hmac.new(
        secret.encode("utf-8"),
        signed_payload.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    # ── 4. Timing-safe comparison ──────────────────────────────────────
    if not hmac.compare_digest(computed, expected_hex):
        return WebhookVerifyResult(False, "Signature mismatch")

    return WebhookVerifyResult(True)
