"""
WhatsApp Cloud API Service
Handles sending WhatsApp messages via Meta's API
"""
import os
import httpx
import re
import logging
from typing import Optional
from datetime import datetime

# Setup logging
logger = logging.getLogger(__name__)


class WhatsAppError(Exception):
    """Custom exception for WhatsApp API errors"""
    pass


class WhatsAppService:
    """Service for sending WhatsApp messages via Meta API"""
    
    def __init__(self):
        self.access_token = os.getenv("WHATSAPP_ACCESS_TOKEN")
        self.phone_number_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID")
        self.api_version = os.getenv("WHATSAPP_API_VERSION", "v20.0")
        self.verify_token = os.getenv("WHATSAPP_VERIFY_TOKEN", "")
        
        if not self.access_token or not self.phone_number_id:
            raise WhatsAppError(
                "WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID must be set in environment"
            )
        
        self.base_url = f"https://graph.facebook.com/{self.api_version}"
        self.headers = {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json"
        }
    
    @staticmethod
    def normalize_phone_to_e164(phone: str) -> str:
        """
        Normalize phone number to E.164 format
        E.164 format: +[country code][number] with no spaces or punctuation
        
        Examples:
        - "+1-555-123-4567" -> "+15551234567"
        - "555-123-4567" -> "+15551234567" (assumes US)
        - "+972541234567" -> "+972541234567"
        - "0541234567" -> "+972541234567" (Israeli format: 0 means +972)
        """
        # Remove all non-digit characters except leading +
        cleaned = re.sub(r"[^\d+]", "", phone)
        
        # If it starts with +, it might already have country code
        if cleaned.startswith("+"):
            return cleaned
        
        # Handle Israeli format (starts with 0)
        if cleaned.startswith("0"):
            # Israeli number starting with 0: convert to +972
            # 0541234567 -> 972541234567 -> +972541234567
            return f"+972{cleaned[1:]}"
        
        # If it's a US number (10 digits), prepend +1
        if len(cleaned) == 10 and all(c.isdigit() for c in cleaned):
            return f"+1{cleaned}"
        
        # If it's already got country code but no +, add it
        if len(cleaned) >= 11 and all(c.isdigit() for c in cleaned):
            return f"+{cleaned}"
        
        # Default: just prepend +
        return f"+{cleaned}"
    
    def validate_phone(self, phone: str) -> bool:
        """
        Validate that phone number is valid E.164 format
        """
        try:
            e164 = self.normalize_phone_to_e164(phone)
            # E.164 should start with + and be 10-15 digits total
            return e164.startswith("+") and 10 <= len(e164) <= 15 and all(c.isdigit() for c in e164[1:])
        except Exception:
            return False
    
    @staticmethod
    def validate_template_params(params: list, expected_count: int = 8) -> bool:
        """
        Validate template parameters
        
        Args:
            params: List of parameters to send
            expected_count: Expected number of parameters (default: 8)
                            Wedding template = 1 header param + 7 body params = 8 total
        
        Returns:
            True if valid, otherwise raises WhatsAppError
        """
        if not params:
            raise WhatsAppError(f"Parameters list is empty, expected {expected_count}")
        
        if len(params) != expected_count:
            raise WhatsAppError(
                f"Parameter count mismatch: got {len(params)}, expected {expected_count}. "
                f"Parameters: {params}"
            )
        
        # Ensure all params are strings and non-empty
        for i, param in enumerate(params, 1):
            param_str = str(param).strip()
            if not param_str:
                raise WhatsAppError(
                    f"Parameter #{i} is empty or None. "
                    f"All {expected_count} parameters must have values. Parameters: {params}"
                )
        
        return True
    
    async def send_text_message(
        self,
        to_phone: str,
        message_text: str,
        context_message_id: Optional[str] = None
    ) -> dict:
        """
        Send a text message via WhatsApp Cloud API
        
        Args:
            to_phone: Recipient phone number (will be normalized to E.164)
            message_text: Message body
            context_message_id: Optional message ID to reply to
        
        Returns:
            dict with message_id and status
            
        Raises:
            WhatsAppError: If message fails to send
        """
        # Normalize phone number
        to_e164 = self.normalize_phone_to_e164(to_phone)
        
        if not self.validate_phone(to_e164):
            raise WhatsAppError(f"Invalid phone number: {to_phone}")
        
        # Build payload
        payload = {
            "messaging_product": "whatsapp",
            "to": to_e164,
            "type": "text",
            "text": {
                "body": message_text
            }
        }
        
        # Add context if provided (for replies)
        if context_message_id:
            payload["context"] = {
                "message_id": context_message_id
            }
        
        # Send to API
        url = f"{self.base_url}/{self.phone_number_id}/messages"
        
        try:
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    url,
                    json=payload,
                    headers=self.headers,
                    timeout=30.0
                )
            
            if response.status_code not in (200, 201):
                error_data = response.json()
                error_msg = error_data.get("error", {}).get("message", "Unknown error")
                raise WhatsAppError(
                    f"WhatsApp API error ({response.status_code}): {error_msg}"
                )
            
            result = response.json()
            return {
                "message_id": result.get("messages", [{}])[0].get("id"),
                "status": "sent",
                "to": to_e164,
                "timestamp": datetime.utcnow().isoformat(),
                "type": "text"
            }
        
        except httpx.HTTPError as e:
            raise WhatsAppError(f"HTTP request failed: {str(e)}")
        except Exception as e:
            raise WhatsAppError(f"Failed to send WhatsApp message: {str(e)}")
    
    async def send_template_message(
        self,
        to_phone: str,
        template_name: str,
        language_code: str = "en",
        parameters: Optional[list] = None
    ) -> dict:
        """
        Send a pre-approved template message via WhatsApp Cloud API
        
        Args:
            to_phone: Recipient phone number
            template_name: Template name (must be approved by Meta)
            language_code: Language code (default: en)
            parameters: List of parameter values for template placeholders (must be 7 for wedding template)
        
        Returns:
            dict with message_id and status
        """
        to_e164 = self.normalize_phone_to_e164(to_phone)
        
        if not self.validate_phone(to_e164):
            raise WhatsAppError(f"Invalid phone number: {to_phone}")
        
        # Validate parameters
        if not parameters:
            raise WhatsAppError("Parameters list is required for template messages")
        
        self.validate_template_params(parameters, expected_count=8)
        
        # Convert all parameters to strings
        param_list = [str(p).strip() for p in parameters]
        
        # Build payload with correct Meta structure (includes "components" array)
        # Template structure: Header (1 param) + Body (7 params)
        # param_list[0] = guest_name (header)
        # param_list[1] = guest_name (body {{1}} - repeated from header)
        # param_list[2] = groom_name
        # param_list[3] = bride_name
        # param_list[4] = hall_name
        # param_list[5] = event_date
        # param_list[6] = event_time
        # param_list[7] = guest_link
        payload = {
            "messaging_product": "whatsapp",
            "to": to_e164,
            "type": "template",
            "template": {
                "name": template_name,
                "language": {
                    "code": language_code
                },
                "components": [
                    {
                        "type": "header",
                        "parameters": [
                            {"type": "text", "text": param_list[0]}   # {{1}} - guest_name (header)
                        ]
                    },
                    {
                        "type": "body",
                        "parameters": [
                            {"type": "text", "text": param_list[1]},  # {{1}} - guest_name (repeated)
                            {"type": "text", "text": param_list[2]},  # {{2}} - groom_name
                            {"type": "text", "text": param_list[3]},  # {{3}} - bride_name
                            {"type": "text", "text": param_list[4]},  # {{4}} - hall_name
                            {"type": "text", "text": param_list[5]},  # {{5}} - event_date
                            {"type": "text", "text": param_list[6]},  # {{6}} - event_time
                            {"type": "text", "text": param_list[7]}   # {{7}} - guest_link
                        ]
                    }
                ]
            }
        }
        
        # DEBUG: Log what we're sending (mask long URLs)
        masked_params = []
        for p in param_list:
            if len(p) > 50 and p.startswith("http"):
                masked_params.append(f"{p[:30]}...{p[-10:]}")
            else:
                masked_params.append(p)
        
        logger.info(
            f"[WhatsApp] Sending template '{template_name}' "
            f"Language: {language_code}, "
            f"To: {to_e164}, "
            f"Params ({len(param_list)}): {masked_params}"
        )
        
        url = f"{self.base_url}/{self.phone_number_id}/messages"
        
        # DEBUG: Print the full payload
        import json
        print("\n" + "=" * 80)
        print("[DEBUG] Full Payload Being Sent to Meta:")
        print("=" * 80)
        print(json.dumps(payload, indent=2, ensure_ascii=False))
        print("=" * 80 + "\n")
        
        try:
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    url,
                    json=payload,
                    headers=self.headers,
                    timeout=30.0
                )
            
            if response.status_code not in (200, 201):
                error_data = response.json()
                error_msg = error_data.get("error", {}).get("message", "Unknown error")
                logger.error(f"[WhatsApp] API Error ({response.status_code}): {error_msg}")
                raise WhatsAppError(
                    f"WhatsApp API error ({response.status_code}): {error_msg}"
                )
            
            result = response.json()
            message_id = result.get("messages", [{}])[0].get("id")
            logger.info(f"[WhatsApp] Message sent successfully! ID: {message_id}")
            
            return {
                "message_id": message_id,
                "status": "sent",
                "to": to_e164,
                "timestamp": datetime.utcnow().isoformat(),
                "type": "template",
                "template": template_name
            }
        
        except httpx.HTTPError as e:
            logger.error(f"[WhatsApp] HTTP Error: {str(e)}")
            raise WhatsAppError(f"HTTP request failed: {str(e)}")
        except Exception as e:
            logger.error(f"[WhatsApp] Unexpected error: {str(e)}")
            raise WhatsAppError(f"Failed to send WhatsApp template: {str(e)}")
    
    async def send_wedding_invitation(
        self,
        to_phone: str,
        guest_name: str,
        partner1_name: str,
        partner2_name: str,
        venue: str,
        event_date: str,  # Should be formatted as DD/MM
        event_time: str,  # Should be formatted as HH:mm
        guest_link: str,
        template_name: Optional[str] = None,
        language_code: Optional[str] = None
    ) -> dict:
        """
        Send wedding invitation template message
        
        IMPORTANT: Always sends exactly 7 parameters in this order:
        {{1}} = contact_name (guest first name or fallback)
        {{2}} = groom_name (partner1)
        {{3}} = bride_name (partner2)
        {{4}} = hall_name (venue)
        {{5}} = event_date (DD/MM format)
        {{6}} = event_time (HH:mm format)
        {{7}} = guest_link (RSVP link)
        
        Args:
            to_phone: Recipient phone number
            guest_name: Guest first name
            partner1_name: First partner name (groom)
            partner2_name: Second partner name (bride)
            venue: Wedding venue/hall name
            event_date: Event date in DD/MM format
            event_time: Event time in HH:mm format
            guest_link: RSVP/guest link
            template_name: Meta template name (uses env var if not provided)
            language_code: Language code (uses env var if not provided)
        
        Returns:
            dict with message_id and status
        """
        # Use environment defaults if not provided
        template_name = template_name or os.getenv("WHATSAPP_TEMPLATE_NAME", "wedding_invitation")
        language_code = language_code or os.getenv("WHATSAPP_LANGUAGE_CODE", "he")
        
        # Build 8 parameters with safe fallbacks
        # The template requires: 1 header param + 7 body params
        # Body {{1}} = guest_name (same as header, repeated)
        param_1_contact_name = (guest_name or "").strip() or "חבר"
        param_2_groom_name = (partner1_name or "").strip() or "החתן"
        param_3_bride_name = (partner2_name or "").strip() or "הכלה"
        param_4_hall_name = (venue or "").strip() or "האולם"
        param_5_event_date = (event_date or "").strip() or "—"
        param_6_event_time = (event_time or "").strip() or "—"
        param_7_guest_link = (guest_link or "").strip() or f"{os.getenv('FRONTEND_URL', 'http://localhost:5174')}/guest?event_id=unknown"
        
        parameters = [
            param_1_contact_name,  # header {{1}}
            param_1_contact_name,  # body {{1}} - guest name repeated
            param_2_groom_name,    # body {{2}}
            param_3_bride_name,    # body {{3}}
            param_4_hall_name,     # body {{4}}
            param_5_event_date,    # body {{5}}
            param_6_event_time,    # body {{6}}
            param_7_guest_link     # body {{7}}
        ]
        
        logger.info(
            f"[WhatsApp Invitation] Building params for {to_phone}: "
            f"guest={param_1_contact_name}, groom={param_2_groom_name}, "
            f"bride={param_3_bride_name}, venue={param_4_hall_name}, "
            f"date={param_5_event_date}, time={param_6_event_time}"
        )
        
        # Use standard template sending with validated parameters
        return await self.send_template_message(
            to_phone=to_phone,
            template_name=template_name,
            language_code=language_code,
            parameters=parameters
        )
    
    async def send_by_template_key(
        self,
        template_key: str,
        to_phone: str,
        params: dict,
    ) -> dict:
        """
        Send a WhatsApp template message using the template registry.

        Looks up *template_key* in whatsapp_templates.py, resolves header and
        body parameter lists (with fallbacks) from *params*, then builds and
        sends the Meta API payload dynamically.

        Args:
            template_key: Registry key (e.g. "wedding_invitation").
            to_phone:     Recipient phone number (normalized to E.164).
            params:       Dict of {param_key: value} for all placeholders.

        Returns:
            dict with message_id and status.
        """
        from whatsapp_templates import get_template, build_params_list

        tpl = get_template(template_key)
        meta_name     = tpl["meta_name"]
        language_code = tpl.get("language_code", "he")

        header_values, body_values = build_params_list(template_key, params)

        to_e164 = self.normalize_phone_to_e164(to_phone)
        if not self.validate_phone(to_e164):
            raise WhatsAppError(f"Invalid phone number: {to_phone}")

        components = []
        if header_values:
            components.append({
                "type": "header",
                "parameters": [{"type": "text", "text": str(v)} for v in header_values],
            })
        if body_values:
            components.append({
                "type": "body",
                "parameters": [{"type": "text", "text": str(v)} for v in body_values],
            })

        # Handle url_button component if defined in template
        url_btn = tpl.get("url_button", {})
        if url_btn and url_btn.get("enabled"):
            param_key = url_btn.get("param_key", "event_id")
            btn_value = str(params.get(param_key, "")).strip()
            if btn_value:
                components.append({
                    "type": "button",
                    "sub_type": "url",
                    "index": str(url_btn.get("button_index", 0)),
                    "parameters": [{"type": "text", "text": btn_value}],
                })

        payload = {
            "messaging_product": "whatsapp",
            "to": to_e164,
            "type": "template",
            "template": {
                "name": meta_name,
                "language": {"code": language_code},
                "components": components,
            },
        }

        import json
        logger.info(
            f"[WhatsApp] send_by_template_key '{template_key}' → meta='{meta_name}' "
            f"lang={language_code} to={to_e164} "
            f"header_params={header_values} body_params={body_values}"
        )
        logger.debug(
            "[WhatsApp] payload: %s",
            json.dumps(payload, ensure_ascii=False),
        )

        url = f"{self.base_url}/{self.phone_number_id}/messages"
        try:
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    url,
                    json=payload,
                    headers=self.headers,
                    timeout=30.0,
                )

            if response.status_code not in (200, 201):
                error_data = response.json()
                error_msg = error_data.get("error", {}).get("message", "Unknown error")
                logger.error(f"[WhatsApp] API error ({response.status_code}): {error_msg}")
                raise WhatsAppError(
                    f"WhatsApp API error ({response.status_code}): {error_msg}"
                )

            result = response.json()
            message_id = result.get("messages", [{}])[0].get("id")
            logger.info(f"[WhatsApp] Message sent successfully via template key. ID: {message_id}")
            return {
                "message_id": message_id,
                "status": "sent",
                "to": to_e164,
                "timestamp": datetime.utcnow().isoformat(),
                "type": "template",
                "template": meta_name,
            }

        except httpx.HTTPError as e:
            raise WhatsAppError(f"HTTP request failed: {str(e)}")
        except WhatsAppError:
            raise
        except Exception as e:
            raise WhatsAppError(f"Failed to send WhatsApp template: {str(e)}")

    def handle_webhook_verification(self, challenge: str) -> str:
        """
        Handle webhook verification challenge from Meta
        
        Args:
            challenge: The challenge string from Meta
        
        Returns:
            The challenge string to echo back
        """
        return challenge
    
    def verify_webhook_signature(self, body: str, signature: str) -> bool:
        """
        Verify webhook signature from Meta
        
        Args:
            body: Raw request body
            signature: x-hub-signature header value
        
        Returns:
            True if signature is valid
        """
        import hmac
        import hashlib
        
        if not self.verify_token:
            return False
        
        # Extract signature from header (format: sha1=...)
        try:
            hash_algo, hash_value = signature.split("=")
        except ValueError:
            return False
        
        # Compute expected signature
        expected_signature = hmac.new(
            self.verify_token.encode(),
            body.encode(),
            hashlib.sha1
        ).hexdigest()
        
        # Constant-time comparison
        return hmac.compare_digest(hash_value, expected_signature)


# Singleton instance
_whatsapp_service: Optional[WhatsAppService] = None


def get_whatsapp_service() -> WhatsAppService:
    """Get or create WhatsApp service singleton"""
    global _whatsapp_service
    if _whatsapp_service is None:
        _whatsapp_service = WhatsAppService()
    return _whatsapp_service
