#!/usr/bin/env python3
from __future__ import annotations

import json
from typing import Any, Dict, List, Optional, Tuple

import requests

from cheapclaw_channel_base import ChannelAdapter
from tool_runtime_helpers import now_iso


class WhatsAppCloudAdapter(ChannelAdapter):
    name = "whatsapp"

    def __init__(self, config: Dict[str, Any], service: Any, *, logger=None):
        super().__init__(config, service, logger=logger)
        self.access_token = str(config.get("access_token") or "").strip()
        self.phone_number_id = str(config.get("phone_number_id") or "").strip()
        self.verify_token = str(config.get("verify_token") or "").strip()
        self.api_version = str(config.get("api_version") or "v21.0").strip()
        self.api_root = f"https://graph.facebook.com/{self.api_version}"

    def handle_webhook_get(self, path: str, query: Dict[str, List[str]], headers: Dict[str, str]) -> Tuple[int, Dict[str, str], bytes]:
        mode = (query.get("hub.mode") or [""])[0]
        token = (query.get("hub.verify_token") or [""])[0]
        challenge = (query.get("hub.challenge") or [""])[0]
        if mode == "subscribe" and token and token == self.verify_token:
            return 200, {"Content-Type": "text/plain; charset=utf-8"}, challenge.encode("utf-8")
        return 403, {"Content-Type": "text/plain; charset=utf-8"}, b"forbidden"

    def handle_webhook_post(self, path: str, body: bytes, headers: Dict[str, str]) -> Tuple[int, Dict[str, str], bytes]:
        try:
            payload = json.loads(body.decode("utf-8"))
        except Exception:
            return 400, {"Content-Type": "application/json"}, b'{"error":"invalid json"}'
        for entry in payload.get("entry", []):
            for change in entry.get("changes", []):
                value = change.get("value", {})
                contacts = value.get("contacts") or []
                contact_name = str((contacts[0].get("profile") or {}).get("name") or "") if contacts else ""
                for message in value.get("messages", []) or []:
                    text = str(((message.get("text") or {}).get("body")) or "").strip()
                    from_id = str(message.get("from") or "").strip()
                    self.service.ingest_event(
                        {
                            "event_id": f"whatsapp_{message.get('id')}",
                            "channel": "whatsapp",
                            "conversation_id": from_id,
                            "conversation_type": "person",
                            "display_name": contact_name or from_id,
                            "sender_id": from_id,
                            "sender_name": contact_name,
                            "message_id": str(message.get("id") or ""),
                            "message_text": text,
                            "attachments": [],
                            "timestamp": now_iso(),
                            "is_mention_to_bot": True,
                            "require_mention": False,
                        }
                    )
        return 200, {"Content-Type": "application/json"}, b'{"ok":true}'

    def send_message(self, conversation_id: str, message: str, attachments: Optional[List[Dict[str, Any]]] = None) -> Tuple[bool, str]:
        if not self.access_token or not self.phone_number_id:
            return False, "whatsapp access_token or phone_number_id is missing"
        last_remote_id = ""
        normalized = self._normalize_attachments(attachments)
        if message:
            response = requests.post(
                f"{self.api_root}/{self.phone_number_id}/messages",
                headers={"Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json"},
                json={"messaging_product": "whatsapp", "to": conversation_id, "type": "text", "text": {"body": message}},
                timeout=30,
            )
            if response.status_code >= 300:
                return False, response.text
            data = response.json()
            if data.get("messages"):
                last_remote_id = str((data.get("messages") or [{}])[0].get("id") or "")
        for item in normalized:
            mime_type = item["mime_type"]
            media_type = "image" if mime_type.startswith("image/") else "document"
            with open(item["path"], "rb") as fh:
                files = {"file": (item["filename"], fh, mime_type)}
                upload_resp = requests.post(
                    f"{self.api_root}/{self.phone_number_id}/media",
                    headers={"Authorization": f"Bearer {self.access_token}"},
                    data={"messaging_product": "whatsapp", "type": mime_type},
                    files=files,
                    timeout=60,
                )
            if upload_resp.status_code >= 300:
                return False, upload_resp.text
            upload_data = upload_resp.json()
            media_id = str(upload_data.get("id") or "")
            if not media_id:
                return False, json.dumps(upload_data, ensure_ascii=False)
            payload = {"messaging_product": "whatsapp", "to": conversation_id, "type": media_type, media_type: {"id": media_id}}
            if media_type == "document":
                payload[media_type]["filename"] = item["filename"]
            caption = item["caption"] or (message if not last_remote_id else "")
            if caption:
                payload[media_type]["caption"] = caption
            send_resp = requests.post(
                f"{self.api_root}/{self.phone_number_id}/messages",
                headers={"Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json"},
                json=payload,
                timeout=30,
            )
            if send_resp.status_code >= 300:
                return False, send_resp.text
            data = send_resp.json()
            if data.get("messages"):
                last_remote_id = str((data.get("messages") or [{}])[0].get("id") or "")
        if not message and not normalized:
            return False, "whatsapp message or attachments are required"
        return True, last_remote_id
