"""
WhatsApp Business API client — XZonas Multiagent Supervisor.

Flujo de mensajes entrantes (webhook POST /whatsapp/webhook):
  1. Meta envía el evento firmado al endpoint.
  2. Se valida la firma HMAC-SHA256 con WHATSAPP_APP_SECRET.
  3. El mensaje se enruta al orchestrator (igual que Telegram).
  4. Para /aprobar o /rechazar, se llama a store.approve/reject_proposal().

Flujo de envío (marketing / notificaciones):
  - send_text()      → mensaje libre (solo válido en ventana de 24h desde el último msg del usuario)
  - send_template()  → plantilla aprobada por Meta (válida en cualquier momento)
  - send_campaign()  → envío a lista de contactos con plantilla

Para activar el servicio, configura en .env:
  WHATSAPP_ENABLED=true
  WHATSAPP_PHONE_NUMBER_ID=<ID Meta, no el número real>
  WHATSAPP_ACCESS_TOKEN=<token permanente del usuario del sistema>
  WHATSAPP_WEBHOOK_VERIFY_TOKEN=<token secreto que tú defines>
  WHATSAPP_APP_SECRET=<App Secret de la App en Meta Developers>
  WHATSAPP_ALLOWED_PHONES=+34600000000,+34611111111  (allowlist)
"""
from __future__ import annotations

import hashlib
import hmac
import logging
from typing import Any

import httpx

logger = logging.getLogger("supervisor.whatsapp")

_API_BASE = "https://graph.facebook.com"


