"""Tests for the PtahBridge orchestration layer."""

import pytest

from cyberbrain26.agents.orchestrator import AgentResult
from cyberbrain26.data_plane import SQLiteDatabase
from cyberbrain26.orchestration.ptah_bridge import (
    PtahBridge,
    Roadmap,
    RoadmapStatus,
    RoadmapStep,
    StepResult,
    StepStatus,
)
from cyberbrain26.security_plane.approval import ApprovalManager
from cyberbrain26.security_plane.audit import AuditEventType, AuditQuery


def _step(step_id: str, role: str, description: str = "", **kwargs: object) -> RoadmapStep:
    return RoadmapStep(id=step_id, role=role, description=description or step_id, **kwargs)


@pytest.fixture
def bridge() -> PtahBridge:
    return PtahBridge()


class TestRoadmapManagement:
    def test_create_roadmap_returns_registered(self, bridge: PtahBridge) -> None:
        roadmap = bridge.create_roadmap("r1", "Test Roadmap")
        assert isinstance(roadmap, Roadmap)
        assert roadmap.id == "r1"
        assert roadmap.name == "Test Roadmap"
        assert roadmap.status is RoadmapStatus.PENDING
        assert bridge.get_roadmap("r1") is roadmap

    def test_get_roadmap_unknown_returns_none(self, bridge: PtahBridge) -> None:
        assert bridge.get_roadmap("missing") is None

    def test_list_roadmaps_empty(self, bridge: PtahBridge) -> None:
        assert bridge.list_roadmaps() == []

    def test_list_roadmaps_returns_all(self, bridge: PtahBridge) -> None:
        bridge.create_roadmap("r1", "A")
        bridge.create_roadmap("r2", "B")
        assert len(bridge.list_roadmaps()) == 2

    def test_create_roadmap_rejects_duplicate_step_ids(self, bridge: PtahBridge) -> None:
        steps = [
            _step("s1", "planner"),
            _step("s1", "coder"),
        ]
        with pytest.raises(ValueError, match="duplicate step id"):
            bridge.create_roadmap("r1", "Dup", steps)


class TestStepExecution:
    @pytest.mark.asyncio
    async def test_execute_step_unknown_role(self, bridge: PtahBridge) -> None:
        step = _step("s1", "ghost")
        result = await bridge.execute_step("r1", step)
        assert isinstance(result, StepResult)
        assert result.success is False
        assert "unknown role" in (result.error or "")
        assert step.status is StepStatus.FAILED
        assert step.result is result

    @pytest.mark.asyncio
    async def test_execute_step_known_role(self, bridge: PtahBridge) -> None:
        step = _step("s1", "planner", "Plan the task", payload={"action": "plan"})
        result = await bridge.execute_step("r1", step)
        assert result.success is True
        assert step.status is StepStatus.COMPLETED
        assert "plan" in result.data

    @pytest.mark.asyncio
    async def test_step_result_recorded(self, bridge: PtahBridge) -> None:
        step = _step("s1", "coder", "Write code", payload={"action": "code"})
        await bridge.execute_step("r1", step)
        recorded = bridge.step_result("s1")
        assert recorded is not None
        assert recorded.step_id == "s1"

    @pytest.mark.asyncio
    async def test_step_result_unknown_id(self, bridge: PtahBridge) -> None:
        assert bridge.step_result("nope") is None


class TestRoadmapExecution:
    @pytest.mark.asyncio
    async def test_execute_roadmap_unknown_raises(self, bridge: PtahBridge) -> None:
        with pytest.raises(KeyError):
            await bridge.execute_roadmap("missing")

    @pytest.mark.asyncio
    async def test_execute_roadmap_single_step(self, bridge: PtahBridge) -> None:
        steps = [_step("s1", "planner", "Plan", payload={"action": "plan"})]
        roadmap = bridge.create_roadmap("r1", "Single", steps)
        await bridge.execute_roadmap("r1")
        assert roadmap.status is RoadmapStatus.COMPLETED
        assert roadmap.steps[0].status is StepStatus.COMPLETED

    @pytest.mark.asyncio
    async def test_execute_roadmap_multiple_steps(self, bridge: PtahBridge) -> None:
        steps = [
            _step("s1", "planner", "Plan", payload={"action": "plan"}),
            _step("s2", "coder", "Code", payload={"action": "code"}),
            _step(
                "s3",
                "evaluator",
                "Evaluate",
                payload={"score": 0.9, "action": "evaluate"},
            ),
        ]
        roadmap = bridge.create_roadmap("r2", "Multi", steps)
        await bridge.execute_roadmap("r2")
        assert roadmap.status is RoadmapStatus.COMPLETED
        assert all(s.status is StepStatus.COMPLETED for s in roadmap.steps)

    @pytest.mark.asyncio
    async def test_execute_roadmap_with_dependencies(self, bridge: PtahBridge) -> None:
        steps = [
            _step("s1", "planner", "Plan", payload={"action": "plan"}),
            _step("s2", "coder", "Code", depends_on=["s1"], payload={"action": "code"}),
        ]
        roadmap = bridge.create_roadmap("r3", "Deps", steps)
        await bridge.execute_roadmap("r3")
        assert roadmap.status is RoadmapStatus.COMPLETED
        assert all(s.status is StepStatus.COMPLETED for s in roadmap.steps)

    @pytest.mark.asyncio
    async def test_execute_roadmap_failed_step(self, bridge: PtahBridge) -> None:
        steps = [
            _step("s1", "ghost", "Fail", payload={"action": "plan"}),
        ]
        roadmap = bridge.create_roadmap("r4", "Fail", steps)
        await bridge.execute_roadmap("r4")
        assert roadmap.status is RoadmapStatus.FAILED
        assert roadmap.steps[0].status is StepStatus.FAILED

    @pytest.mark.asyncio
    async def test_execute_roadmap_blocked_dependencies(self, bridge: PtahBridge) -> None:
        steps = [
            _step("s1", "coder", "Code", depends_on=["missing"], payload={"action": "code"}),
        ]
        roadmap = bridge.create_roadmap("r5", "Blocked", steps)
        await bridge.execute_roadmap("r5")
        # s1 is blocked because its dependency "missing" never completes.
        assert roadmap.status is RoadmapStatus.BLOCKED
        assert roadmap.steps[0].status is StepStatus.BLOCKED


