"""
Tests for the Policy Editor feature — CRUD + version history via API.
"""

import pytest
from fastapi.testclient import TestClient

from autopil.api.app import create_app


# ── fixtures ──────────────────────────────────────────────────────────────────

@pytest.fixture
def client(policy_file, audit_db, api_key) -> TestClient:
    app = create_app(policy_path=policy_file, audit_db=audit_db)
    return TestClient(app, headers={"X-API-Key": api_key})


POLICY_PAYLOAD = {
    "name": "test_policy",
    "agent_role": "tester",
    "allowed_sources": ["source_a", "source_b"],
    "denied_sources": ["source_x"],
    "max_sensitivity": "medium",
}


# ── create ────────────────────────────────────────────────────────────────────

def test_create_policy_returns_detail(client):
    r = client.post("/v1/policies", json=POLICY_PAYLOAD)
    assert r.status_code == 200
    data = r.json()
    assert data["name"] == "test_policy"
    assert data["agent_role"] == "tester"
    assert data["allowed_sources"] == ["source_a", "source_b"]
    assert data["denied_sources"] == ["source_x"]
    assert data["max_sensitivity"] == "medium"
    assert data["version"] == 1
    assert data["policy_id"].startswith("pol_")
    assert data["created_at"]
    assert data["updated_at"]


def test_create_duplicate_returns_409(client):
    client.post("/v1/policies", json=POLICY_PAYLOAD)
    r = client.post("/v1/policies", json=POLICY_PAYLOAD)
    assert r.status_code == 409
    assert "already exists" in r.json()["detail"]


# ── update ────────────────────────────────────────────────────────────────────

def test_update_policy_increments_version(client):
    client.post("/v1/policies", json=POLICY_PAYLOAD)
    update = {
        "agent_role": "tester",
        "allowed_sources": ["source_a"],
        "denied_sources": ["source_x", "source_y"],
        "max_sensitivity": "high",
    }
    r = client.put("/v1/policies/test_policy", json=update)
    assert r.status_code == 200
    data = r.json()
    assert data["version"] == 2
    assert data["max_sensitivity"] == "high"
    assert "source_y" in data["denied_sources"]


def test_update_nonexistent_policy_returns_404(client):
    update = {
        "agent_role": "tester",
        "allowed_sources": [],
        "denied_sources": [],
        "max_sensitivity": "high",
    }
    r = client.put("/v1/policies/nonexistent_policy", json=update)
    assert r.status_code == 404


# ── history ───────────────────────────────────────────────────────────────────

def test_version_history_grows_with_updates(client):
    client.post("/v1/policies", json=POLICY_PAYLOAD)

    # After create: version 1 exists in history
    r = client.get("/v1/policies/test_policy/history")
    assert r.status_code == 200
    history = r.json()
    assert len(history) == 1
    assert history[0]["version"] == 1

    # First update → creates version 2, snapshots v1
    update = {
        "agent_role": "tester",
        "allowed_sources": ["source_a"],
        "denied_sources": [],
        "max_sensitivity": "high",
    }
    client.put("/v1/policies/test_policy", json=update)

    r = client.get("/v1/policies/test_policy/history")
    assert r.status_code == 200
    history = r.json()
    assert len(history) == 2
    # Newest first
    assert history[0]["version"] == 2
    assert history[1]["version"] == 1

    # Second update → three entries in history
    client.put("/v1/policies/test_policy", json=update)
    r = client.get("/v1/policies/test_policy/history")
    assert len(r.json()) == 3


def test_history_not_found_returns_404(client):
    r = client.get("/v1/policies/no_such_policy/history")
    assert r.status_code == 404


# ── delete ────────────────────────────────────────────────────────────────────

def test_delete_removes_policy_from_list(client):
    client.post("/v1/policies", json=POLICY_PAYLOAD)

    r = client.delete("/v1/policies/test_policy")
    assert r.status_code == 200
    assert r.json()["status"] == "deleted"

    r = client.get("/v1/policies")
    policies = r.json()
    names = [p["name"] for p in policies]
    assert "test_policy" not in names


def test_delete_unknown_policy_returns_404(client):
    r = client.delete("/v1/policies/does_not_exist")
    assert r.status_code == 404


# ── list ──────────────────────────────────────────────────────────────────────

def test_list_policies_returns_db_policies_when_available(client):
    client.post("/v1/policies", json=POLICY_PAYLOAD)
    r = client.get("/v1/policies")
    assert r.status_code == 200
    policies = r.json()
    names = [p["name"] for p in policies]
    assert "test_policy" in names
    # DB policies have real policy_id and version > 0
    test_p = next(p for p in policies if p["name"] == "test_policy")
    assert test_p["policy_id"].startswith("pol_")
    assert test_p["version"] == 1


def test_list_policies_falls_back_to_yaml_when_no_db_policies(client):
    # No DB policies created — should fall back to YAML
    r = client.get("/v1/policies")
    assert r.status_code == 200
    policies = r.json()
    # YAML has analyst_policy, viewer_policy, admin_policy
    names = [p["name"] for p in policies]
    assert "analyst_policy" in names
    # YAML fallback uses synthetic metadata
    for p in policies:
        assert p["policy_id"] == ""
        assert p["version"] == 0


# ── restore via PUT ───────────────────────────────────────────────────────────

def test_restore_old_version_via_put(client):
    """Simulate restoring an old version by PUT-ing with old version's values."""
    client.post("/v1/policies", json=POLICY_PAYLOAD)

    # Update to a new state
    update_v2 = {
        "agent_role": "tester",
        "allowed_sources": [],
        "denied_sources": ["source_x", "source_y"],
        "max_sensitivity": "restricted",
    }
    client.put("/v1/policies/test_policy", json=update_v2)

    # Restore original values
    restore = {
        "agent_role": POLICY_PAYLOAD["agent_role"],
        "allowed_sources": POLICY_PAYLOAD["allowed_sources"],
        "denied_sources": POLICY_PAYLOAD["denied_sources"],
        "max_sensitivity": POLICY_PAYLOAD["max_sensitivity"],
    }
    r = client.put("/v1/policies/test_policy", json=restore)
    assert r.status_code == 200
    data = r.json()
    assert data["version"] == 3
    assert data["allowed_sources"] == ["source_a", "source_b"]
    assert data["max_sensitivity"] == "medium"


# ── reload imports into DB ────────────────────────────────────────────────────

def test_reload_imports_yaml_policies_into_db(client):
    r = client.post("/v1/policies/reload")
    assert r.status_code == 200
    data = r.json()
    assert data["status"] == "reloaded"
    assert data["policies_loaded"] > 0

    # Now DB has policies
    r = client.get("/v1/policies")
    policies = r.json()
    # They should have real policy_ids now
    for p in policies:
        assert p["policy_id"].startswith("pol_")
        assert p["version"] >= 1
