"""
WhatsApp Business API Integration

Integrates with Meta's WhatsApp Business API for:
- Receiving messages
- Sending responses
- Template messages
- Media handling
"""
from typing import Any, Dict, List, Optional
from enum import Enum
from datetime import datetime
from pydantic import BaseModel
import httpx
import logging
import hmac
import hashlib

logger = logging.getLogger(__name__)


class WhatsAppMessageType(str, Enum):
    """WhatsApp message types"""
    TEXT = "text"
    IMAGE = "image"
    DOCUMENT = "document"
    AUDIO = "audio"
    VIDEO = "video"
    LOCATION = "location"
    CONTACTS = "contacts"
    STICKER = "sticker"
    INTERACTIVE = "interactive"
    TEMPLATE = "template"
    REACTION = "reaction"


class WhatsAppConfig(BaseModel):
    """WhatsApp Business API configuration"""
    phone_number_id: str
    business_account_id: str
    access_token: str
    verify_token: str  # For webhook verification
    app_secret: str    # For signature verification
    api_version: str = "v17.0"


class WhatsAppMessage(BaseModel):
    """Incoming WhatsApp message"""
    message_id: str
    from_number: str
    timestamp: datetime
    message_type: WhatsAppMessageType
    text: Optional[str] = None
    media_url: Optional[str] = None
    media_id: Optional[str] = None
    caption: Optional[str] = None
    location: Optional[Dict[str, float]] = None
    contacts: Optional[List[Dict]] = None
    context: Optional[Dict] = None  # Reply context
    metadata: Dict[str, Any] = {}


