"""Webhook utilities for HAX SDK."""

from __future__ import annotations

import hashlib
import hmac
import json
from typing import Any

from pydantic import BaseModel, ConfigDict

from hax.models.request import Request


class WebhookEvent(BaseModel):
    """A parsed webhook event from HAX."""

    model_config = ConfigDict(populate_by_name=True)

    type: str  # "request.sent", "request.opened", "request.completed", "request.expired"
    timestamp: str
    data: dict[str, Any]

    @property
    def event_type(self) -> str:
        """Get the event type without the 'request.' prefix."""
        if self.type.startswith("request."):
            return self.type[8:]  # Remove "request." prefix
        return self.type

    @property
    def request_id(self) -> str:
        """Get the request ID from the event data."""
        return self.data["id"]  # type: ignore[no-any-return]

    @property
    def request(self) -> Request:
        """Parse the data as a Request object.

        Note: The webhook payload contains a subset of request fields.
        Some fields like payload may not be present.
        """
        # Create a minimal valid request from webhook data
        request_data = {
            "id": self.data.get("id"),
            "projectId": self.data.get("projectId", ""),
            "type": self.data.get("type"),
            "status": self.data.get("status"),
            "metadata": self.data.get("metadata"),
            "response": self.data.get("response"),
            "respondedBy": self.data.get("respondedBy"),
            "respondedAt": self.data.get("respondedAt"),
            "createdAt": self.data.get("createdAt", self.timestamp),
            "updatedAt": self.data.get("updatedAt", self.timestamp),
            "payload": self.data.get("payload", {}),
        }
        return Request.model_validate(request_data)

    @property
    def metadata(self) -> dict[str, Any] | None:
        """Get the request metadata from the event data."""
        return self.data.get("metadata")

    @property
    def response(self) -> dict[str, Any] | None:
        """Get the response data if the request was completed."""
        return self.data.get("response")


def verify_signature(
    payload: bytes,
    signature: str,
    secret: str,
) -> bool:
    """Verify a webhook signature.

    Args:
        payload: The raw request body bytes.
        signature: The X-Hax-Signature header value (e.g., "sha256=abc123...").
        secret: The webhook secret (e.g., "whsec_...").

    Returns:
        True if the signature is valid, False otherwise.

    Example:
        >>> is_valid = verify_signature(
        ...     payload=request.body,
        ...     signature=request.headers["X-Hax-Signature"],
        ...     secret="whsec_...",
        ... )
    """
    if not signature.startswith("sha256="):
        return False

    expected_signature = signature[7:]  # Remove "sha256=" prefix

    computed = hmac.new(
        key=secret.encode("utf-8"),
        msg=payload,
        digestmod=hashlib.sha256,
    ).hexdigest()

    # Use constant-time comparison to prevent timing attacks
    return hmac.compare_digest(computed, expected_signature)


def parse_event(
    payload: bytes | str,
    *,
    private_key: dict[str, Any] | None = None,
) -> WebhookEvent:
    """Parse a webhook payload into a typed event.

    Args:
        payload: The raw request body (bytes or string).
        private_key: Optional RSA private key (JWK format) to decrypt encrypted response.
            If provided, the response field is decrypted if it's encrypted.

    Returns:
        A WebhookEvent object with decrypted response if key provided.

    Example:
        >>> event = parse_event(request.body)
        >>> if event.type == "request.completed":
        ...     print(f"Request {event.request_id} completed!")
        ...     print(f"Response: {event.response}")

    With decryption:
        >>> _, private_key = generate_key_pair("my-secret")
        >>> event = parse_event(request.body, private_key=private_key)
        >>> # Response is automatically decrypted if it was encrypted
    """
    if isinstance(payload, bytes):
        payload = payload.decode("utf-8")

    data = json.loads(payload)

    # Decrypt response field if private key provided
    if private_key is not None:
        from hax.crypto import decrypt_response, is_encrypted_response

        event_data = data.get("data", {})

        # Only decrypt the response field (not payload or metadata)
        if is_encrypted_response(event_data.get("response")):
            event_data["response"] = decrypt_response(
                event_data["response"]["_encrypted"], private_key
            )

    return WebhookEvent.model_validate(data)


def sign_payload(payload: dict[str, Any], secret: str) -> str:
    """Sign a webhook payload (for testing purposes).

    Args:
        payload: The payload dict to sign.
        secret: The webhook secret.

    Returns:
        The signature in "sha256={hex}" format.
    """
    body = json.dumps(payload, separators=(",", ":"))
    signature = hmac.new(
        key=secret.encode("utf-8"),
        msg=body.encode("utf-8"),
        digestmod=hashlib.sha256,
    ).hexdigest()
    return f"sha256={signature}"
