"""Interactive agent terminal — parse and execute whitelisted commands."""
from __future__ import annotations

import json
import os
from typing import Any, Optional

import httpx

from app.db import fetch_all, fetch_one
from app.services import agent_integration, agent_souls, herman, webbuilder_agent
from app.services.agent_events_log import log_agent_event
from app.services.agent_names import normalize_agent_key

TOOLS_URL = os.getenv("TOOLS_API_URL", "http://tools-api:8700").rstrip("/")
BROWSER_URL = os.getenv("BROWSER_AGENT_URL", "http://browser-agent:7790").rstrip("/")

Line = dict[str, Any]

GLOBAL_HELP = [
    "help              — dit overzicht",
    "status            — huidige status & taak",
    "history           — laatste events (in terminal)",
    "handoff <agent> <type> [notitie]",
    "say <bericht>     — log een notitie",
    "ask <vraag>       — AI-antwoord in rol van deze agent",
]

AGENT_HELP: dict[str, list[str]] = {
    "herman": [
        "briefing          — dagrapport genereren",
        "<tekst>           — vraag / opdracht aan Herman",
    ],
    "browser": [
        "monitor add <url> — site toevoegen aan monitor",
        "monitor list      — actieve sites",
        "browse <url>      — pagina openen",
        "crawl             — alle sites crawlen + parse-overzicht",
        "parse             — laatste parse-resultaten",
        "parse <id>        — parse van specifieke site",
        "intel             — hype & trend analyse (samenvatting)",
        "intel <zoekterm>  — filter analyse",
    ],
    "research": ["run               — research pipeline starten", "briefs            — recente briefs"],
    "retail": ["rss               — retail RSS verversen", "scores            — halal opportunity scores"],
    "sysops": [
        "backup            — config backup (approval)",
        "scan              — maintenance scan",
        "topology          — infra overzicht",
        "status            — sysops status",
    ],
    "marketing": ["reco              — aanbevelingen genereren"],
    "packaging": ["design <brief>    — packaging opdracht (via Herman)"],
    "webbuilder": [
        "build <opdracht>  — website bouwen (agy op Hermes)",
        "projects          — bestaande website-projecten",
        "status [project]  — build-status",
        "preview [project] — preview-URL",
    ],
    "email": ["sync              — inbox sync trigger"],
    "finance": ["margins           — marge-overzicht (placeholder)"],
}


def _line(msg: str, *, detail: str = "", line_type: str = "output") -> Line:
    return {"type": line_type, "message": msg, "detail": detail}


def _help_lines(key: str) -> list[Line]:
    lines = [_line("Beschikbare commando's:", line_type="output")]
    for h in GLOBAL_HELP:
        lines.append(_line("  " + h, line_type="output"))
    extra = AGENT_HELP.get(key, [])
    if extra:
        lines.append(_line(f"— {key} —", line_type="output"))
        for h in extra:
            lines.append(_line("  " + h, line_type="output"))
    return lines


def _status_lines(key: str, soul: dict[str, Any]) -> list[Line]:
    recent = soul.get("recent_events") or []
    last = recent[0] if recent else {}
    lines = [
        _line(f"{soul.get('display_name') or key} · {soul.get('role_title') or 'agent'}"),
        _line(f"Status: {last.get('status') or soul.get('current_status') or 'standby'}"),
        _line(f"Taak: {last.get('title') or soul.get('current_task') or '—'}"),
        _line(f"Events totaal: {soul.get('event_count') or 0}"),
    ]
    resp = (soul.get("responsibilities") or "").strip()
    if resp:
        lines.append(_line(resp[:220], detail=resp if len(resp) > 220 else ""))
    return lines


async def _tools_post(path: str, payload: dict | None = None, timeout: float = 180.0) -> dict[str, Any]:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.post(f"{TOOLS_URL}{path}", json=payload or {})
        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 _tools_get(path: str, timeout: float = 60.0) -> dict[str, Any]:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.get(f"{TOOLS_URL}{path}")
        try:
            return resp.json()
        except Exception:
            return {"ok": False, "detail": resp.text[:500]}


async def _browser_post(path: str, payload: dict, timeout: float = 120.0) -> dict[str, Any]:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.post(f"{BROWSER_URL}{path}", json=payload)
        try:
            return resp.json()
        except Exception:
            return {"ok": False, "detail": resp.text[:500]}


