from __future__ import annotations

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

from src.api.v1 import agents
from src.api.v1.agents import router
from src.services import (
    ai_agent_log_controlled_writeback_consumer_apply as apply_module,
)
from src.services.ai_agent_log_controlled_writeback_consumer_apply import (
    build_ai_agent_log_controlled_writeback_consumer_apply_receipt,
    consume_latest_ai_agent_log_controlled_writeback,
)


class _FakeResult:
    def __init__(self, value: str | None):
        self.value = value

    def scalar(self) -> str | None:
        return self.value


class _FakeDb:
    def __init__(self):
        self.params: list[dict] = []

    async def execute(self, statement, params: dict):
        if not params:
            return _FakeResult(None)
        self.params.append(params)
        if "fallback_operation_type" in params:
            return _FakeResult("km_linked")
        if "input" in params:
            return _FakeResult(
                f"consumer-op-{params['consumer_receipt_id'].rsplit('::', 1)[-1]}"
            )
        assert "pg_get_constraintdef" not in str(statement)
        return _FakeResult("existing-consumer-op")


class _FakeContext:
    def __init__(self, db: _FakeDb):
        self.db = db

    async def __aenter__(self) -> _FakeDb:
        return self.db

    async def __aexit__(self, _exc_type, _exc, _tb) -> bool:
        return False


class _FailingContext:
    async def __aenter__(self):
        raise TimeoutError("pool checkout unavailable")

    async def __aexit__(self, _exc_type, _exc, _tb) -> bool:
        return False


def _consumer_readback() -> dict:
    targets = ("km", "rag", "playbook", "mcp", "verifier", "ai_agent")
    bindings = [
        {
            "consumer_binding_id": f"consumer::log_controlled_writeback_dispatched::{target}",
            "dispatch_receipt_id": f"log_controlled_writeback_dispatched::{target}",
            "ledger_op_id": f"op-{target}",
            "target": target,
            "target_surface": f"{target}_metadata_feedback_binding",
            "consumer_surface": f"{target}_consumer_context",
            "risk_tier": "medium" if target in {"km", "rag", "playbook"} else "low",
            "status": "ready_for_consumer_context",
            "raw_payload_included": False,
            "post_apply_verifier_refs": [f"post-write-verifier://{target}/sample"],
            "post_apply_verifier": {
                "required": True,
                "verifier_refs": [f"post-write-verifier://{target}/sample"],
            },
            "target_write_performed": False,
        }
        for target in targets
    ]
    return {
        "schema_version": "ai_agent_log_controlled_writeback_consumer_readback_v1",
        "priority": "P1-LOG-KM-RAG-MCP-PLAYBOOK",
        "scope": "ai_agent_log_controlled_writeback_consumer_readback",
        "status": "controlled_writeback_consumer_readback_ready",
        "readback": {
            "workplan_id": "P1-LOG-CONTROLLED-WRITEBACK-CONSUMER-READBACK",
            "source_operation_type": "log_controlled_writeback_dispatched",
            "source_executor_route": "ai_agent_metadata_writeback_executor",
        },
        "controlled_consume": {
            "controlled_consume_allowed": True,
            "runtime_target_write_performed": False,
        },
        "consumer_bindings": bindings,
        "rollups": {
            "controlled_consumer_readback_ready": True,
            "consumer_binding_count": 6,
        },
        "active_blockers": [],
        "operation_boundaries": {
            "consumer_readback_only": True,
            "metadata_ledger_read_performed": True,
            "raw_log_payload_persisted": False,
            "secret_value_collection_allowed": False,
            "github_api_used": False,
        },
    }


def test_log_controlled_writeback_consumer_apply_builder_creates_receipts():
    payload = build_ai_agent_log_controlled_writeback_consumer_apply_receipt(
        _consumer_readback()
    )

    assert payload["schema_version"] == (
        "ai_agent_log_controlled_writeback_consumer_apply_receipt_v1"
    )
    assert payload["status"] == "controlled_writeback_consumer_apply_ready"
    assert payload["active_blockers"] == []
    assert payload["readback"]["operation_type"] == "log_controlled_writeback_consumed"
    assert payload["rollups"]["consumer_apply_receipt_count"] == 6
    assert payload["rollups"]["ready_consumer_apply_receipt_count"] == 6
    assert payload["rollups"]["runtime_target_write_performed"] is False
    assert {item["target"] for item in payload["consumer_apply_receipts"]} == {
        "km",
        "rag",
        "playbook",
        "mcp",
        "verifier",
        "ai_agent",
    }
    for item in payload["consumer_apply_receipts"]:
        assert item["status"] == "ready_for_context_receipt_write"
        assert item["raw_payload_included"] is False
        assert item["check_mode"]["enabled"] is True
        assert item["rollback"]["required"] is True

    boundaries = payload["operation_boundaries"]
    assert boundaries["metadata_ledger_write_only"] is True
    assert boundaries["consumer_context_receipt_write_performed"] is False
    assert boundaries["km_write_performed"] is False
    assert boundaries["rag_index_write_performed"] is False
    assert boundaries["playbook_trust_write_performed"] is False
    assert boundaries["mcp_tool_call_performed"] is False
    assert boundaries["raw_log_payload_persisted"] is False
    assert boundaries["secret_value_collection_allowed"] is False
    assert boundaries["github_api_used"] is False


