"""WhatsApp channel implementation using Node.js bridge."""

import asyncio
import json
import time

from loguru import logger

from kabot.bus.events import OutboundMessage
from kabot.bus.queue import MessageBus
from kabot.channels.base import BaseChannel
from kabot.cli.bridge_utils import is_bridge_reachable, is_local_bridge_url, start_bridge_background
from kabot.config.schema import WhatsAppConfig


class WhatsAppChannel(BaseChannel):
    """
    WhatsApp channel that connects to a Node.js bridge.

    The bridge uses @whiskeysockets/baileys to handle the WhatsApp Web protocol.
    Communication between Python and Node.js is via WebSocket.
    """

    name = "whatsapp"

    def __init__(self, config: WhatsAppConfig, bus: MessageBus):
        super().__init__(config, bus)
        self.config: WhatsAppConfig = config
        self._ws = None
        self._connected = False
        self._next_bridge_start_attempt_at = 0.0

    def _ensure_bridge_ready(self) -> None:
        """Start local bridge automatically when it is not reachable."""
        bridge_url = self.config.bridge_url
        if not is_local_bridge_url(bridge_url):
            return
        if is_bridge_reachable(bridge_url):
            return

        now = time.monotonic()
        if now < self._next_bridge_start_attempt_at:
            return
        self._next_bridge_start_attempt_at = now + 30.0

        logger.info(f"WhatsApp bridge not reachable at {bridge_url}; attempting background start")
        started = start_bridge_background(bridge_url=bridge_url, wait_seconds=20.0)
        if started:
            logger.info("WhatsApp bridge is ready")
        else:
            logger.warning("Failed to confirm WhatsApp bridge startup")

    async def start(self) -> None:
        """Start the WhatsApp channel by connecting to the bridge."""
        import websockets

        bridge_url = self.config.bridge_url

        logger.info(f"Connecting to WhatsApp bridge at {bridge_url}...")

        self._running = True

        while self._running:
            self._ensure_bridge_ready()
            try:
                async with websockets.connect(bridge_url) as ws:
                    self._ws = ws
                    self._connected = True
                    logger.info("Connected to WhatsApp bridge")

                    # Listen for messages
                    async for message in ws:
                        try:
                            await self._handle_bridge_message(message)
                        except Exception as e:
                            logger.error(f"Error handling bridge message: {e}")

            except asyncio.CancelledError:
                break
            except Exception as e:
                self._connected = False
                self._ws = None
                logger.warning(f"WhatsApp bridge connection error: {e}")

                if self._running:
                    logger.info("Reconnecting in 5 seconds...")
                    await asyncio.sleep(5)

    async def stop(self) -> None:
        """Stop the WhatsApp channel."""
        self._running = False
        self._connected = False

        if self._ws:
            await self._ws.close()
            self._ws = None

    async def send(self, msg: OutboundMessage) -> None:
        """Send a message through WhatsApp."""
        chat_id_str = str(msg.chat_id)
        async with self._get_chat_send_lock(chat_id_str):
            is_status_update, _phase, _text = self._status_update_payload(msg)
            if is_status_update and self._should_skip_status_update(msg):
                return
            if not is_status_update:
                self._clear_status_state(msg.chat_id)

            if not self._ws or not self._connected:
                logger.warning("WhatsApp bridge not connected")
                return

            try:
                # 1. Send text if no media
                if not msg.media and msg.content:
                    payload = {
                        "type": "send",
                        "to": msg.chat_id,
                        "text": msg.content
                    }
                    await self._ws.send(json.dumps(payload))

                # 2. Send media if present
                if msg.media:
                    for file_path in msg.media:
                        try:
                            import mimetypes
                            from pathlib import Path

                            path = Path(file_path)
                            if not path.exists():
                                logger.error(f"File not found: {file_path}")
                                continue

                            # Guess media type
                            mime_type, _ = mimetypes.guess_type(file_path)
                            media_type = "document"
                            if mime_type:
                                if mime_type.startswith("image/"):
                                    media_type = "image"
                                elif mime_type.startswith("video/"):
                                    media_type = "video"
                                elif mime_type.startswith("audio/"):
                                    media_type = "audio"

                            # Construct absolute path for the bridge to read
                            abs_path = str(path.resolve())

                            payload = {
                                "type": "send_media",
                                "to": msg.chat_id,
                                "mediaType": media_type,
                                "path": abs_path,
                                "caption": msg.content if msg.content and media_type != "audio" else None,
                                "fileName": path.name,
                                "mimetype": mime_type
                            }

                            logger.info(f"Sending media ({media_type}) to WhatsApp: {abs_path}")
                            await self._ws.send(json.dumps(payload))

                            # Clear content to avoid double sending if it was used as caption
                            msg.content = ""

                        except Exception as e:
                            logger.error(f"Failed to send media {file_path}: {e}")

            except Exception as e:
                logger.error(f"Error sending WhatsApp message: {e}")

    async def _handle_bridge_message(self, raw: str) -> None:
        """Handle a message from the bridge."""
        try:
            data = json.loads(raw)
        except json.JSONDecodeError:
            logger.warning(f"Invalid JSON from bridge: {raw[:100]}")
            return

        msg_type = data.get("type")

        if msg_type == "message":
            # Incoming message from WhatsApp
            # Deprecated by whatsapp: old phone number style typically: <phone>@s.whatspp.net
            pn = data.get("pn", "")
            # New LID sytle typically:
            sender = data.get("sender", "")
            content = data.get("content", "")

            # Extract just the phone number or lid as chat_id
            user_id = pn if pn else sender
            sender_id = user_id.split("@")[0] if "@" in user_id else user_id
            logger.info(f"Sender {sender}")

            # Handle voice transcription if it's a voice message
            if content == "[Voice Message]":
                logger.info(f"Voice message received from {sender_id}, but direct download from bridge is not yet supported.")
                content = "[Voice Message: Transcription not available for WhatsApp yet]"

            await self._handle_message(
                sender_id=sender_id,
                chat_id=sender,  # Use full LID for replies
                content=content,
                metadata={
                    "message_id": data.get("id"),
                    "timestamp": data.get("timestamp"),
                    "is_group": data.get("isGroup", False)
                }
            )

        elif msg_type == "status":
            # Connection status update
            status = data.get("status")
            logger.info(f"WhatsApp status: {status}")

            if status == "connected":
                self._connected = True
            elif status == "disconnected":
                self._connected = False

        elif msg_type == "qr":
            # QR code for authentication
            logger.info("Scan QR code in the bridge terminal to connect WhatsApp")

        elif msg_type == "error":
            logger.error(f"WhatsApp bridge error: {data.get('error')}")
