"""Global per-machine configuration and STT server discovery."""

from __future__ import annotations

import json
import os
import socket
import urllib.request
import urllib.error
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path


CONFIG_DIR = Path.home() / ".config" / "agentboard"
CONFIG_FILE = CONFIG_DIR / "config.json"

# Full port range for discovery. TCP scan is fast enough (~2-3s for 65k
# ports at 500 concurrent connections) that we just scan everything.

# Endpoint paths to probe on each open port.
STT_PROBE_PATHS = [
    "/v1/audio/transcriptions",
    "/inference",
]


def load_config() -> dict:
    """Load global config from ~/.config/agentboard/config.json."""
    if CONFIG_FILE.is_file():
        try:
            with open(CONFIG_FILE) as f:
                return json.load(f)
        except (json.JSONDecodeError, OSError):
            pass
    return {}


def save_config(config: dict):
    """Save global config to ~/.config/agentboard/config.json."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    tmp = CONFIG_FILE.with_suffix(".tmp")
    with open(tmp, "w") as f:
        json.dump(config, f, indent=2)
        f.write("\n")
    os.replace(tmp, CONFIG_FILE)


def get_stt_url(cli_override: str | None = None) -> str | None:
    """Resolve STT URL: CLI flag > global config > None."""
    if cli_override:
        return cli_override
    config = load_config()
    return config.get("stt_url")


def _check_port(port: int) -> int | None:
    """Return port if TCP-connectable on localhost, else None."""
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(0.3)
        s.connect(("127.0.0.1", port))
        s.close()
        return port
    except OSError:
        return None


def _probe_stt(port: int) -> str | None:
    """Try known STT endpoints on a port. Returns the full URL if found."""
    for path in STT_PROBE_PATHS:
        url = f"http://127.0.0.1:{port}{path}"
        try:
            # Send an empty POST — a real STT server returns 400/422 (bad
            # request / missing audio file). Generic servers return 404
            # (route doesn't exist) or 403 (forbidden).
            req = urllib.request.Request(url, data=b"", method="POST")
            req.add_header("Content-Type", "multipart/form-data; boundary=probe")
            with urllib.request.urlopen(req, timeout=2) as resp:
                ct = resp.headers.get("Content-Type", "")
                if "html" in ct:
                    continue
                # 200 on an empty POST is suspicious, but accept if JSON
                if "json" in ct:
                    return url
        except urllib.error.HTTPError as e:
            # 400/422 = endpoint exists and rejected our empty audio -> STT server
            # 404 = route not found -> not an STT server
            # 403 = forbidden -> not an STT server (likely AirPlay, auth-gated, etc.)
            # 405 = method not allowed -> endpoint exists but wrong method
            if e.code in (400, 422, 405):
                return url
        except (urllib.error.URLError, OSError, Exception):
            continue
    return None


def discover_stt() -> list[str]:
    """Scan all localhost ports for running STT servers. Returns list of found URLs."""
    ports = list(range(1, 65536))

    # Phase 1: fast TCP scan (~2-3s with 500 workers)
    with ThreadPoolExecutor(max_workers=500) as ex:
        open_ports = sorted(p for p in ex.map(_check_port, ports) if p is not None)

    # Phase 2: probe open ports in parallel for STT endpoints
    with ThreadPoolExecutor(max_workers=20) as ex:
        results = list(ex.map(_probe_stt, open_ports))

    return [url for url in results if url]
