"""Tests for the /api/wizard router endpoints."""

import pytest
from unittest.mock import AsyncMock, patch
from pathlib import Path

from fastapi.testclient import TestClient

from hangar.main import app
from hangar.dependencies import set_project_path


@pytest.fixture
def tmp_project(tmp_path):
    """Create a project with openspec structure."""
    changes_dir = tmp_path / "openspec" / "changes"
    changes_dir.mkdir(parents=True)
    # Create a config.yaml for context injection
    config_dir = tmp_path / "openspec"
    (config_dir / "config.yaml").write_text(
        "context: Test project\nrules:\n  proposal:\n    - Test rule\n"
    )
    (tmp_path / ".beads").mkdir()
    set_project_path(tmp_path)
    return tmp_path


@pytest.fixture
def client(tmp_project):
    return TestClient(app)


class TestCreateEndpoint:
    def test_creates_change(self, client, tmp_project):
        res = client.post(
            "/api/wizard/create",
            json={"name": "my-feature", "description": "Build something cool"},
        )
        assert res.status_code == 200
        data = res.json()
        assert data["name"] == "my-feature"
        assert data["created"] is True
        assert (tmp_project / "openspec" / "changes" / "my-feature").is_dir()

    def test_duplicate_returns_409(self, client, tmp_project):
        client.post(
            "/api/wizard/create",
            json={"name": "my-feature", "description": "First"},
        )
        res = client.post(
            "/api/wizard/create",
            json={"name": "my-feature", "description": "Second"},
        )
        assert res.status_code == 409

    def test_invalid_name_returns_422(self, client):
        res = client.post(
            "/api/wizard/create",
            json={"name": "My Feature!", "description": "Bad name"},
        )
        assert res.status_code == 422


class TestApproveEndpoint:
    def test_writes_artifact(self, client, tmp_project):
        # Create the change first
        (tmp_project / "openspec" / "changes" / "test-change").mkdir()
        res = client.post(
            "/api/wizard/approve",
            json={
                "change_name": "test-change",
                "artifact_type": "proposal",
                "content": "# My Proposal",
            },
        )
        assert res.status_code == 200
        assert res.json()["status"] == "approved"
        path = tmp_project / "openspec" / "changes" / "test-change" / "proposal.md"
        assert path.read_text() == "# My Proposal"

    def test_missing_change_returns_404(self, client):
        res = client.post(
            "/api/wizard/approve",
            json={
                "change_name": "nonexistent",
                "artifact_type": "proposal",
                "content": "content",
            },
        )
        assert res.status_code == 404

    def test_invalid_artifact_type_returns_422(self, client, tmp_project):
        (tmp_project / "openspec" / "changes" / "test-change").mkdir()
        res = client.post(
            "/api/wizard/approve",
            json={
                "change_name": "test-change",
                "artifact_type": "readme",
                "content": "content",
            },
        )
        assert res.status_code == 422


class TestStateEndpoint:
    def test_empty_change(self, client, tmp_project):
        (tmp_project / "openspec" / "changes" / "test-change").mkdir()
        res = client.get("/api/wizard/test-change/state")
        assert res.status_code == 200
        data = res.json()
        assert data["change_name"] == "test-change"
        assert data["current_step"] == 0
        assert len(data["steps"]) == 4
        assert all(s["status"] == "pending" for s in data["steps"])

    def test_partial_progress(self, client, tmp_project):
        change_dir = tmp_project / "openspec" / "changes" / "test-change"
        change_dir.mkdir()
        (change_dir / "proposal.md").write_text("# Proposal content")
        (change_dir / ".description").write_text("My description")

        res = client.get("/api/wizard/test-change/state")
        data = res.json()
        assert data["current_step"] == 1
        assert data["description"] == "My description"
        assert data["steps"][0]["status"] == "approved"
        assert data["steps"][0]["content"] == "# Proposal content"
        assert data["steps"][1]["status"] == "pending"

    def test_missing_change_returns_404(self, client):
        res = client.get("/api/wizard/nonexistent/state")
        assert res.status_code == 404


class TestGenerateEndpoint:
    def test_generate_returns_content(self, client):
        mock_proc = AsyncMock()
        mock_proc.communicate = AsyncMock(
            return_value=(b"# Generated Content", b"")
        )
        mock_proc.returncode = 0

        with patch(
            "hangar.services.wizard.asyncio.create_subprocess_exec",
            return_value=mock_proc,
        ):
            res = client.post(
                "/api/wizard/generate",
                json={
                    "change_name": "test",
                    "artifact_type": "proposal",
                    "description": "Build user auth",
                    "approved_artifacts": {},
                },
            )
        assert res.status_code == 200
        data = res.json()
        assert data["content"] == "# Generated Content"
        assert data["agent_type"] == "claude-code"
        assert data["duration_ms"] >= 0

    def test_generate_with_feedback(self, client):
        mock_proc = AsyncMock()
        mock_proc.communicate = AsyncMock(
            return_value=(b"# Revised Content", b"")
        )
        mock_proc.returncode = 0

        with patch(
            "hangar.services.wizard.asyncio.create_subprocess_exec",
            return_value=mock_proc,
        ):
            res = client.post(
                "/api/wizard/generate",
                json={
                    "change_name": "test",
                    "artifact_type": "proposal",
                    "description": "Build user auth",
                    "approved_artifacts": {},
                    "feedback": "Make it more concise",
                },
            )
        assert res.status_code == 200
        data = res.json()
        assert "Make it more concise" in data["prompt_used"]