class TestQueryHelpers:
    def test_roadmap_status_unknown(self, bridge: PtahBridge) -> None:
        assert bridge.roadmap_status("missing") is None

    def test_roadmap_status_pending(self, bridge: PtahBridge) -> None:
        bridge.create_roadmap("r1", "A")
        assert bridge.roadmap_status("r1") is RoadmapStatus.PENDING

    def test_agent_results_empty(self, bridge: PtahBridge) -> None:
        assert bridge.agent_results("planner") == []

    @pytest.mark.asyncio
    async def test_agent_results_populated(self, bridge: PtahBridge) -> None:
        step = _step("s1", "planner", "Plan", payload={"action": "plan"})
        await bridge.execute_step("r1", step)
        results = bridge.agent_results("planner")
        assert len(results) == 1
        assert isinstance(results[0], AgentResult)
        assert results[0].success is True


class TestSecurityPipelineIntegration:
    @pytest.mark.asyncio
    async def test_execute_step_records_audit_trail(self, bridge: PtahBridge) -> None:
        """Full pipeline: step dispatch → policy → permission → audit."""
        step = _step("s1", "planner", "Plan", payload={"action": "plan"})
        await bridge.execute_step("r1", step)

        perm_events = bridge.audit_log.query(
            AuditQuery(event_type=AuditEventType.PERMISSION_CHECK)
        )
        assert len(perm_events) >= 1
        assert perm_events[0].actor == "ptah-planner"
        assert perm_events[0].action == "plan"

    @pytest.mark.asyncio
    async def test_full_roadmap_execution_audit_coverage(
        self, bridge: PtahBridge
    ) -> None:
        """Executing a roadmap with 2 steps should produce audit events."""
        steps = [
            _step("s1", "planner", "Plan", payload={"action": "plan"}),
            _step("s2", "coder", "Code", payload={"action": "code"}),
        ]
        roadmap = bridge.create_roadmap("r1", "AuditTest", steps)
        await bridge.execute_roadmap("r1")

        perm_events = bridge.audit_log.query(
            AuditQuery(event_type=AuditEventType.PERMISSION_CHECK)
        )
        assert roadmap.status is RoadmapStatus.COMPLETED
        assert len(perm_events) >= 2


class TestShutdown:
    @pytest.mark.asyncio
    async def test_shutdown_no_error(self, bridge: PtahBridge) -> None:
        await bridge.shutdown()  # should not raise


class TestPersistentPtahBridge:
    def test_database_requires_approval_manager_to_share_store(self, tmp_path) -> None:
        database = SQLiteDatabase(tmp_path / "ptah.sqlite3")
        with pytest.raises(ValueError, match="must share the same SQLite database"):
            PtahBridge(database=database, approval_manager=ApprovalManager())

    @pytest.mark.asyncio
    async def test_completed_step_is_restored_without_second_dispatch(self, tmp_path) -> None:
        database = SQLiteDatabase(tmp_path / "ptah.sqlite3")
        first = PtahBridge(database=database)
        step = _step("persistent-step", "planner", "Plan", payload={"action": "plan"})
        result = await first.execute_step("persistent-roadmap", step)
        assert result.success is True

        task_events = first.audit_log.query(AuditQuery(task_id="persistent-step"))
        assert task_events

        reopened = PtahBridge(database=SQLiteDatabase(tmp_path / "ptah.sqlite3"))
        restored_step = _step("persistent-step", "planner", "Plan", payload={"action": "plan"})
        restored = await reopened.execute_step("persistent-roadmap", restored_step)
        assert restored.success is True
        assert restored.data == result.data
        job = reopened.queue.get_job("ptah:persistent-roadmap:persistent-step")
        assert job is not None and job.status.value == "completed"

    @pytest.mark.asyncio
    async def test_interrupted_step_requires_explicit_retry(self, tmp_path) -> None:
        database = SQLiteDatabase(tmp_path / "ptah.sqlite3")
        bridge = PtahBridge(database=database)
        step = _step("interrupted-step", "planner", "Plan", payload={"action": "plan"})
        durable_id = "ptah:interrupted-roadmap:interrupted-step"
        bridge.queue.enqueue_job(
            "ptah.dispatch_step",
            {
                "task_id": step.id,
                "agent_id": "ptah-planner",
                "payload": {"task_id": step.id, "roadmap_id": "interrupted-roadmap"},
            },
            task_id=durable_id,
        )
        with database.transaction() as connection:
            connection.execute(
                "UPDATE tasks SET status = 'running' WHERE task_id = ?", (durable_id,)
            )

        reopened = PtahBridge(database=SQLiteDatabase(tmp_path / "ptah.sqlite3"))
        result = await reopened.execute_step("interrupted-roadmap", step)
        assert result.success is False
        assert "explicitly retrying" in (result.error or "")
        job = reopened.queue.get_job(durable_id)
        assert job is not None and job.status.value == "interrupted"
