"""
Kick webhook handler for processing incoming webhook events.
"""

import base64
import binascii
import hmac
import hashlib
from typing import Callable, Dict, Optional

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from common.platform.kick.model import (
    KickChatMessage,
    KickChannelFollow,
    KickSubscriptionRenewal,
    KickSubscriptionNew,
    KickSubscriptionGift,
    KickLivestreamStatus,
)
from fastapi import Request, HTTPException
from pydantic import ValidationError

from common.logging import get_logger, span

logger = get_logger(__name__)

# Official Kick public key from docs:
# https://docs.kick.com/events/webhook-security
DEFAULT_KICK_PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/+l1WnlRrGSolDMA+A8
6rAhMbQGmQ2SapVcGM3zq8ANXjnhDWocMqfWcTd95btDydITa10kDvHzw9WQOqp2
MZI7ZyrfzJuz5nhTPCiJwTwnEtWft7nV14BYRDHvlfqPUaZ+1KR4OCaO/wWIk/rQ
L/TjY0M70gse8rlBkbo2a8rKhu69RQTRsoaf4DVhDPEeSeI5jVrRDGAMGL3cGuyY
6CLKGdjVEM78g3JfYOvDU/RvfqD7L89TZ3iN94jrmWdGz34JNlEI5hqK8dd7C5EF
BEbZ5jgB8s8ReQV8H+MkuffjdAj3ajDDX3DOJMIut1lBrUVD1AaSrGCKHooWoL2e
twIDAQAB
-----END PUBLIC KEY-----"""


class KickWebhookHandler:
    """
    Handler for Kick webhook events.

    This class provides methods to process different types of webhook events from Kick.

    Attributes:
    -----------
    event_handlers: Dict[str, Callable]
        Dictionary mapping event types to handler functions
    webhook_secret: Optional[str]
        Secret key for webhook signature verification

    Methods:
    --------
    register_handler(event_type: str, handler: Callable):
        Register a handler function for a specific event type
    process_webhook(request: Request):
        Process an incoming webhook request
    """

    def __init__(
        self, webhook_secret: Optional[str] = None, public_key_pem: Optional[str] = None
    ):
        self.event_handlers: Dict[str, Callable] = {}
        # Legacy compatibility only (Kick docs use Kick-Event-Signature + public key)
        self.webhook_secret = webhook_secret
        self._public_key = self._load_public_key(
            public_key_pem or DEFAULT_KICK_PUBLIC_KEY_PEM
        )
        logger.info("init Kick webhook handler")

    def set_webhook_secret(self, secret: str):
        """Set legacy webhook secret for backward-compatible HMAC verification."""
        self.webhook_secret = secret

    def set_public_key(self, public_key_pem: str) -> None:
        """Override the Kick public key used for signature verification."""
        self._public_key = self._load_public_key(public_key_pem)

    @staticmethod
    def _load_public_key(public_key_pem: str) -> rsa.RSAPublicKey:
        parsed_key = serialization.load_pem_public_key(public_key_pem.encode("utf-8"))
        if not isinstance(parsed_key, rsa.RSAPublicKey):
            raise ValueError("Kick webhook public key is not RSA")
        return parsed_key

    def _verify_legacy_signature(self, payload: bytes, signature: str) -> bool:
        """
        Verify legacy webhook signature using HMAC-SHA256.

        Args:
            payload: Raw request body
            signature: Signature from legacy Kick-Signature header

        Returns:
            bool: True if signature is valid, False otherwise
        """
        if not self.webhook_secret:
            logger.warning(
                "No webhook secret configured, skipping signature verification"
            )
            return True

        # Create expected signature
        expected_signature = hmac.new(
            self.webhook_secret.encode("utf-8"), payload, hashlib.sha256
        ).hexdigest()

        # Remove 'sha256=' prefix if present
        if signature.startswith("sha256="):
            signature = signature[7:]

        return hmac.compare_digest(expected_signature, signature)

    def _verify_kick_event_signature(
        self,
        body: bytes,
        signature: str,
        message_id: str,
        message_timestamp: str,
    ) -> bool:
        """
        Verify Kick webhook signature using official Kick public-key flow.

        Docs reference:
        - Kick-Event-Signature
        - Kick-Event-Message-Id
        - Kick-Event-Message-Timestamp
        - signed payload: "<id>.<timestamp>.<raw body>"
        """
        signed_payload = b".".join(
            [message_id.encode("utf-8"), message_timestamp.encode("utf-8"), body]
        )
        try:
            signature_bytes = base64.b64decode(signature, validate=True)
        except (binascii.Error, ValueError) as exc:
            logger.error(
                "Failed to decode Kick-Event-Signature",
                extra={"error": str(exc)},
            )
            return False

        try:
            self._public_key.verify(
                signature_bytes,
                signed_payload,
                padding.PKCS1v15(),
                hashes.SHA256(),
            )
            return True
        except InvalidSignature:
            return False

    def register_handler(self, event_type: str, handler: Callable):
        """
        Register a handler function for a specific event type.

        Args:
            event_type (str): The event type (e.g., "chat.message.sent")
            handler (Callable): The function to handle this event type
        """
        logger.info(f"Registering handler for Kick event type: {event_type}")
        self.event_handlers[event_type] = handler

    async def process_webhook(self, request: Request):
        """
        Process an incoming webhook request.

        Args:
            request (Request): The FastAPI request object

        Returns:
            dict: A response indicating the webhook was processed

        Raises:
            HTTPException: If the webhook cannot be processed
        """
        # Get the event type from headers
        event_type = request.headers.get("Kick-Event-Type")
        event_version = request.headers.get("Kick-Event-Version")
        event_signature = request.headers.get("Kick-Event-Signature")
        legacy_signature = request.headers.get("Kick-Signature")
        message_id = request.headers.get("Kick-Event-Message-Id")
        message_timestamp = request.headers.get("Kick-Event-Message-Timestamp")

        logger.info(
            "process_webhook Kick",
            extra={
                "event_type": event_type,
                "event_version": event_version,
                "has_event_signature": bool(event_signature),
                "has_legacy_signature": bool(legacy_signature),
            },
        )

        if not event_type:
            logger.error("Missing Kick-Event-Type header in webhook request")
            raise HTTPException(
                status_code=400, detail="Missing Kick-Event-Type header"
            )

        attributes = {
            "event_type": event_type,
            "event_version": event_version,
            "signature_mode": (
                "kick_event_signature"
                if event_signature
                else "legacy_kick_signature"
                if legacy_signature
                else "missing"
            ),
        }

        logger.info("Received Kick webhook event", extra=attributes)

        with span(logger, "kick_webhook_process", attributes):
            # Get raw body for signature verification
            body = await request.body()

            # Primary path: official Kick signature headers (RSA with Kick public key).
            if event_signature:
                if not message_id or not message_timestamp:
                    logger.error(
                        "Missing Kick signature message headers",
                        extra={
                            "has_message_id": bool(message_id),
                            "has_message_timestamp": bool(message_timestamp),
                        },
                    )
                    from eventsub_service.core.exceptions import (
                        WebhookVerificationError,
                    )

                    raise WebhookVerificationError("Missing webhook signature metadata")

                if not self._verify_kick_event_signature(
                    body=body,
                    signature=event_signature,
                    message_id=message_id,
                    message_timestamp=message_timestamp,
                ):
                    logger.error("Kick webhook signature verification failed")
                    from eventsub_service.core.exceptions import (
                        WebhookVerificationError,
                    )

                    raise WebhookVerificationError("Invalid webhook signature")
            # Backward-compatibility path for older local tests/senders.
            elif legacy_signature and self.webhook_secret:
                if not self._verify_legacy_signature(body, legacy_signature):
                    logger.error("Legacy Kick webhook signature verification failed")
                    from eventsub_service.core.exceptions import (
                        WebhookVerificationError,
                    )

                    raise WebhookVerificationError("Invalid webhook signature")
            else:
                logger.error("Missing Kick-Event-Signature header")
                from eventsub_service.core.exceptions import WebhookVerificationError

                raise WebhookVerificationError("Missing webhook signature")

            # Parse the payload based on the event type
            try:
                import json

                payload = json.loads(body.decode("utf-8"))
                logger.debug(
                    "Webhook payload received",
                    extra={"payload_size": len(str(payload))},
                )

                # Process the event based on its type
                if event_type == "chat.message.sent":
                    event_data = KickChatMessage(**payload)
                    logger.info(
                        "Processing chat message event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                elif event_type == "channel.followed":
                    event_data = KickChannelFollow(**payload)
                    logger.info(
                        "Processing channel follow event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                elif event_type == "channel.subscription.renewal":
                    event_data = KickSubscriptionRenewal(**payload)
                    logger.info(
                        "Processing subscription renewal event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                elif event_type == "channel.subscription.new":
                    event_data = KickSubscriptionNew(**payload)
                    logger.info(
                        "Processing new subscription event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                elif event_type == "channel.subscription.gifts":
                    event_data = KickSubscriptionGift(**payload)
                    logger.info(
                        "Processing subscription gift event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                elif event_type == "livestream.status.updated":
                    event_data = KickLivestreamStatus(**payload)
                    logger.info(
                        "Processing livestream status update event",
                        extra={
                            "channel_id": event_data.data.channel_id
                            if hasattr(event_data, "data")
                            else None
                        },
                    )
                else:
                    logger.warning("Unhandled event type", extra=attributes)
                    return {
                        "status": "ignored",
                        "reason": f"Unhandled event type: {event_type}",
                    }

                # Call the appropriate handler if registered
                handler = self.event_handlers.get(event_type)
                if handler:
                    with span(
                        logger,
                        f"kick_webhook_handle_{event_type.replace('.', '_')}",
                        attributes,
                    ):
                        await handler(event_data)
                        logger.info(
                            "Event handler executed successfully", extra=attributes
                        )
                    return {"status": "processed", "event_type": event_type}
                else:
                    logger.warning(
                        "No handler registered for event type", extra=attributes
                    )
                    return {
                        "status": "ignored",
                        "reason": f"No handler for event type: {event_type}",
                    }

            except ValidationError as e:
                logger.error(
                    f"Validation error processing webhook: {e}",
                    extra={"error": str(e), **attributes},
                )
                raise HTTPException(
                    status_code=400, detail=f"Invalid payload: {str(e)}"
                )
            except Exception as e:
                logger.error(
                    f"Error processing webhook: {e}",
                    extra={"error": str(e), **attributes},
                )
                raise HTTPException(
                    status_code=500, detail=f"Error processing webhook: {str(e)}"
                )


# Create a singleton instance for dependency injection
webhook_handler = KickWebhookHandler()


def get_webhook_handler() -> KickWebhookHandler:
    """
    Dependency to get the webhook handler instance.
    """
    return webhook_handler


def configure_webhook_handler(webhook_secret: str) -> None:
    """
    Configure legacy webhook secret fallback for signature verification.

    Args:
        webhook_secret: Secret key for legacy HMAC signature verification
    """
    global webhook_handler
    webhook_handler.set_webhook_secret(webhook_secret)


__all__ = [
    "KickWebhookHandler",
    "webhook_handler",
    "get_webhook_handler",
    "configure_webhook_handler",
]
