"""Gateway sub-app: telegram, signal, otel. Uses core_lifecycle and common."""

import os
import subprocess
from typing import Optional

import typer

from armorer.config import ConfigManager
from armorer.messaging import InboxOutboxRelay, SignalCoreBridge, TelegramCoreBridge, create_messaging_gateway
from armorer.cli import common as cli_common
from armorer.cli import core_lifecycle as cli_lifecycle


def get_gateway_app() -> typer.Typer:
    gateway_app = typer.Typer(help="Run messaging gateways")
    signal_app = typer.Typer(help="Signal gateway and provisioning commands")
    gateway_app.add_typer(signal_app, name="signal")
    otel_app = typer.Typer(help="OTEL collector bridge commands")
    gateway_app.add_typer(otel_app, name="otel")

    @gateway_app.command("telegram")
    def gateway_telegram(
        poll_timeout: int = typer.Option(30, help="Telegram long-poll timeout in seconds."),
        response_timeout: int = typer.Option(300, help="Max wait time for Armorer Core response."),
    ):
        """Run Telegram long-polling gateway and route messages through Armorer Core."""
        from dotenv import load_dotenv
        load_dotenv(override=True)
        os.environ["ARMORER_DISABLE_GATEWAY_AUTOSTART"] = "1"
        cli_lifecycle.ensure_armorer_core()
        config_dir = cli_common.armorer_config_dir()
        config_dir.mkdir(parents=True, exist_ok=True)
        pid_file = config_dir / "gateway-telegram.pid"
        cm = ConfigManager()
        signing_key = cm.get_key("ARMORER_TASK_SIGNING_KEY")
        bot_token = (
            cm.get_armorer_telegram_bot_token()
            or os.getenv("ARMORER_TELEGRAM_BOT_TOKEN")
            or os.getenv("TELEGRAM_BOT_TOKEN")
        )
        if not bot_token:
            cli_common.console.print(
                "[red]Missing Telegram bot token.[/red] Set `TELEGRAM_BOT_TOKEN` via `armorer setup` "
                "or export it in your environment."
            )
            raise typer.Exit(1)
        claimed, active_pid = cli_lifecycle.claim_telegram_gateway_singleton(config_dir)
        if not claimed:
            cli_common.console.print(f"[yellow]Telegram gateway is already active (PID {active_pid}).[/yellow]")
            raise typer.Exit(0)
        gateway = create_messaging_gateway("telegram", bot_token=bot_token)
        relay = InboxOutboxRelay(config_dir=config_dir, signing_key=signing_key)
        bridge = TelegramCoreBridge(gateway=gateway, relay=relay, response_timeout=response_timeout)
        cli_common.console.print("[dim]Telegram gateway active. Press Ctrl+C to stop.[/dim]")
        try:
            bridge.run_forever(poll_timeout=poll_timeout)
        except KeyboardInterrupt:
            cli_common.console.print("[dim]Telegram gateway stopped.[/dim]")
        finally:
            cli_lifecycle.release_owned_pid_file(pid_file, os.getpid())

    @signal_app.command("setup")
    def gateway_signal_setup(
        port: int = typer.Option(8085, help="Host port for signal-cli-rest-api."),
        image: str = typer.Option("bbernhard/signal-cli-rest-api:latest", help="Signal REST API image."),
    ):
        """Start local signal-cli-rest-api service in Docker with persistent state."""
        state_dir = cli_common.armorer_config_dir() / "signal"
        state_dir.mkdir(parents=True, exist_ok=True)
        container_name = "armorer-signal-api"
        subprocess.run(["docker", "rm", "-f", container_name], capture_output=True)
        cmd = [
            "docker", "run", "-d",
            "--name", container_name,
            "-p", f"{port}:8080",
            "-v", f"{state_dir}:/home/.local/share/signal-cli",
            "-e", "MODE=native",
            image,
        ]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode != 0:
            cli_common.console.print(f"[red]Failed to start Signal API: {result.stderr.strip()}[/red]")
            raise typer.Exit(1)
        cm = ConfigManager()
        cm.set_key("SIGNAL_ENDPOINT", f"http://127.0.0.1:{port}")
        cli_common.console.print("[green]✓ Signal API service started.[/green]")
        cli_common.console.print(f"[dim]Container: {container_name} | Endpoint: http://127.0.0.1:{port}[/dim]")

    @signal_app.command("register")
    def gateway_signal_register(
        number: str = typer.Option(..., help="Signal number in E.164 format, e.g. +15551234567."),
        use_voice: bool = typer.Option(False, "--voice", help="Use voice call instead of SMS."),
        captcha: Optional[str] = typer.Option(None, help="Optional captcha token if required by Signal."),
    ):
        """Request Signal verification code for a phone number."""
        cm = ConfigManager()
        endpoint = cm.get_key("SIGNAL_ENDPOINT") or "http://127.0.0.1:8085"
        gw = create_messaging_gateway("signal", endpoint=endpoint, account=number)
        try:
            gw.register(number=number, use_voice=use_voice, captcha=captcha)  # type: ignore[attr-defined]
            cm.set_key("SIGNAL_ACCOUNT", number)
            cm.set_key("ARMORER_GATEWAY_SIGNAL_ENABLED", "true")
            cli_common.console.print("[green]✓ Verification request sent.[/green]")
            cli_common.console.print("[dim]Now run: armorer gateway signal verify --code <CODE>[/dim]")
        except Exception as e:
            cli_common.console.print(f"[red]Signal register failed: {e}[/red]")
            raise typer.Exit(1)

    @signal_app.command("verify")
    def gateway_signal_verify(
        code: str = typer.Option(..., help="Verification code received via SMS/voice."),
        number: Optional[str] = typer.Option(None, help="Signal number; defaults to configured SIGNAL_ACCOUNT."),
        pin: Optional[str] = typer.Option(None, help="Signal PIN if registration lock is enabled."),
    ):
        """Verify Signal registration with code and optional PIN."""
        cm = ConfigManager()
        account = number or cm.get_key("SIGNAL_ACCOUNT")
        if not account:
            cli_common.console.print("[red]Missing Signal account number. Provide --number or run register first.[/red]")
            raise typer.Exit(1)
        endpoint = cm.get_key("SIGNAL_ENDPOINT") or "http://127.0.0.1:8085"
        gw = create_messaging_gateway("signal", endpoint=endpoint, account=account)
        try:
            gw.verify(number=account, code=code, pin=pin)  # type: ignore[attr-defined]
            cm.set_key("SIGNAL_ACCOUNT", account)
            cm.set_key("ARMORER_GATEWAY_SIGNAL_ENABLED", "true")
            cli_common.console.print("[green]✓ Signal account verified.[/green]")
        except Exception as e:
            cli_common.console.print(f"[red]Signal verify failed: {e}[/red]")
            raise typer.Exit(1)

    @signal_app.command("run")
    def gateway_signal_run(
        response_timeout: int = typer.Option(300, help="Max wait time for Armorer Core response."),
        receive_timeout: int = typer.Option(1, help="Signal receive poll timeout in seconds."),
        max_messages: int = typer.Option(10, help="Max messages per receive poll."),
    ):
        """Run Signal gateway poller and route messages through Armorer Core."""
        from dotenv import load_dotenv
        load_dotenv(override=True)
        cli_lifecycle.ensure_armorer_core()
        config_dir = cli_common.armorer_config_dir()
        cm = ConfigManager()
        signing_key = cm.get_key("ARMORER_TASK_SIGNING_KEY")
        endpoint = cm.get_key("SIGNAL_ENDPOINT") or "http://127.0.0.1:8085"
        account = cm.get_key("SIGNAL_ACCOUNT")
        if not account:
            cli_common.console.print("[red]Missing SIGNAL_ACCOUNT. Run register/verify first.[/red]")
            raise typer.Exit(1)
        gateway = create_messaging_gateway("signal", endpoint=endpoint, account=account)
        relay = InboxOutboxRelay(config_dir=config_dir, signing_key=signing_key)
        bridge = SignalCoreBridge(gateway=gateway, relay=relay, response_timeout=response_timeout)
        cli_common.console.print("[dim]Signal gateway active. Press Ctrl+C to stop.[/dim]")
        try:
            bridge.run_forever(receive_timeout=receive_timeout, max_messages=max_messages)
        except KeyboardInterrupt:
            cli_common.console.print("[dim]Signal gateway stopped.[/dim]")

    @otel_app.command("setup")
    def gateway_otel_setup(
        port: int = typer.Option(4318, help="Host port for OTLP/HTTP ingestion."),
        image: str = typer.Option("otel/opentelemetry-collector-contrib:0.120.0", help="Collector image."),
    ):
        """Start local OTEL collector bridge and write events to ~/.armorer/otel_events."""
        if os.path.exists("/.dockerenv"):
            cli_common.console.print("[yellow]OTEL bridge setup should be run from host, not inside container.[/yellow]")
            raise typer.Exit(1)
        cm = ConfigManager()
        cm.set_key("ARMORER_OTEL_COLLECTOR_ENABLED", "true")
        cm.set_key("ARMORER_OTEL_COLLECTOR_PORT", str(port))
        cm.set_key("ARMORER_OTEL_COLLECTOR_IMAGE", image)
        config_dir = cli_common.armorer_config_dir()
        cfg_path = cli_lifecycle.write_otel_collector_config(config_dir)
        events_dir = config_dir / "otel_events"
        name = "armorer-otel-collector"
        subprocess.run(["docker", "rm", "-f", name], capture_output=True)
        run_cmd = [
            "docker", "run", "-d",
            "--name", name,
            "--restart", "unless-stopped",
            "--user", "0:0",
            "-p", f"127.0.0.1:{port}:4318",
            "-v", f"{cfg_path}:/etc/otelcol-contrib/config.yaml:ro",
            "-v", f"{events_dir}:/var/lib/armorer-otel",
            image,
            "--config=/etc/otelcol-contrib/config.yaml",
        ]
        result = subprocess.run(run_cmd, capture_output=True, text=True)
        if result.returncode != 0:
            cli_common.console.print(f"[red]Failed to start OTEL bridge: {(result.stderr or result.stdout).strip()}[/red]")
            raise typer.Exit(1)
        endpoint = f"http://127.0.0.1:{port}"
        cm.set_key("ARMORER_OTEL_ENDPOINT", endpoint)
        cli_common.console.print("[green]✓ OTEL collector bridge started.[/green]")
        cli_common.console.print(f"[dim]Endpoint: {endpoint} | Events: {events_dir}[/dim]")

    @otel_app.command("status")
    def gateway_otel_status():
        """Show OTEL collector bridge status."""
        name = "armorer-otel-collector"
        result = subprocess.run(
            ["docker", "ps", "-a", "--filter", f"name={name}", "--format", "{{.Names}}\t{{.Status}}"],
            capture_output=True, text=True,
        )
        lines = [ln for ln in (result.stdout or "").splitlines() if ln.strip()]
        if not lines:
            cli_common.console.print("[yellow]OTEL bridge not installed/running.[/yellow]")
            return
        for ln in lines:
            cli_common.console.print(ln)
        events_dir = cli_common.armorer_config_dir() / "otel_events"
        if events_dir.exists():
            files = sorted(events_dir.glob("*.jsonl"))
            cli_common.console.print(f"[dim]Event files: {len(files)} in {events_dir}[/dim]")

    @otel_app.command("logs")
    def gateway_otel_logs(
        tail: int = typer.Option(100, help="Number of log lines."),
    ):
        """Tail OTEL collector bridge logs."""
        subprocess.run(["docker", "logs", "--tail", str(max(1, tail)), "-f", "armorer-otel-collector"])

    return gateway_app
