"""
Webhook management for Meta Ads API events.

This module provides functionality for managing webhook subscriptions,
validating webhook payloads, and handling webhook events.
"""

import hashlib
import hmac
import json
import time
from typing import Any, Dict, List, Optional

from .logging import get_logger
from .meta_client import MetaClient
from .errors import MetaApiError

logger = get_logger(__name__)


class WebhookSubscription:
    """Represents a webhook subscription."""
    
    def __init__(
        self,
        subscription_id: str,
        object_type: str,
        callback_url: str,
        fields: List[str],
        verify_token: str,
        active: bool = True,
        created_time: Optional[str] = None,
    ):
        self.subscription_id = subscription_id
        self.object_type = object_type
        self.callback_url = callback_url
        self.fields = fields
        self.verify_token = verify_token
        self.active = active
        self.created_time = created_time


class WebhookEvent:
    """Represents a webhook event payload."""
    
    def __init__(
        self,
        object_type: str,
        entry: List[Dict[str, Any]],
        subscription_id: str,
        timestamp: int,
    ):
        self.object_type = object_type
        self.entry = entry
        self.subscription_id = subscription_id
        self.timestamp = timestamp


class WebhookManager:
    """Manages webhook subscriptions and event handling."""
    
    def __init__(self, client: MetaClient):
        self._client = client
        self._subscriptions: Dict[str, WebhookSubscription] = {}
    
    async def create_subscription(
        self,
        object_type: str,
        callback_url: str,
        fields: List[str],
        verify_token: str,
        ad_account_id: Optional[str] = None,
    ) -> WebhookSubscription:
        """
        Create a new webhook subscription.
        
        Args:
            object_type: Type of object to subscribe to (e.g., 'ad_account', 'campaign')
            callback_url: URL to receive webhook events
            fields: Fields to include in webhook payload
            verify_token: Token for webhook verification
            ad_account_id: Ad account ID (required for some object types)
        
        Returns:
            WebhookSubscription: The created subscription
        """
        try:
            payload = {
                "object": object_type,
                "callback_url": callback_url,
                "fields": ",".join(fields),
                "verify_token": verify_token,
            }
            
            if ad_account_id:
                payload["ad_account_id"] = ad_account_id
            
            response = await self._client.post(
                "subscriptions",
                body=payload,
                encoding="form",
            )
            
            subscription = WebhookSubscription(
                subscription_id=response["id"],
                object_type=object_type,
                callback_url=callback_url,
                fields=fields,
                verify_token=verify_token,
                active=response.get("active", True),
                created_time=response.get("created_time"),
            )
            
            self._subscriptions[subscription.subscription_id] = subscription
            logger.info(
                "Webhook subscription created",
                subscription_id=subscription.subscription_id,
                object_type=object_type,
            )
            
            return subscription
            
        except MetaApiError as exc:
            logger.error(
                "Failed to create webhook subscription",
                object_type=object_type,
                error=exc.to_dict(),
            )
            raise
    
    async def list_subscriptions(self) -> List[WebhookSubscription]:
        """List all webhook subscriptions."""
        try:
            response = await self._client.get("subscriptions")
            
            subscriptions = []
            for sub_data in response.get("data", []):
                subscription = WebhookSubscription(
                    subscription_id=sub_data["id"],
                    object_type=sub_data["object"],
                    callback_url=sub_data["callback_url"],
                    fields=sub_data.get("fields", "").split(",") if sub_data.get("fields") else [],
                    verify_token=sub_data.get("verify_token", ""),
                    active=sub_data.get("active", True),
                    created_time=sub_data.get("created_time"),
                )
                subscriptions.append(subscription)
                self._subscriptions[subscription.subscription_id] = subscription
            
            return subscriptions
            
        except MetaApiError as exc:
            logger.error("Failed to list webhook subscriptions", error=exc.to_dict())
            raise
    
    async def delete_subscription(self, subscription_id: str) -> bool:
        """Delete a webhook subscription."""
        try:
            await self._client.delete(f"subscriptions/{subscription_id}")
            
            if subscription_id in self._subscriptions:
                del self._subscriptions[subscription_id]
            
            logger.info("Webhook subscription deleted", subscription_id=subscription_id)
            return True
            
        except MetaApiError as exc:
            logger.error(
                "Failed to delete webhook subscription",
                subscription_id=subscription_id,
                error=exc.to_dict(),
            )
            raise
    
    def verify_webhook_signature(
        self,
        payload: str,
        signature: str,
        app_secret: str,
    ) -> bool:
        """
        Verify webhook signature to ensure authenticity.
        
        Args:
            payload: Raw webhook payload
            signature: X-Hub-Signature-256 header value
            app_secret: App secret for signature verification
        
        Returns:
            bool: True if signature is valid
        """
        try:
            # Remove 'sha256=' prefix if present
            if signature.startswith("sha256="):
                signature = signature[7:]
            
            # Calculate expected signature
            expected_signature = hmac.new(
                app_secret.encode("utf-8"),
                payload.encode("utf-8"),
                hashlib.sha256,
            ).hexdigest()
            
            # Compare signatures using constant-time comparison
            return hmac.compare_digest(signature, expected_signature)
            
        except Exception as e:
            logger.error("Webhook signature verification failed", error=str(e))
            return False
    
    def parse_webhook_payload(self, payload: str) -> WebhookEvent:
        """
        Parse webhook payload into WebhookEvent object.
        
        Args:
            payload: Raw webhook payload JSON string
        
        Returns:
            WebhookEvent: Parsed webhook event
        """
        try:
            data = json.loads(payload)
            
            return WebhookEvent(
                object_type=data.get("object"),
                entry=data.get("entry", []),
                subscription_id=data.get("subscription_id"),
                timestamp=data.get("time", int(time.time())),
            )
            
        except (json.JSONDecodeError, KeyError) as e:
            logger.error("Failed to parse webhook payload", error=str(e))
            raise ValueError(f"Invalid webhook payload: {e}")
    
    async def handle_webhook_event(
        self,
        event: WebhookEvent,
        handler_func: Optional[callable] = None,
    ) -> Dict[str, Any]:
        """
        Handle a webhook event.
        
        Args:
            event: WebhookEvent object
            handler_func: Optional custom handler function
        
        Returns:
            Dict containing processing results
        """
        try:
            results = {
                "processed": 0,
                "errors": [],
                "subscription_id": event.subscription_id,
                "timestamp": event.timestamp,
            }
            
            for entry in event.entry:
                try:
                    # Process each entry in the webhook
                    if handler_func:
                        await handler_func(entry, event)
                    else:
                        # Default processing - just log the event
                        logger.info(
                            "Webhook event received",
                            object_type=event.object_type,
                            entry_id=entry.get("id"),
                            changes=entry.get("changes", []),
                        )
                    
                    results["processed"] += 1
                    
                except Exception as e:
                    error_msg = f"Failed to process webhook entry: {e}"
                    results["errors"].append(error_msg)
                    logger.error(
                        "Webhook entry processing failed",
                        entry=entry,
                        error=str(e),
                    )
            
            return results
            
        except Exception as e:
            logger.error("Webhook event handling failed", error=str(e))
            raise
    
    def get_subscription(self, subscription_id: str) -> Optional[WebhookSubscription]:
        """Get a webhook subscription by ID."""
        return self._subscriptions.get(subscription_id)
    
    async def test_webhook(self, subscription_id: str) -> bool:
        """Test a webhook subscription by sending a test event."""
        try:
            await self._client.post(f"subscriptions/{subscription_id}/test")
            logger.info("Webhook test sent", subscription_id=subscription_id)
            return True
            
        except MetaApiError as exc:
            logger.error(
                "Webhook test failed",
                subscription_id=subscription_id,
                error=exc.to_dict(),
            )
            raise
