"""Contexto operacional rico para chat investigativo — Spec 030+."""

from __future__ import annotations

import sqlite3

from app.agents.catalog import AGENT_CATALOG
from app.agents import store


AGENT_INVESTIGATION_LENS: dict[str, str] = {
    "A0": "Visão global: correlacionar incidentes, delegar aos agentes certos, priorizar por severidade.",
    "A1": "Investigar: VM112 wizard/Carbonio, Proxmox cluster, CPU/RAM, containers down, restarts.",
    "A2": "Investigar: DNS Cloudflare, Traefik CT114, certificados LE, SNI, pfSense API/regras.",
    "A3": "Investigar: SPF/DKIM/DMARC, entregabilidade mail, registos DNS por domínio tenant.",
    "A4": "Investigar: filas mail, amavis/clamav, spam, quarentena, bloqueios de envio.",
    "A5": "Investigar: alertas Wazuh VM104, correlação SIEM, timeline segurança, runbooks R0/R1.",
    "A6": "Investigar: tickets Desk, onboarding stuck, findings abertos, contexto cliente, runbooks KB.",
    "A7": "Investigar: runbooks aprovados pós-incidente, remediação R0–R3, aprovações pendentes.",
    "sentinel": "Investigar: health checks T0 (desk, wizard, pfSense, proxmox, ollama, VM123 stack).",
}


def _fmt_incident(inc: dict) -> str:
    action = (inc.get("suggested_human_action") or "Investigar manualmente.")[:200]
    return (
        f"- [{inc.get('severity', '?').upper()}] {inc.get('title')} "
        f"(cenário `{inc.get('scenario_id')}`, agente {inc.get('agent_name', inc.get('primary_agent'))}, "
        f"visto {inc.get('occurrence_count', 1)}×, última vez {inc.get('last_seen_at', '?')})\n"
        f"  → Acção sugerida: {action}"
    )


def build_ops_context(
    conn: sqlite3.Connection,
    question: str,
    target_agent: str,
    *,
    include_findings: bool = True,
    kb_snippets: list[str] | None = None,
) -> str:
    """Monta dossiê operacional para respostas investigativas."""
    sections: list[str] = []
    ov = store.get_overview(conn)
    open_inc = ov.get("incidents_open") or {}

    sections.append(
        "### Estado do ambiente (dados reais do Agentic Ops)\n"
        f"- Provider LLM: {ov.get('provider')} / {ov.get('model')}\n"
        f"- Último tick worker: {ov.get('last_tick_at') or 'desconhecido'} — status `{ov.get('last_tick_status', '?')}`\n"
        f"- Cenários vigilância: **{ov.get('scenarios_ok', 0)}/{ov.get('scenarios_total', 0)} OK**\n"
        f"- Incidentes abertos: {open_inc.get('critical', 0)} crít · "
        f"{open_inc.get('high', 0)} alto · {open_inc.get('warn', 0)} aviso"
    )

    lens = AGENT_INVESTIGATION_LENS.get(target_agent, AGENT_INVESTIGATION_LENS["A6"])
    profile = AGENT_CATALOG.get(target_agent, AGENT_CATALOG["A6"])
    sections.append(
        f"### Lente investigativa do agente {profile.name} ({target_agent})\n{lens}\n"
        f"Competências: {', '.join(profile.reads[:4])}"
    )

    incidents = store.list_incidents(conn, status="open", limit=15)
    if target_agent not in ("A0", "A6", "ALL"):
        agent_first = [i for i in incidents if i.get("primary_agent") == target_agent]
        other = [i for i in incidents if i.get("primary_agent") != target_agent][:5]
        incidents = agent_first + other
    if incidents:
        sections.append(
            "### Incidentes activos (deduplicados por cenário)\n"
            + "\n".join(_fmt_incident(i) for i in incidents[:8])
        )
    else:
        sections.append("### Incidentes activos\nNenhum incidente aberto no momento.")

    scenarios = store.list_scenarios(conn)
    failing = [s for s in scenarios if s.get("last_run_status") not in (None, "ok")]
    if failing:
        fail_lines = []
        for s in failing[:8]:
            fail_lines.append(
                f"- `{s['id']}` — {s.get('title', s['id'])}: status **{s.get('last_run_status')}** "
                f"(último run {s.get('last_run_at', '?')})"
            )
        sections.append("### Cenários com falha recente\n" + "\n".join(fail_lines))

    if include_findings:
        findings = store.list_findings(conn, limit=10, open_only=True)
        if findings:
            flines = []
            for f in findings[:8]:
                detail = (f.get("detail_md") or f.get("title") or "")[:180]
                flines.append(
                    f"- [{f.get('severity')}] **{f.get('title')}** — {detail}\n"
                    f"  → Humano: {(f.get('suggested_human_action') or 'verificar')[:160]}"
                )
            sections.append("### Findings T0/T1 abertos\n" + "\n".join(flines))

    if kb_snippets:
        sections.append(
            "### Base de conhecimento (RAG — specs/runbooks)\n"
            + "\n---\n".join(kb_snippets[:6])[:4500]
        )

    sections.append(
        "### Infra de referência Ligbox\n"
        "- Proxmox host · pfSense WAN/LAN · VM112 wizard/onboard · VM122 Desk/API · VM123 finance/Ollama\n"
        "- API pfSense via Traefik: `firewall.itecnologys.com` · Desk: `desk.ligbox.com.br`"
    )

    return "\n\n".join(sections)


