"""VS Code Web control API.

This router exposes a small REST surface for starting/stopping the VS Code Web
service (code-server) used in the proxym stack.

Endpoints:
  GET  /api/v1/vscode/status
  POST /api/v1/vscode/start
  POST /api/v1/vscode/stop
  POST /api/v1/vscode/restart
  GET  /api/v1/vscode/logs
  POST /api/v1/vscode/build

Implementation uses docker compose (same approach as proxym.cli.vscode).
"""

from __future__ import annotations

import asyncio
import subprocess
import time
from pathlib import Path

import httpx
import structlog
from fastapi import APIRouter, HTTPException
from fastapi.responses import JSONResponse

from proxym.config import get_settings

log = structlog.get_logger()

router = APIRouter(prefix="/api/v1/vscode", tags=["vscode"])


def _compose_dir() -> Path:
    """Find directory containing docker-compose.yml."""
    for candidate in [
        Path.cwd(),
        Path.cwd().parent,
        Path(__file__).resolve().parent.parent.parent,  # src/proxym/api/ -> repo root
        Path("/app"),
    ]:
        if (candidate / "docker-compose.yml").exists():
            return candidate
    raise FileNotFoundError("docker-compose.yml not found")


def _run_compose(*args: str, timeout: int = 60) -> tuple[int, str, str]:
    """Run docker compose and return (returncode, stdout, stderr)."""
    try:
        d = _compose_dir()
        result = subprocess.run(
            ["docker", "compose", *args],
            cwd=d,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        return result.returncode, result.stdout, result.stderr
    except FileNotFoundError as e:
        return 1, "", str(e)
    except subprocess.TimeoutExpired:
        return 1, "", "timeout"
    except Exception as e:  # pragma: no cover
        return 1, "", str(e)


def _is_vscode_running() -> bool:
    rc, out, _ = _run_compose("ps", "--services", "--filter", "status=running")
    return rc == 0 and "vscode" in out


def _get_vscode_port() -> int:
    try:
        return int(get_settings().vscode_port)
    except Exception:
        return 8080


def _wait_for_vscode(port: int, max_wait: int = 30) -> bool:
    deadline = time.monotonic() + max_wait
    while time.monotonic() < deadline:
        try:
            r = httpx.get(f"http://localhost:{port}", timeout=1.0)
            if r.status_code < 500:
                return True
        except Exception:
            pass
        time.sleep(1)
    return False


@router.get("/status")
async def vscode_status():
    running = _is_vscode_running()
    port = _get_vscode_port()
    return JSONResponse(
        {
            "running": running,
            "port": port,
            "url": f"http://localhost:{port}" if running else None,
            "container": "proxym-vscode",
        }
    )


@router.post("/start")
async def vscode_start():
    if _is_vscode_running():
        port = _get_vscode_port()
        return JSONResponse(
            {
                "running": True,
                "url": f"http://localhost:{port}",
                "message": "VS Code already running.",
            }
        )

    log.info("vscode_starting")
    rc, _out, err = _run_compose("--profile", "vscode", "up", "-d", "vscode", timeout=120)
    if rc != 0:
        log.error("vscode_start_failed", stderr=err)
        raise HTTPException(500, f"docker compose up vscode failed: {err}")

    port = _get_vscode_port()
    ready = _wait_for_vscode(port, max_wait=45)
    return JSONResponse(
        {
            "running": ready,
            "url": f"http://localhost:{port}" if ready else None,
            "message": "VS Code started." if ready else "VS Code starting (may take a moment).",
        }
    )


@router.post("/stop")
async def vscode_stop():
    if not _is_vscode_running():
        return JSONResponse({"stopped": True, "message": "VS Code already stopped."})

    rc, _out, err = _run_compose("--profile", "vscode", "stop", "vscode", timeout=30)
    if rc != 0:
        raise HTTPException(500, f"docker compose stop vscode failed: {err}")

    return JSONResponse({"stopped": True, "message": "VS Code stopped."})


@router.post("/restart")
async def vscode_restart():
    rc, _out, err = _run_compose("--profile", "vscode", "restart", "vscode", timeout=60)
    if rc != 0:
        raise HTTPException(500, f"docker compose restart vscode failed: {err}")

    port = _get_vscode_port()
    ready = _wait_for_vscode(port, max_wait=30)
    return JSONResponse(
        {
            "running": ready,
            "url": f"http://localhost:{port}" if ready else None,
            "message": "VS Code restarted.",
        }
    )


@router.get("/logs")
async def vscode_logs(lines: int = 50):
    rc, out, err = _run_compose(
        "--profile",
        "vscode",
        "logs",
        "--tail",
        str(lines),
        "--no-color",
        "vscode",
        timeout=15,
    )
    if rc != 0:
        return JSONResponse({"lines": [f"Error: {err}"], "container": "proxym-vscode"})
    return JSONResponse({"lines": out.splitlines(), "container": "proxym-vscode"})


@router.post("/build")
async def vscode_build():
    async def _build():
        rc, _out, err = _run_compose("--profile", "vscode", "build", "--no-cache", "vscode", timeout=600)
        if rc == 0:
            log.info("vscode_build_completed")
        else:
            log.error("vscode_build_failed", stderr=err)

    asyncio.create_task(_build())
    return JSONResponse(
        {
            "building": True,
            "message": "VS Code build started in background. Check logs: proxym vscode logs",
        }
    )
