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_readback as consumer_module,
)
from src.services.ai_agent_log_controlled_writeback_consumer_readback import (
    load_latest_ai_agent_log_controlled_writeback_consumer_readback,
)


class _FakeMappingResult:
    def __init__(self, rows: list[dict]):
        self._rows = rows

    def mappings(self):
        return self

    def all(self) -> list[dict]:
        return self._rows


class _FakeDb:
    def __init__(self, rows: list[dict], consumer_rows: list[dict] | None = None):
        self.rows = rows
        self.consumer_rows = consumer_rows or []
        self.params: list[dict] = []

    async def execute(self, _statement, params: dict):
        if not params:
            return _FakeMappingResult([])
        self.params.append(params)
        if params.get("operation_type") == "log_controlled_writeback_consumed":
            return _FakeMappingResult(self.consumer_rows)
        return _FakeMappingResult(self.rows)


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


@pytest.fixture(autouse=True)
def _stub_telegram_alert_card_readback(monkeypatch):
    async def fake_alert_card_readback(**_kwargs):
        return _telegram_alert_card_readback()

    monkeypatch.setattr(
        consumer_module,
        "list_ai_alert_card_delivery_readback",
        fake_alert_card_readback,
    )


def _ledger_rows() -> list[dict]:
    return [
        {
            "op_id": f"op-{target}",
            "operation_type": "km_linked",
            "actor": "ai_agent_metadata_writeback_executor",
            "status": "success",
            "created_at": "2026-06-30T01:40:00+08:00",
            "semantic_operation_type": "log_controlled_writeback_dispatched",
            "ledger_operation_type": "km_linked",
            "dispatch_receipt_id": f"log_controlled_writeback_dispatched::{target}",
            "batch_id": f"log-feedback-writeback::{target}",
            "target": target,
            "target_surface": f"{target}_metadata_feedback_binding",
            "risk_tier": "medium" if target in {"km", "rag", "playbook"} else "low",
            "project_id": "awoooi",
            "raw_payload_included": "false",
            "next_action": "consume_metadata_receipt_in_km_rag_playbook_agent_context",
            "km_write_performed": "false",
            "rag_index_write_performed": "false",
            "playbook_trust_write_performed": "false",
            "mcp_tool_call_performed": "false",
            "telegram_send_performed": "false",
            "post_apply_verifier_refs": [
                f"post-write-verifier://ai-agent-log-feedback/{target}/sample-a",
                f"post-write-verifier://ai-agent-log-feedback/{target}/sample-b",
                f"post-write-verifier://ai-agent-log-feedback/{target}/p0-110-harbor",
            ],
        }
        for target in ("km", "rag", "playbook", "mcp", "verifier", "ai_agent")
    ]


def _consumer_receipt_rows() -> list[dict]:
    return [
        {
            "op_id": f"consumer-op-{target}",
            "operation_type": "km_linked",
            "actor": "ai_agent_metadata_writeback_consumer",
            "status": "success",
            "created_at": "2026-06-30T01:45:00+08:00",
            "semantic_operation_type": "log_controlled_writeback_consumed",
            "ledger_operation_type": "km_linked",
            "consumer_receipt_id": f"log_controlled_writeback_consumed::{target}",
            "source_dispatch_receipt_id": f"log_controlled_writeback_dispatched::{target}",
            "target": target,
            "target_surface": f"{target}_metadata_feedback_binding",
            "consumer_surface": f"{target}_consumer_context",
            "runtime_target_write_performed": "true",
            "raw_payload_included": "false",
            "consumer_context_receipt_recorded": "true",
            "target_context_receipt_write_performed": "true",
            "next_action": "readback_consumer_context_receipts",
            "post_apply_verifier_refs": [f"post-write-verifier://{target}"],
        }
        for target in ("km", "rag", "playbook", "mcp", "verifier", "ai_agent")
    ]