RESPONSE_FORMAT_INSTRUCTION = """
## Formato OBRIGATÓRIO da resposta (markdown)

Estruture SEMPRE assim — respostas planas ou genéricas são proibidas:

### 🔍 Diagnóstico
Sintetize o que os **dados do contexto** indicam sobre a pergunta. Cite incidentes/findings/cenários relevantes por nome.

### 🧪 Hipóteses (ordem de probabilidade)
1. **Hipótese mais provável** — porquê, o que corrobora
2. **Hipótese alternativa** — o que verificar para confirmar/descartar
3. (opcional) terceira hipótese se aplicável

### ✅ Plano de acção proposto
| # | Prioridade | Acção concreta | Onde / comando |
|---|------------|----------------|----------------|
| 1 | Alta/Média/Baixa | passo executável | `comando` ou painel/link |
| 2 | ... | ... | ... |

Inclua **comandos shell** ou **URLs/painéis** quando souber (sem inventar credenciais).

### ⚠️ Riscos e impacto
- O que pode piorar se ignorarmos
- Dependências (ex.: reiniciar X afecta Y)

### ▶️ Próximo passo imediato
Uma frase directa: **faça isto agora** — a acção única de maior valor.

---
Regras: português BR · baseie-se no contexto injectado · se faltar dado, diga **exactamente** o que o operador deve correr/verificar · não invente estados de serviços.
"""


def _question_relevance(question: str, *texts: str) -> int:
    q = question.lower()
    score = 0
    for t in texts:
        if not t:
            continue
        tl = str(t).lower()
        for word in q.split():
            if len(word) > 3 and word in tl:
                score += 2
        if tl in q or q in tl:
            score += 3
    return score


def build_suggested_actions(
    conn: sqlite3.Connection,
    question: str,
    target_agent: str,
    *,
    limit: int = 6,
) -> list[dict]:
    """Acções executáveis no Desk — checks T0 e ack de incidentes."""
    actions: list[dict] = []
    seen_scenarios: set[str] = set()
    seen_incidents: set[int] = set()

    def add_run(scenario_id: str, label: str, severity: str = "info") -> None:
        if scenario_id in seen_scenarios or len(actions) >= limit:
            return
        if not store.get_scenario(conn, scenario_id):
            return
        seen_scenarios.add(scenario_id)
        actions.append({
            "type": "run_scenario",
            "scenario_id": scenario_id,
            "label": label,
            "severity": severity,
        })

    def add_ack(incident_id: int, label: str, severity: str, scenario_id: str) -> None:
        if incident_id in seen_incidents or len(actions) >= limit:
            return
        seen_incidents.add(incident_id)
        actions.append({
            "type": "ack_incident",
            "incident_id": incident_id,
            "scenario_id": scenario_id,
            "label": label,
            "severity": severity,
        })

    incidents = store.list_incidents(conn, status="open", limit=15)
    ranked_inc = sorted(
        incidents,
        key=lambda i: (
            -{"critical": 4, "high": 3, "warn": 2, "info": 1}.get(i.get("severity", "info"), 0),
            -_question_relevance(question, i.get("title", ""), i.get("scenario_id", "")),
        ),
    )
    for inc in ranked_inc[:5]:
        sid = inc.get("scenario_id") or ""
        title = inc.get("title") or sid
        sev = inc.get("severity") or "warn"
        rel = _question_relevance(question, title, sid)
        if rel > 0 or len(actions) < 2:
            add_run(sid, f"▶ Executar check: {title[:48]}", sev)
        if rel > 0 and inc.get("id"):
            add_ack(int(inc["id"]), f"✓ Ack #{inc['id']} · {title[:32]}", sev, sid)

    for s in store.list_scenarios(conn):
        if s.get("last_run_status") not in (None, "ok"):
            add_run(
                s["id"],
                f"▶ Re-validar: {(s.get('title') or s['id'])[:42]}",
                "warn" if s.get("last_run_status") == "fail" else "info",
            )

    profile = AGENT_CATALOG.get(target_agent)
    if profile:
        for sid in profile.scenarios:
            sc = store.get_scenario(conn, sid)
            if sc:
                add_run(sid, f"▶ Check: {(sc.get('title') or sid)[:40]}", "info")

    if len(actions) < 2 and target_agent in ("A6", "A0", "sentinel"):
        for sid, lbl in (
            ("desk.api.health", "▶ Health Desk API"),
            ("proxmox.cluster", "▶ Health Proxmox"),
            ("ollama.vm123.health", "▶ Health Ollama VM123"),
        ):
            add_run(sid, lbl, "info")

    return actions[:limit]
