from __future__ import annotations

from pathlib import Path

from beaver.engine.session import SessionManager
from beaver.memory.runs import RunMemoryStore, RunRecord
from beaver.services.process_service import SessionProcessProjector
from beaver.skills.specs import SkillActivationReceipt


def test_process_projection_maps_task_team_events(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    run_store.append_run_record(
        RunRecord(
            run_id="sub-run",
            session_id="sub-session",
            task_id="task-1",
            attempt_index=1,
            task_text="sub task",
            started_at="2026-01-01T00:00:01+00:00",
            ended_at="2026-01-01T00:00:02+00:00",
            success=True,
            finish_reason="stop",
        )
    )
    run_store.append_run_record(
        RunRecord(
            run_id="main-run",
            session_id="web:test",
            task_id="task-1",
            attempt_index=1,
            task_text="main task",
            started_at="2026-01-01T00:00:03+00:00",
            ended_at="2026-01-01T00:00:04+00:00",
            success=True,
            finish_reason="stop",
        )
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_execution_planned",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "plan_mode": "team",
            "strategy": "sequence",
            "node_ids": ["research"],
            "skill_queries": ["research workflow"],
            "selected_skill_names": ["research-workflow"],
            "skill_resolution_report": [
                {
                    "node_id": "research",
                    "skill_query": "research workflow",
                    "selected_skill_names": ["research-workflow"],
                    "ephemeral_guidance_id": None,
                    "ephemeral_guidance_name": None,
                    "ephemeral_used": False,
                    "reason": "matched published skill",
                }
            ],
            "reason": "needs research",
        },
        context_visible=False,
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_team_run_completed",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "team_success": True,
            "team_run_ids": ["sub-run"],
            "node_results": [
                {
                    "node_id": "research",
                    "success": True,
                    "output_text": "evidence",
                    "run_id": "sub-run",
                    "skill_query": "research workflow",
                    "selected_skill_names": ["research-workflow"],
                    "ephemeral_skill_names": [],
                    "ephemeral_guidance_id": None,
                    "ephemeral_guidance_name": None,
                    "ephemeral_used": False,
                    "finish_reason": "stop",
                }
            ],
        },
        context_visible=False,
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_synthesis_completed",
        event_payload={"task_id": "task-1", "attempt_index": 1, "main_run_id": "main-run"},
        context_visible=False,
    )
    session.append_message(
        "web:test",
        run_id="main-run",
        role="system",
        event_type="task_evidence_recorded",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "evidence_status": "recorded",
        },
        context_visible=False,
    )
    session.append_message(
        "web:test",
        run_id="main-run",
        role="system",
        event_type="task_acceptance_recorded",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "acceptance_type": "accept",
        },
        context_visible=False,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    run_ids = {run["run_id"] for run in projection["runs"]}
    assert "task:task-1:attempt:1" in run_ids
    assert "sub-run" in run_ids
    assert "main-run" in run_ids
    sub_run = next(run for run in projection["runs"] if run["run_id"] == "sub-run")
    assert sub_run["metadata"]["selected_skill_names"] == ["research-workflow"]
    assert sub_run["metadata"]["skill_query"] == "research workflow"
    assert sub_run["metadata"]["ephemeral_guidance_id"] is None
    assert any(event["actor_name"] == "Evidence" for event in projection["events"])
    assert any(run["session_id"] == "web:test" for run in projection["runs"])

    planned_event = next(event for event in projection["events"] if event["kind"] == "task_planned")
    assert planned_event["metadata"]["timeline_type"] == "plan"
    assert planned_event["metadata"]["plan_mode"] == "team"
    assert planned_event["metadata"]["strategy"] == "sequence"
    assert planned_event["metadata"]["selected_skill_names"] == ["research-workflow"]

    skill_event = next(event for event in projection["events"] if event["kind"] == "skill_selected")
    assert skill_event["metadata"]["timeline_type"] == "skill"
    assert skill_event["metadata"]["skill_names"] == ["research-workflow"]

    team_event = next(event for event in projection["events"] if event["kind"] == "agent_team_created")
    assert team_event["metadata"]["timeline_type"] == "agent_team"
    assert team_event["metadata"]["team_run_ids"] == ["sub-run"]

    node_event = next(event for event in projection["events"] if event["kind"] == "agent_finished")
    assert node_event["metadata"]["timeline_type"] == "agent_progress"
    assert "node_result" not in node_event["metadata"]

    evidence_event = next(event for event in projection["events"] if event["kind"] == "task_result_ready")
    assert evidence_event["metadata"]["timeline_type"] == "result"
    assert evidence_event["status"] == "done"

    acceptance_event = next(event for event in projection["events"] if event["kind"] == "task_acceptance_recorded")
    assert acceptance_event["metadata"]["timeline_type"] == "acceptance"