def _telegram_alert_card_readback() -> dict:
    return {
        "summary": {
            "project_id": "awoooi",
            "learning_registry_target_count": 6,
            "learning_registry_ready_target_count": 6,
            "learning_registry_missing_target_count": 0,
            "learning_registry_next_action": (
                "consume_telegram_alert_learning_registry_in_ai_loop_agent_context"
            ),
        },
        "items": [
            {
                "message_id": "msg-telegram-1",
                "run_id": "run-telegram-1",
                "project_id": "awoooi",
                "event_type": "wazuh_dashboard_api_readback_degraded",
                "lane": "siem_observability_readback_degraded",
                "learning_writeback_refs": {
                    "receipt_id": "msg-telegram-1",
                    "run_id": "run-telegram-1",
                    "work_item_id": "CIR-P0-TG-001",
                    "post_verifier_ref": (
                        "verifier://awoooi/telegram-alert-delivery-receipt/"
                        "run-telegram-1"
                    ),
                    "ai_agent_context_ref": (
                        "ai-agent://awoooi/telegram-alert-learning-context/"
                        "run-telegram-1/msg-telegram-1"
                    ),
                },
                "learning_registry_bindings": [
                    {
                        "target": target,
                        "status": "ready_for_consumer_context",
                        "consumer_surface": surface,
                        "ref": f"{target}://awoooi/telegram-alert/msg-telegram-1",
                        "work_item_id": "CIR-P0-TG-001",
                        "target_write_performed": False,
                        "raw_payload_included": False,
                    }
                    for target, surface in [
                        ("km", "knowledge_memory_context"),
                        ("rag", "rag_index_candidate_context"),
                        ("playbook", "playbook_trust_candidate_context"),
                        ("mcp", "mcp_audit_feedback_context"),
                        ("verifier", "post_apply_verifier_feedback_context"),
                        ("ai_agent", "autonomous_runtime_decision_context"),
                    ]
                ],
            }
        ],
        "learning_registry": {
            "schema_version": "ai_alert_card_learning_registry_readback_v1",
            "status": "learning_registry_ready",
            "project_id": "awoooi",
            "target_count": 6,
            "ready_target_count": 6,
            "missing_target_count": 0,
            "ready_receipt_ref_count": 6,
            "consumer_context_route": "ai_loop_agent.telegram_alert_learning_context",
        },
    }


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

    payload = await load_latest_ai_agent_log_controlled_writeback_consumer_readback()

    _assert_consumer_readback(payload)
    assert fake_db.params == [
        {"operation_type": "log_controlled_writeback_dispatched"},
        {"operation_type": "log_controlled_writeback_consumed"},
    ]


@pytest.mark.asyncio
async def test_log_controlled_writeback_consumer_loader_reads_apply_receipts(monkeypatch):
    fake_db = _FakeDb(_ledger_rows(), _consumer_receipt_rows())
    monkeypatch.setattr(
        consumer_module,
        "get_db_context",
        lambda project_id: _FakeContext(fake_db),
    )

    payload = await load_latest_ai_agent_log_controlled_writeback_consumer_readback()

    assert payload["controlled_consume"]["runtime_target_write_performed"] is True
    assert payload["rollups"]["consumer_apply_receipt_row_count"] == 6
    assert payload["rollups"]["target_context_receipt_write_count"] == 6
    assert payload["rollups"]["runtime_target_write_performed"] is True
    assert payload["operation_boundaries"]["runtime_target_write_performed"] is True
    for binding in payload["consumer_bindings"]:
        assert binding["target_write_performed"] is True
        assert binding["target_write_receipt"]["runtime_target_write_performed"] is True


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

    payload = await load_latest_ai_agent_log_controlled_writeback_consumer_readback()

    assert payload["status"] == (
        "blocked_waiting_controlled_writeback_consumer_db_readback"
    )
    assert payload["readback"]["db_read_status"] == "unavailable"
    assert payload["readback"]["error_type"] == "TimeoutError"
    assert payload["controlled_consume"]["controlled_consume_allowed"] is False
    assert payload["rollups"]["controlled_consumer_readback_ready"] is False
    assert payload["active_blockers"] == [
        "log_controlled_writeback_consumer_readback_db_unavailable"
    ]
    assert payload["operation_boundaries"]["metadata_ledger_read_performed"] is False
    assert payload["operation_boundaries"]["runtime_target_write_performed"] is False


def test_log_controlled_writeback_consumer_endpoint_returns_readback(monkeypatch):
    async def fake_loader():
        fake_db = _FakeDb(_ledger_rows())
        monkeypatch.setattr(
            consumer_module,
            "get_db_context",
            lambda project_id: _FakeContext(fake_db),
        )
        return await load_latest_ai_agent_log_controlled_writeback_consumer_readback()

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

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

    assert response.status_code == 200
    _assert_consumer_readback(response.json())


