"""Tests for src/router.py planning integration (W2 — state-aware routing,
start_planning_session, planning_approve, planning_cancel)."""

from __future__ import annotations

import json
from pathlib import Path
from unittest.mock import patch

import pytest

from src import planning_session, planning_orchestrator, router
from src.planning_session import _channel_key, _save_planning_state


@pytest.fixture
def tmp_state(tmp_path, monkeypatch):
    """Redirect planning + approved-tasks into tmp."""
    sessions_dir = tmp_path / "sessions"
    sessions_dir.mkdir()
    monkeypatch.setattr(planning_session, "SESSIONS_DIR", sessions_dir)
    monkeypatch.setattr(
        planning_session, "PLANNING_STATE_FILE", sessions_dir / "planning.json"
    )

    # approved-tasks.json — point router at a tmp file
    approved = tmp_path / "approved-tasks.json"
    approved.write_text(json.dumps({"projects": [], "last_updated": None}))
    monkeypatch.setattr(router, "APPROVED_TASKS_FILE", approved)

    # workspace dir for planning orchestrator final-plan.md target
    workspace = tmp_path / "workspace"
    workspace.mkdir()
    (workspace / "demo").mkdir()
    monkeypatch.setattr(planning_session, "WORKSPACE_ROOT", workspace)
    monkeypatch.setattr(planning_orchestrator, "WORKSPACE_ROOT", workspace)
    yield {"sessions": sessions_dir, "approved": approved, "workspace": workspace}


def _fake_result(session_id="s-1", text="hi"):
    return {
        "result": text,
        "session_id": session_id,
        "usage": {"input_tokens": 10, "output_tokens": 5},
        "total_cost_usd": 0.1,
        "subtype": "success",
        "is_error": False,
    }


# ---------------------------------------------------------------------------
# start_planning_session
# ---------------------------------------------------------------------------


class TestStartPlanningSession:
    def test_creates_entry_and_sets_status_planning(self, tmp_state):
        with patch(
            "src.planning_session._run_claude",
            return_value=_fake_result(session_id="s-1", text="first"),
        ):
            response = router.start_planning_session(
                "demo", "Add filter X", "ch-1", "discord"
            )
        assert response == "first"
        approved = json.loads(tmp_state["approved"].read_text())
        assert len(approved["projects"]) == 1
        entry = approved["projects"][0]
        assert entry["name"] == "demo"
        assert entry["status"] == "planning"
        assert entry["planning_session_id"]  # uuid

    def test_promotes_existing_pending_entry(self, tmp_state):
        # Pre-seed an existing pending entry
        approved_data = {
            "projects": [
                {
                    "name": "demo",
                    "description": "from earlier",
                    "status": "pending",
                    "planning_session_id": None,
                    "final_plan_path": None,
                    "proposed_at": "2026-04-26T18:00:00+00:00",
                    "approved_at": None,
                    "started_at": None,
                    "pid": None,
                }
            ],
            "last_updated": None,
        }
        tmp_state["approved"].write_text(json.dumps(approved_data))

        with patch(
            "src.planning_session._run_claude",
            return_value=_fake_result(session_id="s-2", text="hi"),
        ):
            router.start_planning_session(
                "demo", "Add filter X", "ch-1", "discord"
            )
        approved = json.loads(tmp_state["approved"].read_text())
        assert len(approved["projects"]) == 1
        assert approved["projects"][0]["status"] == "planning"


# ---------------------------------------------------------------------------
# planning_approve
# ---------------------------------------------------------------------------


class TestPlanningApprove:
    def test_promotes_status_and_clears_state(self, tmp_state):
        # Seed an active planning state + approved-tasks pending entry
        _save_planning_state({
            _channel_key("discord", "ch-1"): {
                "slug": "demo",
                "description": "x",
                "phase": "__complete__",
                "phases_planned": ["/office-hours"],
                "phases_completed": ["/office-hours"],
                "phase_index": 1,
                "session_id": "s-uuid",
                "planning_session_id": "ps-uuid",
                "final_plan_path": "/tmp/final-plan.md",
                "adapter": "discord",
                "channel_id": "ch-1",
                "started_at": "2026-04-26T20:00:00+00:00",
                "updated_at": "2026-04-26T20:30:00+00:00",
                "last_text_excerpt": "done",
                "last_subtype": "success",
            }
        })
        approved_data = {
            "projects": [
                {
                    "name": "demo",
                    "description": "x",
                    "status": "planning",
                    "planning_session_id": "ps-uuid",
                    "final_plan_path": None,
                    "proposed_at": "2026-04-26T18:00:00+00:00",
                    "approved_at": None,
                    "started_at": None,
                    "pid": None,
                }
            ],
            "last_updated": None,
        }
        tmp_state["approved"].write_text(json.dumps(approved_data))

        msg = router.planning_approve("ch-1", "discord")
        assert "Aprobat" in msg or "✅" in msg

        approved = json.loads(tmp_state["approved"].read_text())
        entry = approved["projects"][0]
        assert entry["status"] == "approved"
        assert entry["approved_at"] is not None
        assert entry["planning_session_id"] is None
        assert entry["final_plan_path"]  # set

        # Planning state cleared
        from src.planning_session import is_in_planning
        assert is_in_planning("discord", "ch-1") is False

    def test_no_state_returns_error_message(self, tmp_state):
        msg = router.planning_approve("ch-missing", "discord")
        assert "Nu există" in msg