def _handoff(src: str, dst_raw: str, htype: str, note: str) -> list[Line]:
    dst = normalize_agent_key(dst_raw)
    if not dst:
        return [_line("Onbekende agent: " + dst_raw, line_type="error")]
    payload = {"note": note} if note else {}
    try:
        agent_integration.create_handoff(src, dst, handoff_type=htype, payload=payload)
        return [_line(f"Handoff → {dst} ({htype})" + (f": {note}" if note else ""))]
    except Exception as exc:
        return [_line("Handoff mislukt: " + str(exc), line_type="error")]


async def _agent_ask(key: str, soul: dict[str, Any], question: str) -> list[Line]:
    name = soul.get("display_name") or key
    role = soul.get("role_title") or ""
    prompt = (
        f"Je bent {name} ({key}), {role}. "
        f"Beantwoord kort en actiegericht in het Nederlands.\n\nOpdracht: {question}"
    )
    result = await herman.chat(prompt, channel=f"terminal:{key}")
    reply = (result.get("reply") or "").strip()
    delegated = result.get("delegated_agents") or []
    lines = [_line(reply or "(geen antwoord)", detail=reply)]
    if delegated:
        lines.append(_line("Gedelegeerd: " + ", ".join(delegated), line_type="handoff_out"))
    log_agent_event(
        key,
        "terminal_out",
        reply[:255] if reply else "Antwoord",
        reply[:4000],
        channel="terminal",
        metadata={"delegated": delegated, "question": question[:500]},
    )
    return lines


def _format_crawl_results(result: dict[str, Any]) -> list[Line]:
    if result.get("message") and not result.get("results"):
        return [_line(result["message"], line_type="output")]
    lines: list[Line] = [
        _line(
            f"Crawl klaar — {result.get('sites', 0)} site(s), "
            f"{result.get('changed', 0)} wijziging(en), {result.get('errors', 0)} fout(en)"
        )
    ]
    for r in result.get("results") or []:
        if r.get("status") == "ERROR":
            lines.append(_line(f"✗ {r.get('name') or r.get('url')}: {r.get('error')}", line_type="error"))
            continue
        flag = "△" if r.get("changed") else "✓"
        lines.append(
            _line(
                f"{flag} {r.get('title') or r.get('name')} — {r.get('word_count', 0)} woorden, "
                f"{r.get('links_count', 0)} links",
                detail=(r.get("excerpt") or "")[:200],
            )
        )
        headings = r.get("headings") or []
        if headings:
            lines.append(_line("  H: " + " · ".join(headings[:4])[:180], line_type="output"))
        for link in (r.get("links_sample") or [])[:3]:
            lbl = link.get("label") or link.get("href") or ""
            lines.append(_line("  → " + lbl[:70], line_type="output"))
    return lines


def _format_parse_items(items: list[dict[str, Any]]) -> list[Line]:
    if not items:
        return [_line("Geen parse-resultaten — voer eerst crawl uit.")]
    lines: list[Line] = [_line(f"Parse-overzicht ({len(items)} pagina's):")]
    for p in items:
        lines.append(
            _line(
                f"#{p.get('site_id') or '—'} {p.get('title') or p.get('url')} — "
                f"{p.get('word_count', 0)} woorden",
                detail=(p.get("excerpt") or "")[:200],
            )
        )
        if p.get("meta_description"):
            lines.append(_line("  desc: " + str(p["meta_description"])[:120], line_type="output"))
        for h in (p.get("headings") or [])[:3]:
            lines.append(_line("  · " + h[:90], line_type="output"))
    return lines