def test_process_projection_maps_failed_task_team_events(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    run_store.append_run_record(
        RunRecord(
            run_id="failed-sub-run",
            session_id="failed-sub-session",
            task_id="task-1",
            attempt_index=1,
            task_text="failed sub task",
            started_at="2026-01-01T00:00:01+00:00",
            ended_at="2026-01-01T00:00:02+00:00",
            success=False,
            finish_reason="error",
        )
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_team_run_failed",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "team_success": False,
            "team_run_ids": ["failed-sub-run"],
            "error": "research node failed",
            "node_results": [
                {
                    "node_id": "research",
                    "success": False,
                    "error": "source unavailable",
                    "run_id": "failed-sub-run",
                    "finish_reason": "error",
                }
            ],
        },
        context_visible=False,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    team_event = next(event for event in projection["events"] if event["kind"] == "agent_team_created")
    assert team_event["status"] == "error"
    assert team_event["metadata"]["timeline_type"] == "agent_team"
    assert team_event["metadata"]["team_run_ids"] == ["failed-sub-run"]

    node_event = next(event for event in projection["events"] if event["kind"] == "agent_finished")
    assert node_event["status"] == "error"
    assert node_event["metadata"]["timeline_type"] == "agent_progress"


def test_process_projection_uses_normalized_plan_metadata_defaults(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    session.append_message(
        "web:test",
        role="system",
        event_type="task_execution_planned",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "plan_mode": None,
            "strategy": None,
        },
        context_visible=False,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    root_run = next(run for run in projection["runs"] if run["run_id"] == "task:task-1:attempt:1")
    assert root_run["metadata"]["plan_mode"] == "single"
    assert root_run["metadata"]["strategy"] == "single"
    planned_event = next(event for event in projection["events"] if event["kind"] == "task_planned")
    assert planned_event["metadata"]["plan_mode"] == "single"
    assert planned_event["metadata"]["strategy"] == "single"


def test_process_projection_emits_skill_card_from_main_run_receipts(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    run_store.append_run_record(
        RunRecord(
            run_id="main-run",
            session_id="web:test",
            task_id="task-1",
            attempt_index=1,
            task_text="main task",
            started_at="2026-01-01T00:00:03+00:00",
            ended_at="2026-01-01T00:00:04+00:00",
            success=True,
            finish_reason="stop",
            activated_skills=[
                SkillActivationReceipt(
                    run_id="main-run",
                    session_id="web:test",
                    skill_name="web-operation",
                    skill_version="1",
                    content_hash="hash",
                    activated_at="2026-01-01T00:00:03+00:00",
                    activation_reason="Needs live web lookup.",
                )
            ],
        )
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_execution_planned",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "plan_mode": "single",
            "strategy": "single",
            "selected_skill_names": [],
        },
        context_visible=False,
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_synthesis_completed",
        event_payload={"task_id": "task-1", "attempt_index": 1, "main_run_id": "main-run"},
        context_visible=False,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    skill_events = [
        event
        for event in projection["events"]
        if event["kind"] == "skill_selected" and event["run_id"] == "main-run"
    ]
    assert skill_events
    assert skill_events[0]["metadata"]["timeline_type"] == "skill"
    assert skill_events[0]["metadata"]["skill_names"] == ["web-operation"]


def test_process_projection_emits_tool_cards_from_run_messages(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    run_store.append_run_record(
        RunRecord(
            run_id="main-run",
            session_id="web:test",
            task_id="task-1",
            attempt_index=1,
            task_text="main task",
            started_at="2026-01-01T00:00:03+00:00",
            ended_at="2026-01-01T00:00:04+00:00",
            success=True,
            finish_reason="stop",
        )
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_execution_planned",
        event_payload={"task_id": "task-1", "attempt_index": 1},
        context_visible=False,
    )
    session.append_message(
        "web:test",
        run_id="main-run",
        role="assistant",
        event_type="assistant_message_added",
        event_payload={"task_id": "task-1"},
        content="Searching",
        tool_calls=[
            {
                "id": "call-1",
                "name": "multi_search",
                "arguments": {"query": "Macau cafe near Bóvia"},
            }
        ],
        context_visible=False,
    )
    session.append_message(
        "web:test",
        run_id="main-run",
        role="tool",
        event_type="tool_result_recorded",
        event_payload={"success": True, "error": None},
        content="Found 3 restaurants",
        tool_name="multi_search",
        tool_call_id="call-1",
        context_visible=True,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    tool_call = next(event for event in projection["events"] if event["kind"] == "tool_call_started")
    assert tool_call["metadata"]["timeline_type"] == "tool_call"
    assert tool_call["metadata"]["tool_name"] == "multi_search"
    assert tool_call["run_id"] == "main-run"

    tool_result = next(event for event in projection["events"] if event["kind"] == "tool_call_finished")
    assert tool_result["metadata"]["timeline_type"] == "tool_result"
    assert tool_result["metadata"]["tool_name"] == "multi_search"
    assert tool_result["metadata"]["success"] is True


def test_process_projection_exposes_ephemeral_guidance_artifacts(tmp_path: Path) -> None:
    session = SessionManager(tmp_path)
    run_store = RunMemoryStore(tmp_path / "memory" / "runs")
    run_store.append_run_record(
        RunRecord(
            run_id="sub-run",
            session_id="sub-session",
            task_id="task-1",
            attempt_index=1,
            task_text="sub task",
            started_at="2026-01-01T00:00:01+00:00",
            ended_at="2026-01-01T00:00:02+00:00",
            success=True,
            finish_reason="stop",
        )
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_execution_planned",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "plan_mode": "team",
            "strategy": "sequence",
            "node_ids": ["research"],
            "ephemeral_guidance_ids": ["eg_123"],
            "skill_resolution_report": [
                {
                    "node_id": "research",
                    "skill_query": "research workflow",
                    "selected_skill_names": [],
                    "ephemeral_guidance_id": "eg_123",
                    "ephemeral_guidance_name": "research-workflow",
                    "ephemeral_used": True,
                    "reason": "generated ephemeral guidance",
                }
            ],
        },
        context_visible=False,
    )
    session.append_message(
        "web:test",
        role="system",
        event_type="task_team_run_completed",
        event_payload={
            "task_id": "task-1",
            "attempt_index": 1,
            "team_success": True,
            "team_run_ids": ["sub-run"],
            "node_results": [
                {
                    "node_id": "research",
                    "success": True,
                    "output_text": "evidence",
                    "run_id": "sub-run",
                    "skill_query": "research workflow",
                    "selected_skill_names": [],
                    "ephemeral_skill_names": ["ephemeral:research-workflow"],
                    "ephemeral_guidance_id": "eg_123",
                    "ephemeral_guidance_name": "research-workflow",
                    "ephemeral_used": True,
                    "finish_reason": "stop",
                }
            ],
        },
        context_visible=False,
    )

    projection = SessionProcessProjector(session, run_store).project("web:test")

    sub_run = next(run for run in projection["runs"] if run["run_id"] == "sub-run")
    assert sub_run["metadata"]["ephemeral_guidance_id"] == "eg_123"
    assert projection["artifacts"][0]["artifact_id"] == "sub-run:ephemeral-guidance:eg_123"
    assert projection["artifacts"][0]["metadata"]["ephemeral_guidance_name"] == "research-workflow"