@pytest.mark.asyncio
async def test_log_controlled_writeback_consumer_apply_writes_idempotent_rows(monkeypatch):
    fake_db = _FakeDb()
    monkeypatch.setattr(
        apply_module,
        "get_db_context",
        lambda project_id: _FakeContext(fake_db),
    )

    async def fake_readback(*, project_id: str):
        assert project_id == "awoooi"
        return _consumer_readback()

    monkeypatch.setattr(
        apply_module,
        "load_latest_ai_agent_log_controlled_writeback_consumer_readback",
        fake_readback,
    )

    payload = await consume_latest_ai_agent_log_controlled_writeback(project_id="awoooi")

    assert payload["status"] == "controlled_writeback_consumer_apply_ready"
    assert payload["rollups"]["runtime_target_write_performed"] is True
    assert payload["rollups"]["consumer_apply_receipt_row_count"] == 6
    assert payload["rollups"]["inserted_consumer_apply_receipt_row_count"] == 6
    assert payload["operation_boundaries"]["consumer_context_receipt_write_performed"] is True
    assert payload["operation_boundaries"]["runtime_target_write_performed"] is True
    assert payload["apply_result"]["operation_type"] == "log_controlled_writeback_consumed"
    assert payload["apply_result"]["ledger_operation_type"] == "km_linked"

    write_params = [params for params in fake_db.params if "input" in params]
    assert len(write_params) == 6
    for params in write_params:
        assert params["operation_type"] == "log_controlled_writeback_consumed"
        assert params["ledger_operation_type"] == "km_linked"
        assert params["actor"] == "ai_agent_metadata_writeback_consumer"
        assert '"semantic_operation_type": "log_controlled_writeback_consumed"' in params["input"]
        assert '"runtime_target_write_performed": true' in params["input"]
        assert '"raw_payload_included": false' in params["input"]
        assert '"target_context_receipt_write_performed": true' in params["output"]
        assert '"km_write_performed": false' in params["output"]


@pytest.mark.asyncio
async def test_log_controlled_writeback_consumer_apply_blocks_when_readback_degraded(
    monkeypatch,
):
    degraded_readback = _consumer_readback()
    degraded_readback["status"] = (
        "blocked_waiting_controlled_writeback_consumer_db_readback"
    )
    degraded_readback["controlled_consume"]["controlled_consume_allowed"] = False
    degraded_readback["active_blockers"] = [
        "log_controlled_writeback_consumer_readback_db_unavailable"
    ]

    async def fake_readback(*, project_id: str):
        assert project_id == "awoooi"
        return degraded_readback

    monkeypatch.setattr(
        apply_module,
        "load_latest_ai_agent_log_controlled_writeback_consumer_readback",
        fake_readback,
    )

    payload = await consume_latest_ai_agent_log_controlled_writeback(project_id="awoooi")

    assert payload["status"] == (
        "blocked_waiting_controlled_writeback_consumer_apply_inputs"
    )
    assert "controlled_writeback_consumer_readback_not_ready" in payload[
        "active_blockers"
    ]
    assert payload["apply_result"]["db_write_status"] == "not_attempted"
    assert payload["operation_boundaries"]["runtime_target_write_performed"] is False


@pytest.mark.asyncio
async def test_log_controlled_writeback_consumer_apply_degrades_on_db_write_pressure(
    monkeypatch,
):
    monkeypatch.setattr(
        apply_module,
        "get_db_context",
        lambda project_id: _FailingContext(),
    )

    async def fake_readback(*, project_id: str):
        assert project_id == "awoooi"
        return _consumer_readback()

    monkeypatch.setattr(
        apply_module,
        "load_latest_ai_agent_log_controlled_writeback_consumer_readback",
        fake_readback,
    )

    payload = await consume_latest_ai_agent_log_controlled_writeback(project_id="awoooi")

    assert payload["status"] == "blocked_waiting_controlled_writeback_consumer_apply_db"
    assert "consumer_apply_db_write_unavailable" in payload["active_blockers"]
    assert payload["apply_result"]["db_write_status"] == "unavailable"
    assert payload["apply_result"]["error_type"] == "TimeoutError"
    assert payload["operation_boundaries"]["consumer_context_receipt_write_performed"] is False
    assert payload["operation_boundaries"]["runtime_target_write_performed"] is False


def test_log_controlled_writeback_consumer_apply_endpoint_returns_receipt(monkeypatch):
    async def fake_apply():
        payload = build_ai_agent_log_controlled_writeback_consumer_apply_receipt(
            _consumer_readback()
        )
        payload["rollups"]["runtime_target_write_performed"] = True
        payload["operation_boundaries"]["runtime_target_write_performed"] = True
        return payload

    monkeypatch.setattr(
        agents,
        "consume_latest_ai_agent_log_controlled_writeback",
        fake_apply,
    )
    app = FastAPI()
    app.include_router(router, prefix="/api/v1")
    client = TestClient(app)

    response = client.post(
        "/api/v1/agents/agent-log-controlled-writeback-consumer-apply"
    )

    assert response.status_code == 200
    payload = response.json()
    assert payload["schema_version"] == (
        "ai_agent_log_controlled_writeback_consumer_apply_receipt_v1"
    )
    assert payload["status"] == "controlled_writeback_consumer_apply_ready"
    assert payload["rollups"]["runtime_target_write_performed"] is True
