"""Web Builder agent — websites bouwen via Hermes/agy API op VM107."""
from __future__ import annotations

import os
import re
from typing import Any

import httpx

from app.config import settings
from app.services.agent_events_log import log_agent_event

HERMES_BUILD_URL = os.getenv("HERMES_BUILD_URL", "http://10.4.7.27:8798").rstrip("/")
HERMES_PREVIEW_BASE = os.getenv("HERMES_PREVIEW_BASE", "http://10.4.7.27:8080").rstrip("/")

WEBSITE_KEYWORDS = (
    "maak website",
    "maak een website",
    "bouw website",
    "bouw een website",
    "genereer website",
    "website voor",
    "website bouwen",
    "landingspagina",
    "landing page",
    "webshop",
    "webpagina",
    "nieuwe site",
    "nieuwe website",
    "/website",
    "web builder",
    "webbuilder",
    "agy",
    "antigravity",
    "anti gravity",
    "anti-gravity",
    "site maken",
    "maak een site",
    "bouw een site",
    "website laten maken",
    "laat agy",
    "via agy",
)

PROJECT_RE = re.compile(
    r"(?:website|site|project)\s+(?:voor\s+|called\s+|genaamd\s+)?['\"]?([a-z0-9][a-z0-9 _-]{1,48})['\"]?",
    re.I,
)
FOR_RE = re.compile(
    r"\bvoor\s+['\"]?([a-z0-9][a-z0-9 _-]{1,48})['\"]?",
    re.I,
)


def wants_website(raw: str) -> bool:
    t = (raw or "").strip().lower()
    return any(k in t for k in WEBSITE_KEYWORDS)


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 extract_project_name(raw: str) -> str:
    t = (raw or "").strip()
    m = PROJECT_RE.search(t)
    if m:
        return slugify(m.group(1))
    m = FOR_RE.search(t)
    if m:
        name = m.group(1)
        name = re.split(r"\s+(?:met|with|incl|including)\b", name, maxsplit=1, flags=re.I)[0]
        return slugify(name)
    for token in t.split():
        clean = slugify(token)
        if len(clean) >= 3 and clean not in (
            "maak", "bouw", "website", "site", "voor", "een", "the", "and", "met",
            "simpele", "landing", "page", "pagina", "landingspagina",
        ):
            return clean
    return slugify(t[:40]) or "website"


def extract_build_prompt(raw: str, project: str) -> str:
    body = (raw or "").strip()
    lower = body.lower()
    for k in WEBSITE_KEYWORDS:
        if lower.startswith(k):
            rest = body[len(k) :].strip(" :,-")
            if rest:
                return rest
    return (
        f"Bouw een complete, moderne, responsive website voor project '{project}'. "
        f"Opdracht: {body}. "
        "Gebruik index.html, style.css, script.js en een assets/ map. "
        "Foodlinkk halal kant-en-klaar food branding waar passend."
    )


async def _hermes_get(path: str, timeout: float = 30.0) -> dict[str, Any]:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.get(f"{HERMES_BUILD_URL}{path}")
        try:
            data = resp.json()
        except Exception:
            data = {"ok": False, "detail": resp.text[:500]}
        if resp.status_code >= 400:
            data.setdefault("ok", False)
        return data


async def _hermes_post(path: str, payload: dict[str, Any], timeout: float = 60.0) -> dict[str, Any]:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.post(f"{HERMES_BUILD_URL}{path}", json=payload)
        try:
            data = resp.json()
        except Exception:
            data = {"ok": False, "detail": resp.text[:500]}
        if resp.status_code >= 400:
            data.setdefault("ok", False)
            data.setdefault("detail", resp.text[:500])
        return data


async def list_projects() -> dict[str, Any]:
    return await _hermes_get("/api/projects")


async def get_build_status(project: str) -> dict[str, Any]:
    slug = slugify(project)
    return await _hermes_get(f"/api/build/{slug}/status")


async def build_from_message(raw: str, *, channel: str = "herman") -> dict[str, Any]:
    project = extract_project_name(raw)
    prompt = extract_build_prompt(raw, project)

    log_agent_event(
        "webbuilder",
        "website_build_start",
        f"Start build: {project}",
        prompt[:4000],
        channel=channel,
        metadata={"project": project, "prompt": prompt[:500]},
        status="running",
    )

    data = await _hermes_post(
        "/api/build",
        {"project": project, "prompt": prompt},
        timeout=45.0,
    )
    if not data.get("ok", True) and data.get("detail"):
        log_agent_event(
            "webbuilder",
            "website_build_error",
            f"Build mislukt: {project}",
            str(data.get("detail", data))[:2000],
            channel=channel,
            metadata={"project": project},
            status="error",
        )
        raise RuntimeError(str(data.get("detail") or data))

    job_id = data.get("job_id") or project
    preview = data.get("preview_url") or f"{HERMES_PREVIEW_BASE}/"
    nas_path = data.get("nas_path") or f"//10.4.7.11/share/Websites/{project}/"

    log_agent_event(
        "webbuilder",
        "website_build_running",
        f"agy bezig: {project}",
        f"Job {job_id}\nPreview: {preview}\nNAS: {nas_path}",
        channel=channel,
        metadata={
            "project": project,
            "job_id": job_id,
            "preview_url": preview,
            "nas_path": nas_path,
            "correlation_id": job_id,
        },
        status="running",
    )

    return {
        "project": project,
        "job_id": job_id,
        "preview_url": preview,
        "nas_path": nas_path,
        "prompt": prompt,
        "hermes_status": data.get("status", "running"),
    }


async def poll_until_done(project: str, *, channel: str = "herman", timeout: float = 1800.0) -> dict[str, Any]:
    """Poll Hermes build status until completed or failed."""
    import asyncio

    slug = slugify(project)
    elapsed = 0.0
    interval = 5.0
    last_log = ""

    while elapsed < timeout:
        st = await get_build_status(slug)
        status = str(st.get("status") or "unknown")
        msg = str(st.get("message") or status)
        if msg != last_log:
            log_agent_event(
                "webbuilder",
                "website_build_progress",
                msg[:255],
                (st.get("log_tail") or "")[:4000],
                channel=channel,
                metadata={"project": slug, "status": status},
                status="running" if status in ("running", "queued") else status,
            )
            last_log = msg

        if status == "completed":
            files = st.get("files") or []
            preview = st.get("preview_url") or f"{HERMES_PREVIEW_BASE}/"
            log_agent_event(
                "webbuilder",
                "website_build_done",
                f"Website klaar: {slug}",
                f"Bestanden: {', '.join(files[:8])}\nPreview: {preview}",
                channel=channel,
                metadata={
                    "project": slug,
                    "preview_url": preview,
                    "files": files,
                    "for_herman": True,
                    "source_agent": "webbuilder",
                },
                status="completed",
            )
            return {**st, "project": slug, "preview_url": preview}

        if status == "failed":
            detail = str(st.get("error") or st.get("message") or "Build mislukt")
            log_agent_event(
                "webbuilder",
                "website_build_error",
                f"Build mislukt: {slug}",
                detail[:2000],
                channel=channel,
                metadata={"project": slug},
                status="error",
            )
            raise RuntimeError(detail)

        await asyncio.sleep(interval)
        elapsed += interval

    raise TimeoutError(f"Build timeout voor {slug} na {int(timeout)}s")


async def generate_from_message(raw: str, *, channel: str = "herman", wait: bool = True) -> dict[str, Any]:
    started = await build_from_message(raw, channel=channel)
    if wait:
        finished = await poll_until_done(started["project"], channel=channel)
        started.update(finished)
    return started
