"""Webhook signature verification for BeeL. webhooks."""

import hashlib
import hmac
import json
import time
from dataclasses import dataclass
from typing import Any, Dict, Optional


class WebhookVerificationError(Exception):
    """Raised when webhook signature verification fails."""
    pass


@dataclass
class WebhookEvent:
    """Parsed webhook event."""
    id: str
    type: str
    created_at: str
    data: Dict[str, Any]
    test: Optional[bool] = None
    raw_body: str = ""

    @classmethod
    def from_dict(cls, d: Dict[str, Any], raw: str = "") -> "WebhookEvent":
        return cls(
            id=d.get("id", ""),
            type=d.get("type", ""),
            created_at=d.get("created_at", ""),
            data=d.get("data", {}),
            test=d.get("test"),
            raw_body=raw,
        )


class WebhookVerifier:
    """
    Verify BeeL. webhook signatures (HMAC-SHA256).

    Every webhook delivery includes a ``BeeL-Signature`` header::

        BeeL-Signature: t=1741362026,v1=3c4f7a...

    Example (Flask)::

        from beel import WebhookVerifier, WebhookVerificationError

        verifier = WebhookVerifier("your_webhook_secret")

        @app.route("/webhooks/beel", methods=["POST"])
        def handle_webhook():
            signature = request.headers.get("BeeL-Signature")
            raw_body = request.get_data(as_text=True)
            try:
                event = verifier.verify(raw_body, signature)
                print(f"Event: {event.type}")
                return "OK", 200
            except WebhookVerificationError as e:
                return str(e), 400

    Example (Django)::

        from beel import WebhookVerifier, WebhookVerificationError

        verifier = WebhookVerifier("your_webhook_secret")

        def webhook_view(request):
            signature = request.META.get("HTTP_BEEL_SIGNATURE")
            raw_body = request.body.decode("utf-8")
            try:
                event = verifier.verify(raw_body, signature)
                # Process event...
                return HttpResponse("OK", status=200)
            except WebhookVerificationError:
                return HttpResponse("Invalid signature", status=400)

    Example (FastAPI)::

        from beel import WebhookVerifier, WebhookVerificationError
        from fastapi import Request, HTTPException

        verifier = WebhookVerifier("your_webhook_secret")

        @app.post("/webhooks/beel")
        async def handle_webhook(request: Request):
            signature = request.headers.get("beel-signature")
            raw_body = (await request.body()).decode("utf-8")
            try:
                event = verifier.verify(raw_body, signature)
                return {"status": "ok"}
            except WebhookVerificationError:
                raise HTTPException(status_code=400, detail="Invalid signature")
    """

    def __init__(self, secret: str, tolerance_seconds: int = 300):
        if not secret or not secret.strip():
            raise ValueError("Webhook secret is required")
        self._secret = secret
        self._tolerance_seconds = tolerance_seconds

    def verify(self, raw_body: str, signature_header: Optional[str]) -> WebhookEvent:
        """
        Verify a webhook signature and return the parsed event.

        Args:
            raw_body: Raw request body (before JSON parsing).
            signature_header: Value of the ``BeeL-Signature`` header.

        Returns:
            Parsed WebhookEvent.

        Raises:
            WebhookVerificationError: If signature is invalid or event is too old.
        """
        if not signature_header:
            raise WebhookVerificationError("Missing BeeL-Signature header")

        timestamp, signature = self._parse_signature_header(signature_header)

        now = int(time.time())
        age = abs(now - timestamp)
        if age > self._tolerance_seconds:
            raise WebhookVerificationError(
                f"Webhook timestamp too old ({age}s ago, max {self._tolerance_seconds}s allowed)"
            )

        expected = self._compute_signature(timestamp, raw_body)
        if not hmac.compare_digest(signature, expected):
            raise WebhookVerificationError("Invalid webhook signature")

        try:
            data = json.loads(raw_body)
        except json.JSONDecodeError as e:
            raise WebhookVerificationError(f"Invalid JSON in webhook body: {e}")

        return WebhookEvent.from_dict(data, raw_body)

    def _parse_signature_header(self, header: str) -> tuple:
        timestamp = None
        signature = None

        for part in header.split(","):
            kv = part.split("=", 1)
            if len(kv) != 2:
                continue
            key, value = kv[0].strip(), kv[1].strip()
            if key == "t":
                try:
                    timestamp = int(value)
                except ValueError:
                    raise WebhookVerificationError("Invalid timestamp in signature header")
            elif key == "v1":
                signature = value

        if timestamp is None or signature is None:
            raise WebhookVerificationError("Invalid signature header format")

        return timestamp, signature

    def _compute_signature(self, timestamp: int, body: str) -> str:
        signed_payload = f"{timestamp}.{body}"
        return hmac.new(
            self._secret.encode("utf-8"),
            signed_payload.encode("utf-8"),
            hashlib.sha256,
        ).hexdigest()
