from __future__ import annotations
import pytest

import re
from pathlib import Path

import yaml

from agent_framework.workflows import FileWorkflowRepository
from app.domain.contas import ContasDomainService
from app.domain.contas.workflow_actions import build_contas_workflow_actions

ROOT = Path(__file__).resolve().parents[2]


def test_domain_does_not_import_langgraph_directly():
    violations = []
    for base in (ROOT / "app", ROOT / "mcp"):
        for path in base.rglob("*.py"):
            text = path.read_text(encoding="utf-8", errors="ignore")
            if re.search(r"(^|\n)\s*(from\s+langgraph|import\s+langgraph)", text):
                violations.append(str(path.relative_to(ROOT)))
    assert not violations


def test_official_templates_use_framework_state_graph():
    for rel in (
        "agent_framework_oci/templates/agent_template_backend/app/workflows/agent_graph.py",
        "agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py",
    ):
        text = (ROOT / rel).read_text(encoding="utf-8")
        assert "FrameworkStateGraph" in text
        assert "from langgraph.graph" not in text


def test_all_active_workflow_actions_registered(monkeypatch):
    monkeypatch.setenv("TIM_USE_MOCK_GATEWAY", "true")
    registry = build_contas_workflow_actions(ContasDomainService())
    registered = set(registry._actions)
    repo = FileWorkflowRepository(ROOT / "workflows")
    active = []
    required = set()
    for marker in sorted((ROOT / "workflows").glob("*.active.yaml")):
        name = marker.name.removesuffix(".active.yaml")
        definition = repo.get_active(name)
        active.append((name, definition.version))
        required.update(node.action for node in definition.nodes)
    assert len(active) == 10
    assert required <= registered, sorted(required - registered)


def test_guardrail_config_declares_all_execution_stages():
    raw = yaml.safe_load((ROOT / "config" / "guardrails.yaml").read_text(encoding="utf-8"))
    assert raw.get("input")
    assert raw.get("output")
    assert raw.get("retrieval")
    assert raw.get("tool")


def test_contestacao_workflow_encerra_imediatamente_quando_cval_bloqueia():
    import yaml
    from pathlib import Path
    wf = yaml.safe_load((Path(__file__).parents[2] / "workflows" / "contestacao_tool.v2.yaml").read_text())
    edges = wf["edges"]
    blocked = [e for e in edges if e.get("from") == "abrir_contestacao_cliente" and e.get("to") == "END"]
    assert blocked
    assert blocked[0]["priority"] == 1
    assert blocked[0]["when"] == {"eq": ["$.vars.abrir_contestacao_cliente.success", False]}


@pytest.mark.asyncio
async def test_workflow_runtime_preserva_snapshot_parcial_quando_action_posterior_falha(tmp_path, monkeypatch):
    from types import SimpleNamespace
    from agent_framework.workflows.models import WorkflowDefinition, WorkflowNode
    from agent_framework.workflows.runtime import WorkflowRuntime

    class Repo:
        def get_active(self, name):
            return WorkflowDefinition(name=name, version=1, start="a", nodes=[WorkflowNode(id="a", action="noop")], edges=[])
    class Graph:
        async def ainvoke(self, initial, config=None):
            raise RuntimeError("API contestacao indisponivel")
        async def aget_state(self, config=None):
            return SimpleNamespace(values={
                "nodes": {"registrar_protocolo": {"protocolo_id": "PRT-123"}},
                "trace": [{"node": "registrar_protocolo", "status": "COMPLETED"}],
                "input": {"msisdn": "119"},
            })
    runtime = WorkflowRuntime(Repo())  # type: ignore[arg-type]
    monkeypatch.setattr(runtime, "_compile", lambda definition: Graph())
    result = await runtime.arun("contestacao_tool", {"msisdn": "119"}, execution_id="exec-1")
    assert result.status == "FAILED"
    assert result.output["registrar_protocolo"]["protocolo_id"] == "PRT-123"
    assert result.trace == [{"node": "registrar_protocolo", "status": "COMPLETED"}]
    assert result.state["input"]["msisdn"] == "119"


def test_agent_workflow_init_realmente_inicializa_router_agentes_e_graph():
    import ast
    from pathlib import Path
    source = (Path(__file__).parents[2] / "app" / "workflows" / "agent_graph.py").read_text()
    tree = ast.parse(source)
    cls = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == "AgentWorkflow")
    init = next(n for n in cls.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and n.name == "__init__")
    assigned = set()
    for node in ast.walk(init):
        if isinstance(node, ast.Assign):
            for target in node.targets:
                if isinstance(target, ast.Attribute) and isinstance(target.value, ast.Name) and target.value.id == "self":
                    assigned.add(target.attr)
    assert {"router", "faturas", "vas", "contestacao", "suporte_contas", "graph"} <= assigned