class WhatsAppService:
    """Servicio WhatsApp Business API.

    Instanciado en app/main.py junto al TelegramBotService.
    Se activa solo si WHATSAPP_ENABLED=true y WHATSAPP_PHONE_NUMBER_ID está configurado.
    """

    def __init__(
        self,
        *,
        enabled: bool,
        phone_number_id: str,
        access_token: str,
        webhook_verify_token: str,
        app_secret: str,
        allowed_phones: tuple[str, ...],
        api_version: str = "v20.0",
    ) -> None:
        self.enabled = enabled and bool(phone_number_id) and bool(access_token)
        self.phone_number_id = phone_number_id
        self.access_token = access_token
        self.webhook_verify_token = webhook_verify_token
        self.app_secret = app_secret
        self.allowed_phones = allowed_phones
        self.api_version = api_version
        self._base_url = f"{_API_BASE}/{api_version}/{phone_number_id}/messages"
        # Callback que main.py registra para manejar mensajes entrantes
        self._message_handler: Any = None

    # ── Ciclo de vida ── #

    async def start(self) -> None:
        if not self.enabled:
            logger.info("whatsapp.disabled")
            return
        logger.info(
            "whatsapp.started",
            extra={"phone_number_id": self.phone_number_id, "allowed_phones": len(self.allowed_phones)},
        )

    async def stop(self) -> None:
        logger.info("whatsapp.stopped")

    def set_message_handler(self, handler: Any) -> None:
        """Registra el callback que procesará mensajes entrantes."""
        self._message_handler = handler

    # ── Verificación del webhook (GET) ── #

    def verify_webhook(self, mode: str, token: str, challenge: str) -> str | None:
        """Verifica la solicitud de Meta para activar el webhook.

        Returns challenge si es válido, None si el token no coincide.
        """
        if mode == "subscribe" and token == self.webhook_verify_token:
            logger.info("whatsapp.webhook_verified")
            return challenge
        logger.warning("whatsapp.webhook_verify_failed", extra={"mode": mode})
        return None

    # ── Validación de firma HMAC (POST) ── #

    def validate_signature(self, body: bytes, signature_header: str) -> bool:
        """Valida la firma HMAC-SHA256 de Meta."""
        if not self.app_secret:
            return True  # Sin app_secret configurado, se acepta en desarrollo
        expected = "sha256=" + hmac.new(
            self.app_secret.encode(), body, hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(expected, signature_header or "")

    # ── Procesamiento de eventos entrantes (POST) ── #

    async def handle_webhook(self, payload: dict[str, Any]) -> None:
        """Procesa el payload JSON recibido en POST /whatsapp/webhook."""
        for entry in payload.get("entry", []):
            for change in entry.get("changes", []):
                value = change.get("value", {})
                await self._process_value(value)

    async def _process_value(self, value: dict[str, Any]) -> None:
        messages = value.get("messages", [])
        for msg in messages:
            await self._route_message(msg, value)

    async def _route_message(self, msg: dict[str, Any], value: dict[str, Any]) -> None:
        from_phone = msg.get("from", "")
        msg_type = msg.get("type", "")
        msg_id = msg.get("id", "")

        # Allowlist check
        if self.allowed_phones and from_phone not in self.allowed_phones:
            logger.info("whatsapp.message_ignored_not_allowed", extra={"from": from_phone})
            return

        if msg_type == "text":
            text = msg.get("text", {}).get("body", "").strip()
        elif msg_type in {"image", "audio", "video", "document"}:
            media = msg.get(msg_type, {})
            text = f"[{msg_type}] {media.get('caption', '(sin texto)')}"
        elif msg_type == "interactive":
            interactive = msg.get("interactive", {})
            reply = interactive.get("button_reply") or interactive.get("list_reply") or {}
            text = reply.get("id", "") or reply.get("title", "")
        else:
            text = f"[mensaje tipo {msg_type}]"

        logger.info("whatsapp.message_received", extra={"from": from_phone, "type": msg_type, "text": text[:80]})

        if self._message_handler:
            await self._message_handler(
                phone=from_phone,
                text=text,
                msg_id=msg_id,
                raw=msg,
            )

    # ── Envío de mensajes ── #

    async def send_text(self, to: str, text: str) -> dict[str, Any]:
        """Envía mensaje de texto libre (válido solo en ventana de 24h)."""
        return await self._request({
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "text",
            "text": {"preview_url": False, "body": text},
        })

    async def send_template(
        self,
        to: str,
        template_name: str,
        lang: str = "es_ES",
        components: list[dict[str, Any]] | None = None,
    ) -> dict[str, Any]:
        """Envía una plantilla aprobada por Meta.

        Args:
            to: Número de teléfono con prefijo +34...
            template_name: Nombre exacto de la plantilla aprobada en Meta Business Manager.
            lang: Código de idioma (es_ES, en_US, ...).
            components: Lista de parámetros de la plantilla (header, body, buttons).
        """
        payload: dict[str, Any] = {
            "messaging_product": "whatsapp",
            "to": to,
            "type": "template",
            "template": {
                "name": template_name,
                "language": {"code": lang},
            },
        }
        if components:
            payload["template"]["components"] = components
        return await self._request(payload)

    async def send_interactive_buttons(
        self,
        to: str,
        body_text: str,
        buttons: list[dict[str, str]],
        header_text: str | None = None,
        footer_text: str | None = None,
    ) -> dict[str, Any]:
        """Envía mensaje interactivo con botones de respuesta rápida (máx. 3).

        Args:
            buttons: Lista de dicts con {"id": "btn_id", "title": "Texto botón"}.
        """
        interactive: dict[str, Any] = {
            "type": "button",
            "body": {"text": body_text},
            "action": {
                "buttons": [
                    {"type": "reply", "reply": {"id": b["id"], "title": b["title"][:20]}}
                    for b in buttons[:3]
                ]
            },
        }
        if header_text:
            interactive["header"] = {"type": "text", "text": header_text}
        if footer_text:
            interactive["footer"] = {"text": footer_text}
        return await self._request({
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": to,
            "type": "interactive",
            "interactive": interactive,
        })

    async def send_campaign(
        self,
        contacts: list[dict[str, str]],
        template_name: str,
        lang: str = "es_ES",
        components_builder: Any = None,
    ) -> list[dict[str, Any]]:
        """Envía una plantilla a múltiples contactos (campaña de marketing).

        Args:
            contacts: Lista de dicts con al menos {"phone": "+34600...", "name": "...", ...}.
            template_name: Nombre de la plantilla Meta aprobada.
            components_builder: Callable(contact) → lista de components opcionales.
        """
        results = []
        for contact in contacts:
            phone = contact.get("phone", "").strip()
            if not phone:
                continue
            components = components_builder(contact) if components_builder else None
            try:
                result = await self.send_template(phone, template_name, lang, components)
                results.append({"phone": phone, "status": "sent", "result": result})
                logger.info("whatsapp.campaign_sent", extra={"phone": phone, "template": template_name})
            except httpx.HTTPStatusError as exc:
                results.append({"phone": phone, "status": "error", "detail": str(exc)})
                logger.warning("whatsapp.campaign_failed", extra={"phone": phone, "error": str(exc)})
        return results

    # ── Núcleo HTTP ── #

    async def _request(self, payload: dict[str, Any]) -> dict[str, Any]:
        headers = {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json",
        }
        async with httpx.AsyncClient(timeout=15) as client:
            response = await client.post(self._base_url, json=payload, headers=headers)
            response.raise_for_status()
            return response.json()

    # ── Estado del servicio ── #

    def status_summary(self) -> dict[str, Any]:
        return {
            "whatsapp_enabled": self.enabled,
            "phone_number_id": self.phone_number_id if self.enabled else "",
            "api_version": self.api_version,
            "allowed_phones": len(self.allowed_phones),
        }
