"""
Tests for autopil.api (FastAPI endpoints)

Coverage:
  - GET  /health                         — liveness check
  - POST /v1/context/evaluate            — allow and deny paths, audit persistence
  - GET  /v1/audit/sessions/{id}         — trail for known and unknown session
  - GET  /v1/audit/events                — all events, with decision and role filters
  - GET  /v1/audit/stats                 — summary counts
  - GET  /v1/policies                    — lists loaded policies
  - POST /v1/policies/reload             — hot-reload response
"""

import uuid

import pytest

from autopil.models import Decision


def _eval_payload(**overrides):
    base = {
        "query": "test query",
        "agent_role": "analyst",
        "user_id": "u1",
        "source_id": "reports",
        "sensitivity_level": "low",
        "session_id": str(uuid.uuid4()),
    }
    base.update(overrides)
    return base


# ── health ────────────────────────────────────────────────────────────────────

class TestHealth:
    def test_health_returns_ok(self, client):
        r = client.get("/health")
        assert r.status_code == 200
        body = r.json()
        assert body["status"] == "ok"
        assert body["version"] == "0.1.0"

    def test_health_includes_paths(self, client, policy_file, audit_db):
        r = client.get("/health")
        body = r.json()
        assert body["policy_file"] == policy_file
        assert body["audit_db"]    == audit_db


# ── evaluate ─────────────────────────────────────────────────────────────────

class TestEvaluate:
    def test_allowed_request_returns_allow(self, client):
        r = client.post("/v1/context/evaluate", json=_eval_payload())
        assert r.status_code == 200
        body = r.json()
        assert body["decision"]     == Decision.ALLOW
        assert body["policy_name"]  == "analyst_policy"
        assert "event_id" in body
        assert "session_id" in body

    def test_denied_request_returns_deny(self, client):
        r = client.post(
            "/v1/context/evaluate",
            json=_eval_payload(source_id="executive_comms"),
        )
        assert r.status_code == 200
        body = r.json()
        assert body["decision"] == Decision.DENY

    def test_unknown_role_returns_deny(self, client):
        r = client.post(
            "/v1/context/evaluate",
            json=_eval_payload(agent_role="unknown_role"),
        )
        assert r.status_code == 200
        assert r.json()["decision"] == Decision.DENY

    def test_evaluate_persists_event_to_audit_log(self, client):
        sid = str(uuid.uuid4())
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid))

        r = client.get(f"/v1/audit/sessions/{sid}")
        assert r.status_code == 200
        assert r.json()["total_events"] == 1

    def test_sensitivity_above_ceiling_is_denied(self, client):
        r = client.post(
            "/v1/context/evaluate",
            json=_eval_payload(agent_role="viewer", source_id="reports",
                               sensitivity_level="restricted"),
        )
        assert r.json()["decision"] == Decision.DENY


# ── audit sessions ────────────────────────────────────────────────────────────

class TestAuditSessions:
    def test_known_session_returns_trail(self, client):
        sid = str(uuid.uuid4())
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid))
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid))

        r = client.get(f"/v1/audit/sessions/{sid}")
        assert r.status_code == 200
        body = r.json()
        assert body["total_events"] == 2
        assert body["allowed"]      == 2
        assert body["denied"]       == 0

    def test_mixed_trail_counts_correctly(self, client):
        sid = str(uuid.uuid4())
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid, source_id="reports"))
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid, source_id="executive_comms"))

        body = client.get(f"/v1/audit/sessions/{sid}").json()
        assert body["allowed"] == 1
        assert body["denied"]  == 1

    def test_unknown_session_returns_404(self, client):
        r = client.get(f"/v1/audit/sessions/{uuid.uuid4()}")
        assert r.status_code == 404

    def test_trail_events_contain_expected_fields(self, client):
        sid = str(uuid.uuid4())
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid))

        event = client.get(f"/v1/audit/sessions/{sid}").json()["events"][0]
        for field in ("event_id", "session_id", "agent_role", "user_id",
                      "source_id", "query", "decision", "policy_name", "reason", "timestamp"):
            assert field in event


# ── audit events ──────────────────────────────────────────────────────────────

class TestAuditEvents:
    def test_returns_all_events(self, client):
        for _ in range(3):
            client.post("/v1/context/evaluate", json=_eval_payload())
        r = client.get("/v1/audit/events")
        assert r.status_code == 200
        assert len(r.json()) == 3

    def test_filter_by_decision_allow(self, client):
        client.post("/v1/context/evaluate", json=_eval_payload(source_id="reports"))
        client.post("/v1/context/evaluate", json=_eval_payload(source_id="executive_comms"))

        r = client.get("/v1/audit/events?decision=ALLOW")
        assert all(e["decision"] == "ALLOW" for e in r.json())

    def test_filter_by_decision_deny(self, client):
        client.post("/v1/context/evaluate", json=_eval_payload(source_id="reports"))
        client.post("/v1/context/evaluate", json=_eval_payload(source_id="executive_comms"))

        r = client.get("/v1/audit/events?decision=DENY")
        assert all(e["decision"] == "DENY" for e in r.json())

    def test_filter_by_agent_role(self, client):
        client.post("/v1/context/evaluate", json=_eval_payload(agent_role="analyst"))
        client.post("/v1/context/evaluate", json=_eval_payload(agent_role="viewer"))

        r = client.get("/v1/audit/events?agent_role=viewer")
        events = r.json()
        assert len(events) == 1
        assert events[0]["agent_role"] == "viewer"

    def test_empty_db_returns_empty_list(self, client):
        r = client.get("/v1/audit/events")
        assert r.json() == []


# ── audit stats ───────────────────────────────────────────────────────────────

class TestAuditStats:
    def test_empty_db_returns_zero_counts(self, client):
        r = client.get("/v1/audit/stats")
        assert r.status_code == 200
        body = r.json()
        assert body["total_events"]    == 0
        assert body["allowed"]         == 0
        assert body["denied"]          == 0
        assert body["active_sessions"] == 0

    def test_counts_match_recorded_events(self, client):
        sid = str(uuid.uuid4())
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid, source_id="reports"))
        client.post("/v1/context/evaluate", json=_eval_payload(session_id=sid, source_id="executive_comms"))

        body = client.get("/v1/audit/stats").json()
        assert body["total_events"] == 2
        assert body["allowed"]      == 1
        assert body["denied"]       == 1

    def test_active_sessions_counted(self, client):
        for _ in range(3):
            client.post("/v1/context/evaluate", json=_eval_payload(session_id=str(uuid.uuid4())))
        body = client.get("/v1/audit/stats").json()
        assert body["active_sessions"] == 3


# ── policies ──────────────────────────────────────────────────────────────────

class TestPolicies:
    def test_list_policies_returns_all_roles(self, client):
        r = client.get("/v1/policies")
        assert r.status_code == 200
        roles = {p["agent_role"] for p in r.json()}
        assert "analyst" in roles
        assert "viewer"  in roles
        assert "admin"   in roles

    def test_policy_fields_present(self, client):
        r = client.get("/v1/policies")
        p = r.json()[0]
        for field in ("name", "agent_role", "allowed_sources", "denied_sources", "max_sensitivity"):
            assert field in p

    def test_reload_returns_ok(self, client):
        r = client.post("/v1/policies/reload")
        assert r.status_code == 200
        body = r.json()
        assert body["status"]           == "reloaded"
        assert body["policies_loaded"]  == 3  # analyst, viewer, admin
