#!/usr/bin/env python3
"""Hermes Web Builder API — agy builds voor Cockpit Web Builder agent."""
from __future__ import annotations

import asyncio
import json
import os
import re
import subprocess
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

app = FastAPI(title="Hermes Web Builder API", version="1.0.0")

PROJECTS_BASE = Path(os.getenv("WEBBUILDER_PROJECTS", "/home/hermes/projects"))
PREVIEW_PORT = int(os.getenv("WEBBUILDER_PREVIEW_PORT", "8080"))
PREVIEW_HOST = os.getenv("WEBBUILDER_PREVIEW_HOST", "10.4.7.27")
NAS_PREFIX = os.getenv("WEBBUILDER_NAS_PREFIX", "//10.4.7.11/share/Websites")
AGY_BIN = os.getenv("AGY_BIN", "/home/hermes/.local/bin/agy")
BUILD_TIMEOUT = os.getenv("WEBBUILDER_BUILD_TIMEOUT", "30m")

JOBS: dict[str, dict[str, Any]] = {}


def slugify(name: str) -> str:
    s = (name or "").strip().lower()
    s = re.sub(r"[^a-z0-9_-]+", "-", s)
    s = re.sub(r"-+", "-", s).strip("-")
    return s[:64] or "website"


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


def _list_files(project_dir: Path) -> list[str]:
    if not project_dir.is_dir():
        return []
    out: list[str] = []
    for p in sorted(project_dir.rglob("*")):
        if p.is_file():
            out.append(str(p.relative_to(project_dir)))
    return out[:50]


def _preview_url() -> str:
    return f"http://{PREVIEW_HOST}:{PREVIEW_PORT}/"


async def _run_shell(cmd: str, *, timeout: float = 120.0) -> tuple[int, str, str]:
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    try:
        stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
    except asyncio.TimeoutError:
        proc.kill()
        return 124, "", "timeout"
    return proc.returncode or 0, stdout.decode(errors="replace"), stderr.decode(errors="replace")


async def _ensure_project(slug: str) -> Path:
    proj = PROJECTS_BASE / slug
    proj.mkdir(parents=True, exist_ok=True)
    await _run_shell(f"chown hermes:hermes '{proj}'")
    return proj


async def _start_preview(slug: str) -> None:
    cmd = (
        f"pkill -f 'http.server.*{PREVIEW_PORT}' 2>/dev/null || true; "
        f"su - hermes -c 'cd {PROJECTS_BASE}/{slug} && "
        f"nohup python3 -m http.server {PREVIEW_PORT} > /tmp/webbuilder-preview.log 2>&1 &'"
    )
    await _run_shell(cmd, timeout=15.0)


async def _run_build(job_id: str, slug: str, prompt: str) -> None:
    job = JOBS[job_id]
    job["status"] = "running"
    job["message"] = f"agy bouwt {slug}…"
    job["updated_at"] = _now()

    proj = await _ensure_project(slug)
    safe_prompt = prompt.replace('"', '\\"')[:8000]
    agy_cmd = (
        f'su - hermes -c \'export PATH="$HOME/.local/bin:$PATH"; '
        f"cd '{proj}' && "
        f"{AGY_BIN} --print --dangerously-skip-permissions --print-timeout {BUILD_TIMEOUT} "
        f'"{safe_prompt}"\''
    )

    job["message"] = "Antigravity CLI gestart…"
    rc, out, err = await _run_shell(agy_cmd, timeout=3600.0)
    log_tail = (out + "\n" + err).strip()[-4000:]
    job["log_tail"] = log_tail
    job["updated_at"] = _now()

    files = _list_files(proj)
    job["files"] = files

    if rc == 0 and files:
        await _start_preview(slug)
        job["status"] = "completed"
        job["message"] = f"Website klaar — {len(files)} bestand(en)"
        job["preview_url"] = _preview_url()
        job["completed_at"] = _now()
    elif rc == 0:
        job["status"] = "completed"
        job["message"] = "agy klaar (geen bestanden gevonden)"
        job["preview_url"] = _preview_url() if files else None
        job["completed_at"] = _now()
    else:
        job["status"] = "failed"
        job["message"] = f"agy exit code {rc}"
        job["error"] = log_tail[-800:] or f"exit {rc}"
        job["completed_at"] = _now()


class BuildRequest(BaseModel):
    project: str = Field(..., min_length=1, max_length=64)
    prompt: str = Field(..., min_length=1, max_length=8000)


@app.get("/health")
async def health() -> dict[str, Any]:
    return {"ok": True, "service": "hermes-webbuilder", "projects_base": str(PROJECTS_BASE)}


@app.get("/api/projects")
async def list_projects() -> dict[str, Any]:
    items: list[dict[str, Any]] = []
    if PROJECTS_BASE.is_dir():
        for p in sorted(PROJECTS_BASE.iterdir()):
            if p.is_dir() and not p.name.startswith("."):
                files = _list_files(p)
                items.append({"slug": p.name, "files": len(files), "has_index": (p / "index.html").is_file()})
    return {"ok": True, "items": items, "count": len(items)}


@app.post("/api/build")
async def start_build(body: BuildRequest) -> dict[str, Any]:
    slug = slugify(body.project)
    if not slug:
        raise HTTPException(400, "Ongeldige projectnaam")

    await _ensure_project(slug)
    job_id = str(uuid.uuid4())
    JOBS[job_id] = {
        "job_id": job_id,
        "project": slug,
        "status": "queued",
        "message": "Build in wachtrij",
        "prompt": body.prompt[:500],
        "nas_path": f"{NAS_PREFIX}/{slug}/",
        "preview_url": _preview_url(),
        "created_at": _now(),
        "updated_at": _now(),
    }
    JOBS[slug] = JOBS[job_id]

    asyncio.create_task(_run_build(job_id, slug, body.prompt))
    return {
        "ok": True,
        "job_id": job_id,
        "project": slug,
        "status": "queued",
        "nas_path": f"{NAS_PREFIX}/{slug}/",
        "preview_url": _preview_url(),
    }


@app.get("/api/build/{project}/status")
async def build_status(project: str) -> dict[str, Any]:
    slug = slugify(project)
    job = JOBS.get(slug) or JOBS.get(project)
    if not job:
        proj = PROJECTS_BASE / slug
        files = _list_files(proj)
        if files:
            return {
                "ok": True,
                "project": slug,
                "status": "completed",
                "message": "Project bestaat (geen actieve job)",
                "files": files,
                "preview_url": _preview_url(),
                "nas_path": f"{NAS_PREFIX}/{slug}/",
            }
        return {"ok": True, "project": slug, "status": "idle", "message": "Geen build actief", "files": []}

    return {
        "ok": True,
        "project": slug,
        "job_id": job.get("job_id"),
        "status": job.get("status"),
        "message": job.get("message"),
        "files": job.get("files") or [],
        "preview_url": job.get("preview_url"),
        "nas_path": f"{NAS_PREFIX}/{slug}/",
        "log_tail": job.get("log_tail", ""),
        "error": job.get("error"),
        "updated_at": job.get("updated_at"),
    }


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "8798")))