async def _run_agent_command(key: str, soul: dict[str, Any], cmd: str, args: list[str], raw: str) -> list[Line]:
    if key == "herman":
        if cmd == "briefing":
            try:
                content = await herman.generate_briefing()
                preview = (content or "")[:400]
                log_agent_event("herman", "terminal_out", "Briefing gegenereerd", content[:4000], channel="terminal")
                return [_line("CEO briefing gegenereerd.", detail=preview)]
            except Exception as exc:
                return [_line("Briefing mislukt: " + str(exc), line_type="error")]
        result = await herman.chat(raw, channel="terminal")
        reply = (result.get("reply") or "").strip()
        delegated = result.get("delegated_agents") or []
        lines = [_line(reply or "(geen antwoord)", detail=reply)]
        if delegated:
            lines.append(_line("→ " + ", ".join(delegated), line_type="handoff_out"))
        log_agent_event("herman", "terminal_out", reply[:255] if reply else "Antwoord", reply[:4000], channel="terminal")
        return lines

    if key == "browser":
        if cmd == "monitor" and args and args[0].lower() == "add" and len(args) >= 2:
            url = args[1]
            from app.services.monitor import add_site

            site = add_site(url, name=url[:80])
            log_agent_event(
                "browser",
                "monitor_site_added",
                f"Terminal: site toegevoegd {url[:120]}",
                "",
                channel="terminal",
                metadata={"url": url, "site_id": site.get("id")},
            )
            return [_line(f"Monitor: {url} toegevoegd (id {site.get('id')})")]
        if cmd == "monitor" and args and args[0].lower() == "list":
            rows = fetch_all(
                "SELECT id, url, is_active FROM monitored_sites ORDER BY id DESC LIMIT 15"
            )
            if not rows:
                return [_line("Geen monitor-sites.")]
            return [_line(f"#{r['id']} {'✓' if r.get('is_active') else '○'} {r['url']}") for r in rows]
        if cmd == "browse" and args:
            url = args[0]
            data = await _browser_post("/browse", {"url": url, "purpose": "terminal"})
            log_agent_event("browser", "browse", f"Terminal browse: {url[:120]}", json.dumps(data)[:500], channel="terminal")
            title = data.get("title") or data.get("url") or url
            return [_line(f"Browse OK: {title}")]
        if cmd == "crawl":
            from app.services.monitor import trigger_crawl

            site_id = int(args[0]) if args and str(args[0]).isdigit() else None
            result = trigger_crawl(site_id)
            log_agent_event(
                "browser",
                "monitor_crawl",
                f"Terminal crawl ({result.get('sites', 0)} sites)",
                json.dumps([{k: r.get(k) for k in ("url", "title", "word_count", "status")} for r in result.get("results", [])])[:800],
                channel="terminal",
            )
            return _format_crawl_results(result)
        if cmd == "parse":
            from app.services.monitor import list_parse_results

            sid = int(args[0]) if args and str(args[0]).isdigit() else None
            return _format_parse_items(list_parse_results(site_id=sid, limit=10))
        if cmd == "intel":
            from app.services.monitor import build_parse_intelligence

            q = " ".join(args) if args else None
            data = build_parse_intelligence(query=q, limit=20)
            lines = [
                _line(
                    f"Analyse: {data['summary']['pages']} pagina's · "
                    f"{data['summary']['total_words']} woorden · "
                    f"{data['summary']['themes_detected']} trend-termen"
                ),
                _line("Open Browser → tab Parse Analyse voor volledig overzicht", line_type="output"),
            ]
            for t in (data.get("hype_terms") or [])[:12]:
                flag = "★ " if t.get("cross_site") else ""
                lines.append(
                    _line(f"{flag}{t['term']} ({t['score']}) — {', '.join(t.get('sites') or [])[:3]}", line_type="output")
                )
            return lines

    if key == "research":
        if cmd == "run":
            data = await _tools_post("/research/run")
            ok = data.get("ok", True)
            log_agent_event("research", "terminal_action", "Research run (terminal)", json.dumps(data)[:500], channel="terminal")
            return [_line("Research pipeline: " + ("OK" if ok else "fout"), detail=json.dumps(data)[:300])]
        if cmd == "briefs":
            data = await _tools_get("/research/briefs")
            items = data if isinstance(data, list) else data.get("items") or []
            if not items:
                return [_line("Geen briefs.")]
            return [_line(str(b.get("title") or b.get("id") or b))[:120] for b in items[:8]]

    if key == "retail":
        if cmd == "rss":
            data = await _tools_post("/retail/rss/refresh")
            log_agent_event("retail", "terminal_action", "RSS refresh (terminal)", json.dumps(data)[:300], channel="terminal")
            return [_line("RSS verversd.", detail=json.dumps(data)[:200])]
        if cmd == "scores":
            data = await _tools_post("/retail/compute-opportunities")
            return [_line("Opportunity scores bijgewerkt.", detail=json.dumps(data)[:200])]

    if key == "sysops":
        if cmd == "backup":
            data = await _tools_post("/ops/backup/run")
            log_agent_event("sysops", "terminal_action", "Backup (terminal)", json.dumps(data)[:400], channel="terminal")
            return [_line("Backup uitgevoerd.", detail=json.dumps(data)[:300])]
        if cmd == "scan":
            data = await _tools_post("/ops/maintenance/scan")
            return [_line("Maintenance scan klaar.", detail=json.dumps(data)[:300])]
        if cmd == "topology":
            data = await _tools_get("/ops/topology")
            return [_line("Topology geladen.", detail=json.dumps(data)[:400])]
        if cmd == "status":
            data = await _tools_get("/ops/status")
            return [_line(json.dumps(data)[:350], detail=json.dumps(data)[:800])]

    if key == "marketing" and cmd == "reco":
        data = await _tools_post("/recommendations/generate")
        return [_line("Aanbevelingen gegenereerd.", detail=json.dumps(data)[:300])]

    if key == "packaging" and cmd == "design":
        brief = " ".join(args) or raw
        result = await herman.chat(f"Packaging design: {brief}", channel="terminal:packaging")
        reply = (result.get("reply") or "").strip()
        return [_line(reply, detail=reply)]

    if key == "webbuilder":
        if cmd == "build":
            brief = rest or raw
            if not brief:
                return [_line("Gebruik: build <opdracht>", line_type="error")]
            try:
                outcome = await webbuilder_agent.generate_from_message(brief, channel="terminal", wait=False)
                proj = outcome.get("project") or "?"
                preview = outcome.get("preview_url") or "—"
                return [
                    _line(f"Build gestart: {proj}"),
                    _line(f"Preview: {preview}", line_type="output"),
                    _line("Volg voortgang live in dit terminal-venster.", line_type="output"),
                ]
            except Exception as exc:
                return [_line("Build mislukt: " + str(exc), line_type="error")]
        if cmd == "projects":
            data = await webbuilder_agent.list_projects()
            items = data.get("items") or []
            if not items:
                return [_line("Geen projecten op Hermes/NAS.")]
            return [
                _line(
                    f"{p.get('slug')} — {p.get('files', 0)} bestand(en)"
                    + (" · index.html ✓" if p.get("has_index") else "")
                )
                for p in items
            ]
        if cmd == "status":
            slug = args[0] if args else ""
            if not slug:
                return [_line("Gebruik: status <project>", line_type="error")]
            st = await webbuilder_agent.get_build_status(slug)
            lines = [_line(f"{st.get('project')} · {st.get('status')} — {st.get('message') or ''}")]
            tail = (st.get("log_tail") or "")[:300]
            if tail:
                lines.append(_line(tail, line_type="output"))
            return lines
        if cmd == "preview":
            slug = args[0] if args else ""
            if slug:
                st = await webbuilder_agent.get_build_status(slug)
                url = st.get("preview_url") or webbuilder_agent.HERMES_PREVIEW_BASE
            else:
                url = webbuilder_agent.HERMES_PREVIEW_BASE
            return [_line(f"Preview: {url}")]

    return await _agent_ask(key, soul, raw)