def _assert_consumer_readback(payload: dict):
    assert (
        payload["schema_version"]
        == "ai_agent_log_controlled_writeback_consumer_readback_v1"
    )
    assert payload["priority"] == "P1-LOG-KM-RAG-MCP-PLAYBOOK"
    assert payload["status"] == "controlled_writeback_consumer_readback_ready"
    assert payload["active_blockers"] == []
    assert payload["readback"]["source_operation_type"] == (
        "log_controlled_writeback_dispatched"
    )
    assert payload["readback"]["source_executor_route"] == (
        "ai_agent_metadata_writeback_executor"
    )
    assert payload["controlled_consume"]["controlled_consume_allowed"] is True
    assert payload["controlled_consume"]["runtime_target_write_performed"] is False
    assert payload["rollups"]["dispatch_ledger_row_count"] == 6
    assert payload["rollups"]["consumer_binding_count"] == 6
    assert payload["rollups"]["ready_consumer_binding_count"] == 6
    assert payload["rollups"]["ready_target_count"] == 6
    assert payload["rollups"]["metadata_only_receipt_count"] == 6
    assert payload["rollups"]["post_apply_verifier_ref_count"] == 18
    assert payload["rollups"]["controlled_consumer_readback_ready"] is True
    assert payload["rollups"]["km_consumer_binding_count"] == 1
    assert payload["rollups"]["rag_consumer_binding_count"] == 1
    assert payload["rollups"]["playbook_consumer_binding_count"] == 1
    assert payload["rollups"]["mcp_consumer_binding_count"] == 1
    assert payload["rollups"]["verifier_consumer_binding_count"] == 1
    assert payload["rollups"]["ai_agent_consumer_binding_count"] == 1
    assert payload["rollups"]["telegram_alert_learning_context_readback_ready"] is True
    assert payload["rollups"]["telegram_alert_learning_context_receipt_count"] == 6
    assert (
        payload["rollups"]["telegram_alert_learning_ai_agent_context_receipt_count"]
        == 1
    )
    assert payload["rollups"]["telegram_alert_learning_ready_target_count"] == 6

    telegram_context = payload["telegram_alert_learning_context"]
    assert telegram_context["schema_version"] == (
        "telegram_alert_ai_loop_consumer_context_readback_v1"
    )
    assert telegram_context["status"] == "ai_loop_agent_context_receipt_ready"
    assert telegram_context["consumer_context_route"] == (
        "ai_loop_agent.telegram_alert_learning_context"
    )
    assert telegram_context["next_action"] == (
        "verify_telegram_alert_learning_context_receipts_with_ai_loop_post_apply_verifier"
    )
    assert telegram_context["context_receipt_count"] == 6
    assert telegram_context["ai_agent_context_receipt_count"] == 1
    ai_agent_receipt = [
        receipt
        for receipt in telegram_context["context_receipts"]
        if receipt["target"] == "ai_agent"
    ][0]
    assert ai_agent_receipt["status"] == "ready_for_ai_loop_context"
    assert ai_agent_receipt["work_item_id"] == "CIR-P0-TG-001"
    assert ai_agent_receipt["metadata_only"] is True
    assert ai_agent_receipt["target_write_performed"] is False
    assert ai_agent_receipt["check_mode"]["enabled"] is True
    assert ai_agent_receipt["rollback"]["required"] is True

    target_rollups = {item["target"]: item for item in payload["target_rollups"]}
    assert set(target_rollups) == {"km", "rag", "playbook", "mcp", "verifier", "ai_agent"}
    for rollup in target_rollups.values():
        assert rollup["consumer_surface"]
        assert rollup["binding_count"] == 1
        assert rollup["ready_binding_count"] == 1
        assert rollup["metadata_only"] is True

    bindings = payload["consumer_bindings"]
    assert {binding["target"] for binding in bindings} == set(target_rollups)
    for binding in bindings:
        assert binding["status"] == "ready_for_consumer_context"
        assert binding["executor_route"] == "ai_agent_metadata_writeback_executor"
        assert binding["semantic_operation_type"] == "log_controlled_writeback_dispatched"
        assert binding["ledger_status"] == "success"
        assert binding["raw_payload_included"] is False
        assert binding["target_write_performed"] is False
        assert binding["check_mode"]["enabled"] is True
        assert binding["rollback"]["required"] is True
        assert binding["post_apply_verifier"]["required"] is True
        assert binding["post_apply_verifier"]["verifier_refs"]
        assert binding["source_of_truth_diff"]["raw_payload_included"] is False

    boundaries = payload["operation_boundaries"]
    assert boundaries["consumer_readback_only"] is True
    assert boundaries["metadata_ledger_read_performed"] is True
    assert boundaries["telegram_alert_registry_metadata_read_performed"] is True
    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["agent_runtime_action_performed"] is False
    assert boundaries["telegram_send_performed"] is False
    assert boundaries["workflow_trigger_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