# ---------------------------------------------------------------------------
# planning_cancel via route_message /cancel
# ---------------------------------------------------------------------------


class TestRouteMessagePlanningCancel:
    def test_slash_cancel_in_planning_clears_state(self, tmp_state):
        # Seed a planning session and approved-tasks pending entry
        _save_planning_state({
            _channel_key("discord", "ch-1"): {
                "slug": "demo",
                "description": "x",
                "phase": "/office-hours",
                "phases_planned": ["/office-hours", "/plan-ceo-review"],
                "phases_completed": [],
                "phase_index": 0,
                "session_id": "s-uuid",
                "planning_session_id": "ps-uuid",
                "adapter": "discord",
                "channel_id": "ch-1",
                "started_at": "2026-04-26T20:00:00+00:00",
                "updated_at": "2026-04-26T20:00:00+00:00",
                "last_text_excerpt": "Hi",
                "last_subtype": "success",
            }
        })
        approved_data = {
            "projects": [
                {
                    "name": "demo",
                    "description": "x",
                    "status": "planning",
                    "planning_session_id": "ps-uuid",
                    "final_plan_path": None,
                    "proposed_at": "2026-04-26T18:00:00+00:00",
                    "approved_at": None,
                    "started_at": None,
                    "pid": None,
                }
            ],
            "last_updated": None,
        }
        tmp_state["approved"].write_text(json.dumps(approved_data))

        response, is_cmd = router.route_message(
            "ch-1", "user-1", "/cancel", adapter_name="discord"
        )
        assert is_cmd is True
        assert "anulat" in response.lower()
        approved = json.loads(tmp_state["approved"].read_text())
        assert approved["projects"][0]["status"] == "pending"
        from src.planning_session import is_in_planning
        assert is_in_planning("discord", "ch-1") is False


class TestRouteMessagePlanningRespond:
    def test_plain_message_in_planning_routes_to_orchestrator(self, tmp_state):
        # Seed a planning session
        _save_planning_state({
            _channel_key("discord", "ch-1"): {
                "slug": "demo",
                "description": "x",
                "phase": "/office-hours",
                "phases_planned": ["/office-hours"],
                "phases_completed": [],
                "phase_index": 0,
                "session_id": "s-uuid",
                "planning_session_id": "ps-uuid",
                "adapter": "discord",
                "channel_id": "ch-1",
                "started_at": "2026-04-26T20:00:00+00:00",
                "updated_at": "2026-04-26T20:00:00+00:00",
                "last_text_excerpt": "Hi",
                "last_subtype": "success",
            }
        })
        with patch(
            "src.planning_session._run_claude",
            return_value=_fake_result(
                session_id="s-uuid", text="thanks PHASE_STATUS: needs_input"
            ),
        ) as mock_run:
            response, is_cmd = router.route_message(
                "ch-1", "user-1", "Vreau așa ceva.", adapter_name="discord"
            )
        mock_run.assert_called_once()
        # respond uses --resume
        cmd = mock_run.call_args[0][0]
        assert "--resume" in cmd
        assert is_cmd is False
        assert "thanks" in response

    def test_no_planning_state_falls_through_to_normal_routing(self, tmp_state):
        # No planning state — should go to ralph dispatch / Claude.
        with patch(
            "src.router.send_message", return_value="claude says hi"
        ) as mock_send:
            response, is_cmd = router.route_message(
                "ch-1", "user-1", "hello",
                adapter_name="discord",
                model="sonnet",
            )
        mock_send.assert_called_once()
        assert response == "claude says hi"
        assert is_cmd is False
