from fastapi.testclient import TestClient

from eda_agent.engine import EDAEngine
from eda_agent.services.normbase_client import MockNormbaseClient
from llm.providers import BaseLLMProvider
from eda_agent.main import app


class FakeLLMProvider(BaseLLMProvider):
    async def chat(self, messages, **kwargs):
        del messages, kwargs
        return "[]"

    async def structured_output(self, messages, schema):
        del messages, schema
        return {}

    async def async_get_embedding(self, text):
        del text
        return [0.1, 0.2, 0.3]


def test_eda_api_create_and_get_state(monkeypatch):
    from eda_agent.api import agent as agent_api

    def _fake_build_engine(profile):
        del profile
        return EDAEngine(MockNormbaseClient(norms=[]), FakeLLMProvider())

    monkeypatch.setattr(agent_api, "_build_engine", _fake_build_engine)

    with TestClient(app) as client:
        create_payload = {
            "agent_id": "api-agent-1",
            "agent_role": "analyst",
            "llm_provider": "doubao",
            "llm_api_key": "dummy-key",
            "llm_model": "dummy-model",
            "llm_embedding_model": "dummy-embedding",
            "llm_base_url": "https://example.com",
            "value_weights": [
                {
                    "value_name": "compliance",
                    "weight": 0.9,
                    "hierarchy": 1,
                    "rationale": "must",
                }
            ],
            "active_fields": ["field-a"],
            "trust_score": 0.8,
        }
        created = client.post("/api/v1/agents", json=create_payload)
        assert created.status_code in (201, 409)

        state = client.get("/api/v1/agents/api-agent-1")
        assert state.status_code == 200
        body = state.json()
        assert body["agent_id"] == "api-agent-1"
        assert body["agent_role"] == "analyst"

        listed = client.get("/api/v1/agents")
        assert listed.status_code == 200
        ids = [item["agent_id"] for item in listed.json()]
        assert "api-agent-1" in ids


def test_eda_bind_norms_upstream_failure(monkeypatch):
    from eda_agent.api import agent as agent_api

    class FailingNormbaseClient:
        async def retrieve_norms(self, context, agent_role, field_ids=None, top_k=5):
            del context, agent_role, field_ids, top_k
            raise TimeoutError("simulated timeout")

        async def get_default_norms(self, agent_role):
            del agent_role
            return []

        async def report_outcome(self, agent_id, norm_id, outcome, details):
            del agent_id, norm_id, outcome, details
            return None

    def _failing_engine(profile):
        del profile
        return EDAEngine(FailingNormbaseClient(), FakeLLMProvider())

    monkeypatch.setattr(agent_api, "_build_engine", _failing_engine)

    with TestClient(app) as client:
        create_payload = {
            "agent_id": "api-agent-fail",
            "agent_role": "analyst",
            "llm_provider": "doubao",
            "llm_api_key": "dummy-key",
            "llm_model": "dummy-model",
            "llm_embedding_model": "dummy-embedding",
            "active_fields": ["field-a"],
            "trust_score": 0.8,
        }
        _ = client.post("/api/v1/agents", json=create_payload)
        resp = client.post(
            "/api/v1/agents/api-agent-fail/bind_norms",
            json={"context": {"event": "x"}},
        )
        assert resp.status_code == 503
        assert "Normbase unavailable" in resp.json()["detail"]


def test_eda_trace_contains_identity_headers(monkeypatch):
    from eda_agent.api import agent as agent_api

    def _fake_build_engine(profile):
        del profile
        norms = [
            {
                "id": "n-trace-1",
                "type": "obligation",
                "scope_roles": ["analyst"],
                "status": "active",
                "condition": {"trigger": "requires_report"},
                "action": {"required": "mask_pii"},
                "consequence": {"on_violation": {"boundary": "soft"}},
            }
        ]
        return EDAEngine(MockNormbaseClient(norms=norms), FakeLLMProvider())

    monkeypatch.setattr(agent_api, "_build_engine", _fake_build_engine)

    with TestClient(app) as client:
        _ = client.post(
            "/api/v1/agents",
            json={
                "agent_id": "api-agent-trace",
                "agent_role": "analyst",
                "llm_provider": "doubao",
                "llm_api_key": "dummy-key",
                "llm_model": "dummy-model",
                "llm_embedding_model": "dummy-embedding",
                "active_fields": [],
                "trust_score": 0.8,
            },
        )
        bind = client.post(
            "/api/v1/agents/api-agent-trace/bind_norms",
            json={"context": {"requires_report": True}},
            headers={
                "X-Agent-Id": "api-agent-trace",
                "X-Key-Id": "kid-test",
                "X-Request-Id": "req-test",
                "X-Caller-Id": "caller-test",
            },
        )
        assert bind.status_code == 200, bind.text

        traces = client.get("/api/v1/agents/api-agent-trace/traces?limit=5")
        assert traces.status_code == 200, traces.text
        bind_trace = next((t for t in traces.json() if t["stage"] == "bind_norms"), None)
        assert bind_trace is not None
        identity = bind_trace["result_payload"].get("identity", {})
        assert identity.get("agent_id") == "api-agent-trace"
        assert identity.get("key_id") == "kid-test"
        assert identity.get("request_id") == "req-test"
        assert identity.get("caller_id") == "caller-test"


def test_eda_agent_update_and_delete(monkeypatch):
    from eda_agent.api import agent as agent_api

    def _fake_build_engine(profile):
        del profile
        return EDAEngine(MockNormbaseClient(norms=[]), FakeLLMProvider())

    monkeypatch.setattr(agent_api, "_build_engine", _fake_build_engine)

    with TestClient(app) as client:
        create_payload = {
            "agent_id": "api-agent-update",
            "agent_role": "analyst",
            "llm_provider": "doubao",
            "llm_api_key": "dummy-key",
            "llm_model": "dummy-model",
            "llm_embedding_model": "dummy-embedding",
            "active_fields": ["field-a"],
            "trust_score": 0.8,
        }
        _ = client.post("/api/v1/agents", json=create_payload)

        updated = client.patch(
            "/api/v1/agents/api-agent-update",
            json={"agent_role": "reviewer", "trust_score": 0.6, "active_fields": ["field-b"]},
        )
        assert updated.status_code == 200, updated.text
        assert updated.json()["agent_role"] == "reviewer"

        state = client.get("/api/v1/agents/api-agent-update")
        assert state.status_code == 200, state.text
        body = state.json()
        assert body["agent_role"] == "reviewer"
        assert body["trust_score"] == 0.6
        assert body["active_fields"] == ["field-b"]

        deleted = client.delete("/api/v1/agents/api-agent-update")
        assert deleted.status_code == 204, deleted.text

        missing = client.get("/api/v1/agents/api-agent-update")
        assert missing.status_code == 404