class WhatsAppService:
    """
    WhatsApp Business API integration service.

    Handles:
    - Webhook verification
    - Message receiving
    - Message sending (text, media, templates)
    - Read receipts
    - Interactive messages (buttons, lists)
    """

    BASE_URL = "https://graph.facebook.com"

    def __init__(self, config: WhatsAppConfig):
        self.config = config
        self.api_url = f"{self.BASE_URL}/{config.api_version}/{config.phone_number_id}"

    # ==================== WEBHOOK HANDLING ====================

    def verify_webhook(self, mode: str, token: str, challenge: str) -> Optional[str]:
        """
        Verify webhook subscription from Meta.
        Called when Meta sends GET request to verify webhook.
        """
        if mode == "subscribe" and token == self.config.verify_token:
            logger.info("WhatsApp webhook verified successfully")
            return challenge
        logger.warning("WhatsApp webhook verification failed")
        return None

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """
        Verify incoming webhook signature.
        Meta sends X-Hub-Signature-256 header.
        """
        expected = hmac.new(
            self.config.app_secret.encode(),
            payload,
            hashlib.sha256
        ).hexdigest()

        return hmac.compare_digest(f"sha256={expected}", signature)

    def parse_webhook(self, payload: Dict[str, Any]) -> List[WhatsAppMessage]:
        """
        Parse incoming webhook payload into messages.

        Webhook payload structure:
        {
            "object": "whatsapp_business_account",
            "entry": [{
                "id": "BUSINESS_ACCOUNT_ID",
                "changes": [{
                    "value": {
                        "messaging_product": "whatsapp",
                        "metadata": {...},
                        "contacts": [{...}],
                        "messages": [{...}]
                    },
                    "field": "messages"
                }]
            }]
        }
        """
        messages = []

        try:
            for entry in payload.get("entry", []):
                for change in entry.get("changes", []):
                    value = change.get("value", {})

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

                    for msg_data in value.get("messages", []):
                        message = self._parse_message(msg_data)
                        if message:
                            messages.append(message)

        except Exception as e:
            logger.error(f"Error parsing WhatsApp webhook: {str(e)}")

        return messages

    def _parse_message(self, msg_data: Dict) -> Optional[WhatsAppMessage]:
        """Parse individual message from webhook"""
        try:
            msg_type = msg_data.get("type", "text")

            message = WhatsAppMessage(
                message_id=msg_data["id"],
                from_number=msg_data["from"],
                timestamp=datetime.fromtimestamp(int(msg_data["timestamp"])),
                message_type=WhatsAppMessageType(msg_type)
            )

            # Parse based on type
            if msg_type == "text":
                message.text = msg_data.get("text", {}).get("body")

            elif msg_type in ["image", "audio", "video", "document", "sticker"]:
                media_data = msg_data.get(msg_type, {})
                message.media_id = media_data.get("id")
                message.caption = media_data.get("caption")

            elif msg_type == "location":
                loc = msg_data.get("location", {})
                message.location = {
                    "latitude": loc.get("latitude"),
                    "longitude": loc.get("longitude"),
                    "name": loc.get("name"),
                    "address": loc.get("address")
                }

            elif msg_type == "contacts":
                message.contacts = msg_data.get("contacts", [])

            elif msg_type == "interactive":
                interactive = msg_data.get("interactive", {})
                if interactive.get("type") == "button_reply":
                    message.text = interactive.get("button_reply", {}).get("title")
                elif interactive.get("type") == "list_reply":
                    message.text = interactive.get("list_reply", {}).get("title")

            # Context (if this is a reply)
            if "context" in msg_data:
                message.context = msg_data["context"]

            return message

        except Exception as e:
            logger.error(f"Error parsing message: {str(e)}")
            return None

    # ==================== SENDING MESSAGES ====================

    async def send_text(
        self,
        to: str,
        text: str,
        preview_url: bool = False
    ) -> Dict[str, Any]:
        """Send a text message"""
        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "text",
            "text": {
                "preview_url": preview_url,
                "body": text
            }
        }

        return await self._send_message(payload)

    async def send_image(
        self,
        to: str,
        image_url: str = None,
        image_id: str = None,
        caption: str = None
    ) -> Dict[str, Any]:
        """Send an image"""
        image_data = {}
        if image_url:
            image_data["link"] = image_url
        elif image_id:
            image_data["id"] = image_id

        if caption:
            image_data["caption"] = caption

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "image",
            "image": image_data
        }

        return await self._send_message(payload)

    async def send_document(
        self,
        to: str,
        document_url: str = None,
        document_id: str = None,
        filename: str = None,
        caption: str = None
    ) -> Dict[str, Any]:
        """Send a document"""
        doc_data = {}
        if document_url:
            doc_data["link"] = document_url
        elif document_id:
            doc_data["id"] = document_id

        if filename:
            doc_data["filename"] = filename
        if caption:
            doc_data["caption"] = caption

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "document",
            "document": doc_data
        }

        return await self._send_message(payload)

    async def send_buttons(
        self,
        to: str,
        body: str,
        buttons: List[Dict[str, str]],
        header: str = None,
        footer: str = None
    ) -> Dict[str, Any]:
        """
        Send interactive button message.

        buttons: [{"id": "btn_1", "title": "Option 1"}, ...]
        Max 3 buttons.
        """
        interactive = {
            "type": "button",
            "body": {"text": body},
            "action": {
                "buttons": [
                    {"type": "reply", "reply": btn}
                    for btn in buttons[:3]
                ]
            }
        }

        if header:
            interactive["header"] = {"type": "text", "text": header}
        if footer:
            interactive["footer"] = {"text": footer}

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "interactive",
            "interactive": interactive
        }

        return await self._send_message(payload)

    async def send_list(
        self,
        to: str,
        body: str,
        button_text: str,
        sections: List[Dict[str, Any]],
        header: str = None,
        footer: str = None
    ) -> Dict[str, Any]:
        """
        Send interactive list message.

        sections: [{
            "title": "Section 1",
            "rows": [
                {"id": "row_1", "title": "Row 1", "description": "..."},
                ...
            ]
        }]
        """
        interactive = {
            "type": "list",
            "body": {"text": body},
            "action": {
                "button": button_text,
                "sections": sections
            }
        }

        if header:
            interactive["header"] = {"type": "text", "text": header}
        if footer:
            interactive["footer"] = {"text": footer}

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "interactive",
            "interactive": interactive
        }

        return await self._send_message(payload)

    async def send_template(
        self,
        to: str,
        template_name: str,
        language_code: str = "en",
        components: List[Dict] = None
    ) -> Dict[str, Any]:
        """
        Send a pre-approved template message.

        Used for:
        - First contact (24h window)
        - Proactive messages
        - Notifications

        components: [
            {
                "type": "header",
                "parameters": [{"type": "image", "image": {"link": "..."}}]
            },
            {
                "type": "body",
                "parameters": [
                    {"type": "text", "text": "John"},
                    {"type": "text", "text": "Order #123"}
                ]
            }
        ]
        """
        template = {
            "name": template_name,
            "language": {"code": language_code}
        }

        if components:
            template["components"] = components

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "template",
            "template": template
        }

        return await self._send_message(payload)

    async def send_location(
        self,
        to: str,
        latitude: float,
        longitude: float,
        name: str = None,
        address: str = None
    ) -> Dict[str, Any]:
        """Send a location"""
        location = {
            "latitude": latitude,
            "longitude": longitude
        }
        if name:
            location["name"] = name
        if address:
            location["address"] = address

        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "location",
            "location": location
        }

        return await self._send_message(payload)

    async def mark_as_read(self, message_id: str) -> Dict[str, Any]:
        """Mark a message as read"""
        payload = {
            "messaging_product": "whatsapp",
            "status": "read",
            "message_id": message_id
        }

        return await self._send_message(payload)

    async def send_reaction(
        self,
        to: str,
        message_id: str,
        emoji: str
    ) -> Dict[str, Any]:
        """React to a message with emoji"""
        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "reaction",
            "reaction": {
                "message_id": message_id,
                "emoji": emoji
            }
        }

        return await self._send_message(payload)

    # ==================== MEDIA HANDLING ====================

    async def get_media_url(self, media_id: str) -> Optional[str]:
        """Get download URL for a media file"""
        url = f"{self.BASE_URL}/{self.config.api_version}/{media_id}"

        async with httpx.AsyncClient() as client:
            response = await client.get(
                url,
                headers={"Authorization": f"Bearer {self.config.access_token}"}
            )

            if response.status_code == 200:
                return response.json().get("url")

            logger.error(f"Failed to get media URL: {response.text}")
            return None

    async def download_media(self, media_url: str) -> Optional[bytes]:
        """Download media content"""
        async with httpx.AsyncClient() as client:
            response = await client.get(
                media_url,
                headers={"Authorization": f"Bearer {self.config.access_token}"}
            )

            if response.status_code == 200:
                return response.content

            logger.error(f"Failed to download media: {response.text}")
            return None

    async def upload_media(
        self,
        file_content: bytes,
        mime_type: str,
        filename: str
    ) -> Optional[str]:
        """Upload media to WhatsApp servers, returns media_id"""
        url = f"{self.api_url}/media"

        async with httpx.AsyncClient() as client:
            files = {
                "file": (filename, file_content, mime_type)
            }
            data = {
                "messaging_product": "whatsapp",
                "type": mime_type
            }

            response = await client.post(
                url,
                headers={"Authorization": f"Bearer {self.config.access_token}"},
                files=files,
                data=data
            )

            if response.status_code == 200:
                return response.json().get("id")

            logger.error(f"Failed to upload media: {response.text}")
            return None

    # ==================== INTERNAL ====================

    async def _send_message(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Send message to WhatsApp API"""
        url = f"{self.api_url}/messages"

        async with httpx.AsyncClient() as client:
            response = await client.post(
                url,
                headers={
                    "Authorization": f"Bearer {self.config.access_token}",
                    "Content-Type": "application/json"
                },
                json=payload
            )

            result = response.json()

            if response.status_code == 200:
                logger.info(f"WhatsApp message sent: {result.get('messages', [{}])[0].get('id')}")
                return {"success": True, "data": result}
            else:
                logger.error(f"WhatsApp send failed: {result}")
                return {"success": False, "error": result}


# ==================== WHATSAPP CHANNEL HANDLER ====================

class WhatsAppChannelHandler:
    """
    High-level handler that integrates WhatsApp with AI-360.

    Connects:
    - WhatsApp messages → AI Service
    - AI responses → WhatsApp replies
    """

    def __init__(self, whatsapp_service: WhatsAppService, ai_service, client_id: int):
        self.whatsapp = whatsapp_service
        self.ai_service = ai_service
        self.client_id = client_id
        self._conversations: Dict[str, str] = {}  # phone -> conversation_id

    async def handle_incoming(self, message: WhatsAppMessage) -> None:
        """Handle incoming WhatsApp message"""
        phone = message.from_number

        # Mark as read
        await self.whatsapp.mark_as_read(message.message_id)

        # Get or create conversation
        conversation_id = self._conversations.get(phone)
        if not conversation_id:
            import uuid
            conversation_id = str(uuid.uuid4())
            self._conversations[phone] = conversation_id

        # Extract text content
        user_message = message.text or message.caption or ""

        if not user_message:
            # Handle non-text messages
            if message.message_type == WhatsAppMessageType.LOCATION:
                user_message = f"[Location shared: {message.location}]"
            elif message.message_type in [WhatsAppMessageType.IMAGE, WhatsAppMessageType.DOCUMENT]:
                user_message = "[Media shared]"
            else:
                return  # Ignore other types for now

        # Process with AI
        ai_response = await self.ai_service.process_message(
            client_id=self.client_id,
            conversation_id=conversation_id,
            message=user_message,
            context={"channel": "whatsapp", "phone": phone}
        )

        # Send response
        response_text = ai_response.get("response", "I'm sorry, I couldn't process your request.")

        # Check if response should be interactive
        if ai_response.get("buttons"):
            await self.whatsapp.send_buttons(
                to=phone,
                body=response_text,
                buttons=ai_response["buttons"]
            )
        elif ai_response.get("list"):
            await self.whatsapp.send_list(
                to=phone,
                body=response_text,
                button_text="Select",
                sections=ai_response["list"]
            )
        else:
            await self.whatsapp.send_text(to=phone, text=response_text)
