from __future__ import annotations

import asyncio
import logging
import os
import shlex
import shutil
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

LOGGER = logging.getLogger(__name__)
_missing_command_warned = False
_deps_install_warned = False


def _env_flag(name: str, default: bool = False) -> bool:
    raw = os.getenv(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "on"}


def _default_gateway_command() -> list[str]:
    raw = os.getenv("WHATSAPP_GATEWAY_COMMAND", "").strip()
    if raw:
        return shlex.split(raw)
    gateway_entry = Path(__file__).resolve().parent / "gateway-node" / "index.js"
    node = shutil.which("node")
    if node and gateway_entry.exists():
        return [node, str(gateway_entry)]
    return []


def _gateway_dir() -> Path:
    return Path(__file__).resolve().parent / "gateway-node"


@dataclass
class WhatsAppGatewayConfig:
    command: list[str]
    cwd: str
    enabled: bool


class WhatsAppGatewayRuntime:
    def __init__(self) -> None:
        self._process: Optional[asyncio.subprocess.Process] = None
        self._lock = asyncio.Lock()
        self._install_lock = asyncio.Lock()
        self._sync_generation = 0

    def _config(self) -> WhatsAppGatewayConfig:
        workdir = os.getenv("WHATSAPP_GATEWAY_WORKDIR", "").strip()
        return WhatsAppGatewayConfig(
            command=_default_gateway_command(),
            cwd=workdir or str(_gateway_dir()),
            enabled=_env_flag("WHATSAPP_GATEWAY_ENABLED", True),
        )

    def is_running(self) -> bool:
        return bool(self._process and self._process.returncode is None)

    async def sync(self, enabled: bool) -> None:
        cfg = self._config()
        should_run = bool(enabled and cfg.enabled and cfg.command)
        async with self._lock:
            self._sync_generation += 1
            generation = self._sync_generation
            if not should_run:
                await self._stop_locked()
                return
            if self.is_running():
                return

        await self._ensure_dependencies(cfg)

        async with self._lock:
            if generation != self._sync_generation:
                return
            if not should_run:
                await self._stop_locked()
                return
            await self._start_locked(cfg)

    async def _ensure_dependencies(self, cfg: WhatsAppGatewayConfig) -> None:
        global _deps_install_warned
        if not _env_flag("WHATSAPP_GATEWAY_AUTO_INSTALL", True):
            return

        async with self._install_lock:
            gateway_dir = Path(cfg.cwd)
            node_modules = gateway_dir / "node_modules"
            if node_modules.exists():
                return

            npm = shutil.which("npm")
            if not npm:
                if not _deps_install_warned:
                    LOGGER.warning(
                        "npm is not available; WhatsApp gateway dependencies cannot be installed automatically"
                    )
                    _deps_install_warned = True
                return

            package_json = gateway_dir / "package.json"
            if not package_json.exists():
                LOGGER.warning("WhatsApp gateway package.json not found in %s", gateway_dir)
                return

            LOGGER.info("installing WhatsApp gateway dependencies in %s", gateway_dir)
            proc = await asyncio.create_subprocess_exec(
                npm,
                "install",
                "--no-fund",
                "--no-audit",
                cwd=str(gateway_dir),
            )
            try:
                code = await asyncio.wait_for(proc.wait(), timeout=300)
            except asyncio.TimeoutError as ex:
                proc.kill()
                await proc.wait()
                raise RuntimeError("npm install timed out after 300s") from ex
            if code != 0:
                raise RuntimeError(f"npm install failed with exit code {code}")
            _deps_install_warned = False

    async def _start_locked(self, cfg: WhatsAppGatewayConfig) -> None:
        global _missing_command_warned
        if self.is_running():
            return
        if not cfg.command:
            if not _missing_command_warned:
                LOGGER.warning("WhatsApp gateway command is not configured; gateway will not start")
                _missing_command_warned = True
            return
        _missing_command_warned = False

        env = os.environ.copy()
        env.setdefault("PYTHONUNBUFFERED", "1")
        LOGGER.info("starting WhatsApp gateway: %s", " ".join(cfg.command))
        self._process = await asyncio.create_subprocess_exec(
            *cfg.command,
            cwd=cfg.cwd,
            env=env,
        )

    async def _stop_locked(self) -> None:
        proc = self._process
        if proc is None:
            return

        if proc.returncode is None:
            LOGGER.info("stopping WhatsApp gateway")
            proc.terminate()
            try:
                await asyncio.wait_for(proc.wait(), timeout=10)
            except asyncio.TimeoutError:
                LOGGER.warning("WhatsApp gateway did not stop in time; killing it")
                proc.kill()
                await proc.wait()
            except Exception:
                LOGGER.debug("WhatsApp gateway stop failed", exc_info=True)

        self._process = None


_gateway_runtime = WhatsAppGatewayRuntime()


async def sync_whatsapp_gateway(enabled: bool) -> None:
    await _gateway_runtime.sync(enabled)
