"""WhatsApp channel implementation using Meta Cloud API.

This module provides the WhatsApp channel implementation for
receiving and sending messages via WhatsApp Business API.

References:
    - Meta Cloud API: https://developers.facebook.com/docs/whatsapp/cloud-api
    - Webhooks: https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks
"""

import hashlib
import hmac
import logging
import os
from typing import Any, Dict, List, Optional

from agent_factory.channels.base import (
    BaseChannel,
    ChannelError,
    ChannelMessage,
    ChannelResponse,
    ChannelSendError,
    ChannelType,
)

logger = logging.getLogger(__name__)


class WhatsAppChannel(BaseChannel):
    """WhatsApp channel implementation using Meta Cloud API.

    Handles WhatsApp webhook events and sends messages via the Meta Cloud API.

    Example:
        channel = WhatsAppChannel(
            access_token="EAAG...",
            phone_number_id="123456789",
            verify_token="my-verify-token",
            app_secret="abc123",  # For signature verification
        )

        # Parse incoming webhook
        message = channel.parse_event(webhook_event)

        # Send response
        channel.send_message(ChannelResponse(
            text="Hello!",
            channel_id=message.channel_id,  # recipient phone number
        ))
    """

    def __init__(
        self,
        access_token: Optional[str] = None,
        phone_number_id: Optional[str] = None,
        verify_token: Optional[str] = None,
        app_secret: Optional[str] = None,
        api_version: str = "v21.0",
    ):
        """Initialize WhatsApp channel.

        Args:
            access_token: Meta access token (or WHATSAPP_ACCESS_TOKEN env var)
            phone_number_id: WhatsApp Business phone number ID (or WHATSAPP_PHONE_NUMBER_ID env var)
            verify_token: Webhook verification token (or WHATSAPP_VERIFY_TOKEN env var)
            app_secret: Meta app secret for signature verification (or WHATSAPP_APP_SECRET env var)
            api_version: Meta Graph API version (default: v21.0)
        """
        self._access_token = access_token or os.getenv("WHATSAPP_ACCESS_TOKEN")
        self._phone_number_id = phone_number_id or os.getenv("WHATSAPP_PHONE_NUMBER_ID")
        self._verify_token = verify_token or os.getenv("WHATSAPP_VERIFY_TOKEN")
        self._app_secret = app_secret or os.getenv("WHATSAPP_APP_SECRET")
        self._api_version = api_version
        self._api_base = f"https://graph.facebook.com/{api_version}"

    @property
    def channel_type(self) -> ChannelType:
        return ChannelType.WHATSAPP

    def verify_request(self, headers: Dict[str, str], body: bytes) -> bool:
        """Verify WhatsApp webhook signature.

        Meta signs webhook payloads using the app secret.
        The signature is in the X-Hub-Signature-256 header.
        """
        if not self._app_secret:
            logger.warning("WhatsApp app secret not configured, skipping verification")
            return True

        # Get signature from headers (case-insensitive)
        signature = None
        for key, value in headers.items():
            if key.lower() == "x-hub-signature-256":
                signature = value
                break

        if not signature:
            logger.warning("No X-Hub-Signature-256 header found")
            return False

        # Signature format: sha256=<hex_digest>
        if not signature.startswith("sha256="):
            return False

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

        # Compute expected signature
        computed = hmac.new(
            self._app_secret.encode(),
            body,
            hashlib.sha256,
        ).hexdigest()

        return hmac.compare_digest(computed, expected_signature)

    def is_verification_request(self, event: Dict[str, Any]) -> bool:
        """Check if this is a webhook verification request from Meta.

        Meta sends GET requests with hub.mode, hub.verify_token, and hub.challenge
        to verify the webhook endpoint.

        Args:
            event: Lambda event (API Gateway format)

        Returns:
            True if this is a verification request
        """
        # Check for query parameters (API Gateway format)
        query_params = event.get("queryStringParameters", {}) or {}
        return (
            query_params.get("hub.mode") == "subscribe"
            and query_params.get("hub.verify_token") is not None
        )

    def get_verification_response(self, event: Dict[str, Any]) -> Dict[str, Any]:
        """Get the response for webhook verification.

        Args:
            event: Lambda event with query parameters

        Returns:
            Lambda response dict with the challenge or error
        """
        query_params = event.get("queryStringParameters", {}) or {}
        mode = query_params.get("hub.mode")
        token = query_params.get("hub.verify_token")
        challenge = query_params.get("hub.challenge")

        if mode == "subscribe" and token == self._verify_token:
            logger.info("WhatsApp webhook verification successful")
            return {
                "statusCode": 200,
                "headers": {"Content-Type": "text/plain"},
                "body": challenge,
            }
        else:
            logger.warning("WhatsApp webhook verification failed")
            return {
                "statusCode": 403,
                "headers": {"Content-Type": "text/plain"},
                "body": "Forbidden",
            }

    def parse_event(self, event: Dict[str, Any]) -> Optional[ChannelMessage]:
        """Parse WhatsApp webhook event into ChannelMessage.

        WhatsApp webhooks have a nested structure:
        {
            "object": "whatsapp_business_account",
            "entry": [{
                "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
                "changes": [{
                    "value": {
                        "messaging_product": "whatsapp",
                        "metadata": {...},
                        "contacts": [...],
                        "messages": [...]
                    },
                    "field": "messages"
                }]
            }]
        }
        """
        if event.get("object") != "whatsapp_business_account":
            return None

        entries = event.get("entry", [])
        if not entries:
            return None

        # Process first entry (typically there's only one)
        entry = entries[0]
        changes = entry.get("changes", [])

        for change in changes:
            if change.get("field") != "messages":
                continue

            value = change.get("value", {})
            messages = value.get("messages", [])

            if not messages:
                continue

            # Process first message
            message = messages[0]

            # Only handle text messages for now
            if message.get("type") != "text":
                logger.info(f"Skipping non-text message type: {message.get('type')}")
                continue

            sender = message.get("from", "")
            text = message.get("text", {}).get("body", "")
            message_id = message.get("id", "")
            timestamp = message.get("timestamp", "")

            if not sender or not text:
                continue

            # Get contact info if available
            contacts = value.get("contacts", [])
            contact_name = ""
            if contacts:
                contact_name = contacts[0].get("profile", {}).get("name", "")

            # Get metadata
            metadata_info = value.get("metadata", {})
            phone_number_id = metadata_info.get("phone_number_id", "")

            return ChannelMessage(
                channel_type=ChannelType.WHATSAPP,
                channel_id=sender,  # Sender's phone number (for replies)
                user_id=sender,
                text=text,
                thread_id=None,  # WhatsApp doesn't have threads like Slack
                metadata={
                    "message_id": message_id,
                    "timestamp": timestamp,
                    "contact_name": contact_name,
                    "phone_number_id": phone_number_id,
                    "whatsapp_business_account_id": entry.get("id"),
                },
                raw_event=event,
            )

        return None

    def send_message(self, response: ChannelResponse) -> bool:
        """Send message via WhatsApp Cloud API."""
        if not self._access_token:
            raise ChannelSendError(
                "WhatsApp access token not configured",
                channel_type=ChannelType.WHATSAPP,
            )

        if not self._phone_number_id:
            raise ChannelSendError(
                "WhatsApp phone number ID not configured",
                channel_type=ChannelType.WHATSAPP,
            )

        try:
            import requests
        except ImportError:
            raise ChannelError(
                "requests is required for WhatsApp. "
                "Install with: pip install agent-factory-core[whatsapp]",
                channel_type=ChannelType.WHATSAPP,
            )

        url = f"{self._api_base}/{self._phone_number_id}/messages"
        headers = {
            "Authorization": f"Bearer {self._access_token}",
            "Content-Type": "application/json",
        }

        # Build message payload
        payload: Dict[str, Any] = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": response.channel_id,  # Recipient phone number
            "type": "text",
            "text": {
                "preview_url": False,
                "body": response.text,
            },
        }

        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=30)
            result = resp.json()

            if resp.status_code >= 400:
                error = result.get("error", {})
                logger.error(
                    "WhatsApp API error",
                    extra={
                        "status_code": resp.status_code,
                        "error_code": error.get("code"),
                        "error_message": error.get("message"),
                    },
                )
                return False

            logger.info(
                "WhatsApp message sent",
                extra={"message_id": result.get("messages", [{}])[0].get("id")},
            )
            return True

        except Exception as e:
            logger.exception("Failed to send WhatsApp message")
            raise ChannelSendError(
                f"Failed to send WhatsApp message: {e}",
                channel_type=ChannelType.WHATSAPP,
                is_retryable=True,
            )

    def send_template_message(
        self,
        to: str,
        template_name: str,
        language_code: str = "en",
        components: Optional[List[Dict[str, Any]]] = None,
    ) -> bool:
        """Send a template message (for initiating conversations).

        WhatsApp requires template messages for initiating conversations
        or messaging users who haven't messaged you in 24 hours.

        Args:
            to: Recipient phone number (with country code)
            template_name: Approved template name
            language_code: Template language code (e.g., "en", "es")
            components: Optional template components (header, body, buttons)

        Returns:
            True if sent successfully
        """
        if not self._access_token or not self._phone_number_id:
            raise ChannelSendError(
                "WhatsApp credentials not configured",
                channel_type=ChannelType.WHATSAPP,
            )

        try:
            import requests
        except ImportError:
            raise ChannelError(
                "requests is required for WhatsApp",
                channel_type=ChannelType.WHATSAPP,
            )

        url = f"{self._api_base}/{self._phone_number_id}/messages"
        headers = {
            "Authorization": f"Bearer {self._access_token}",
            "Content-Type": "application/json",
        }

        payload: Dict[str, Any] = {
            "messaging_product": "whatsapp",
            "to": to,
            "type": "template",
            "template": {
                "name": template_name,
                "language": {"code": language_code},
            },
        }

        if components:
            payload["template"]["components"] = components

        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=30)
            return resp.status_code < 400
        except Exception:
            return False

    def mark_as_read(self, message_id: str) -> bool:
        """Mark a message as read.

        This sends a read receipt to the user.

        Args:
            message_id: WhatsApp message ID to mark as read

        Returns:
            True if successful
        """
        if not self._access_token or not self._phone_number_id:
            return False

        try:
            import requests
        except ImportError:
            return False

        url = f"{self._api_base}/{self._phone_number_id}/messages"
        headers = {
            "Authorization": f"Bearer {self._access_token}",
            "Content-Type": "application/json",
        }

        payload = {
            "messaging_product": "whatsapp",
            "status": "read",
            "message_id": message_id,
        }

        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=10)
            return resp.status_code < 400
        except Exception:
            return False

    def send_reaction(self, message_id: str, emoji: str, to: str) -> bool:
        """Send a reaction to a message.

        Args:
            message_id: Message ID to react to
            emoji: Emoji to send (e.g., "")
            to: Recipient phone number

        Returns:
            True if successful
        """
        if not self._access_token or not self._phone_number_id:
            return False

        try:
            import requests
        except ImportError:
            return False

        url = f"{self._api_base}/{self._phone_number_id}/messages"
        headers = {
            "Authorization": f"Bearer {self._access_token}",
            "Content-Type": "application/json",
        }

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "reaction",
            "reaction": {
                "message_id": message_id,
                "emoji": emoji,
            },
        }

        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=10)
            return resp.status_code < 400
        except Exception:
            return False

    def format_response(
        self,
        text: str,
        channel_id: str,
        **kwargs,
    ) -> ChannelResponse:
        """Format response for WhatsApp."""
        return ChannelResponse(
            text=text,
            channel_id=channel_id,
            thread_id=None,  # WhatsApp doesn't have threads
            metadata=kwargs,
        )

    def get_context(self, message: ChannelMessage) -> Dict[str, Any]:
        """Get WhatsApp-specific context."""
        return {
            "channel_type": self.channel_type.value,
            "channel_id": message.channel_id,
            "user_id": message.user_id,
            "phone_number": message.user_id,
            "contact_name": message.metadata.get("contact_name"),
            "message_id": message.metadata.get("message_id"),
        }
