"""Process control for the Docstream service (spec §6.7)."""
from __future__ import annotations

import os
import signal
import subprocess
import sys
from pathlib import Path

import httpx
from rich.console import Console

from docstream_api_client import Client as SdkClient
from docstream_api_client.api.health import health_service_health
from docstream_api_client.types import Response as SdkResponse

from .client import DEFAULT_BASE_URL, _sdk_base_url

console = Console()

PID_DIR = Path(os.environ.get("XDG_DATA_HOME", "~/.local/share")).expanduser() / "docstream"
PID_FILE = PID_DIR / "docstream.pid"
UVICORN_TARGET = "docstream_api.main:app"


def _ensure_pid_dir() -> None:
    PID_DIR.mkdir(parents=True, exist_ok=True)


def start_service(host: str = "127.0.0.1", port: int = 49202) -> None:
    """Spawn ``uvicorn docstream_api.main:app`` as a background process.

    Writes the PID to ``~/.local/share/docstream/docstream.pid`` (per §6.7).
    """
    if PID_FILE.exists():
        try:
            pid = int(PID_FILE.read_text().strip())
            os.kill(pid, 0)
            console.print(
                f"[yellow]Service already running[/yellow] (PID {pid})."
            )
            return
        except (ProcessLookupError, ValueError, PermissionError):
            # Stale PID file — remove it and continue.
            PID_FILE.unlink(missing_ok=True)

    _ensure_pid_dir()
    cmd = [
        sys.executable,
        "-m",
        "uvicorn",
        UVICORN_TARGET,
        "--host",
        host,
        "--port",
        str(port),
    ]
    log_path = PID_DIR / "docstream.log"
    log_file = open(log_path, "a")  # noqa: SIM115 — kept open by the child
    proc = subprocess.Popen(
        cmd,
        stdout=log_file,
        stderr=log_file,
        stdin=subprocess.DEVNULL,
        start_new_session=True,
    )
    PID_FILE.write_text(str(proc.pid))
    console.print(
        f"[green]Started[/green] Docstream service (PID {proc.pid}) on "
        f"{host}:{port}. Logs: {log_path}"
    )


def stop_service() -> None:
    """Read the PID file, send ``SIGTERM``, and remove the PID file.

    Returns successfully (exit 0) whether or not the process was running.
    """
    if not PID_FILE.exists():
        console.print("[yellow]No PID file found[/yellow] — service is not running.")
        return
    try:
        pid = int(PID_FILE.read_text().strip())
    except ValueError:
        console.print("[red]Invalid PID file[/red] — removing.")
        PID_FILE.unlink(missing_ok=True)
        return
    try:
        os.kill(pid, signal.SIGTERM)
        console.print(f"[green]Stopped[/green] Docstream service (PID {pid}).")
    except ProcessLookupError:
        console.print(
            f"[yellow]Process {pid} not running[/yellow] — removing stale PID file."
        )
    except PermissionError as exc:
        console.print(f"[red]Cannot stop PID {pid}:[/red] {exc}")
        return
    finally:
        PID_FILE.unlink(missing_ok=True)


def status_service(api_url: str | None = None) -> None:
    """Call ``GET /api/v1/health`` via the SDK and report service status.

    If the PID file exists but the health check fails, reports "stale PID".
    """
    base_url = api_url or os.environ.get("DOCSTREAM_API_URL") or DEFAULT_BASE_URL
    pid_exists = PID_FILE.exists()
    pid_str = ""
    if pid_exists:
        try:
            pid_str = PID_FILE.read_text().strip()
        except OSError:
            pid_str = "?"

    sdk_client = SdkClient(
        base_url=_sdk_base_url(base_url),
        timeout=httpx.Timeout(3.0),
    )

    try:
        resp: SdkResponse = health_service_health.sync_detailed(client=sdk_client)
    except Exception as exc:
        if pid_exists:
            console.print(
                f"[red]stale PID[/red] — PID file present ({pid_str}) but "
                f"health check failed: {exc}"
            )
        else:
            console.print(f"[red]unreachable[/red] — {exc}")
        return

    if resp.status_code != 200:
        if pid_exists:
            console.print(
                f"[red]stale PID[/red] — PID file present ({pid_str}) but "
                f"health check returned {resp.status_code}."
            )
        else:
            console.print(f"[red]unhealthy[/red] — health check returned {resp.status_code}.")
        return

    console.print(
        f"[green]ok[/green] — service reachable at {base_url}/health"
        + (f" (PID {pid_str})" if pid_exists else "")
    )