async def execute_terminal_command(
    agent_key: str,
    command: str,
    *,
    issued_by: str = "ceo",
) -> dict[str, Any]:
    key = normalize_agent_key(agent_key)
    soul = agent_souls.get_soul(key)
    if not soul:
        raise ValueError("Agent niet gevonden")

    raw = (command or "").strip()
    if not raw:
        raise ValueError("Leeg commando")

    log_agent_event(
        key,
        "terminal_in",
        raw[:255],
        "",
        channel="terminal",
        metadata={"issued_by": issued_by, "command": raw[:500]},
    )

    parts = raw.split()
    cmd = parts[0].lower()
    args = parts[1:]
    rest = " ".join(args)

    if cmd in ("help", "?", "h"):
        lines = _help_lines(key)
    elif cmd == "status":
        lines = _status_lines(key, soul)
    elif cmd == "handoff":
        if len(args) < 2:
            lines = [_line("Gebruik: handoff <agent> <type> [notitie]", line_type="error")]
        else:
            note = " ".join(args[2:]) if len(args) > 2 else ""
            lines = _handoff(key, args[0], args[1], note)
    elif cmd == "say":
        if not rest:
            lines = [_line("Gebruik: say <bericht>", line_type="error")]
        else:
            log_agent_event(key, "terminal_note", rest[:255], rest, channel="terminal")
            lines = [_line("Genoteerd.")]
    elif cmd == "ask":
        if not rest:
            lines = [_line("Gebruik: ask <vraag>", line_type="error")]
        else:
            lines = await _agent_ask(key, soul, rest)
    elif cmd == "history":
        events = agent_souls.list_agent_events(key, limit=8)
        if not events:
            lines = [_line("Geen history.")]
        else:
            lines = []
            for ev in reversed(events):
                t = (ev.get("created_at") or "")[11:19] or "--:--"
                lines.append(_line(f"{t} {ev.get('title') or ev.get('event_type')}"))
    else:
        lines = await _run_agent_command(key, soul, cmd, args, raw)

    return {"ok": True, "agent_key": key, "command": raw, "lines": lines}
