"""Tests for /api/specs router."""

from unittest.mock import AsyncMock

import pytest
from fastapi.testclient import TestClient

from hangar.dependencies import get_openspec_service
from hangar.main import app
from hangar.models.openspec import (
    OpenSpecArtifact,
    OpenSpecArtifactContent,
    OpenSpecChangeList,
    OpenSpecChangeSummary,
    OpenSpecChangeStatus,
)


def _mock_openspec_service() -> AsyncMock:
    """Create a mock OpenSpecService."""
    service = AsyncMock()
    service.list_changes.return_value = OpenSpecChangeList(
        changes=[
            OpenSpecChangeSummary(
                name="mvp-foundation",
                completedTasks=3,
                totalTasks=30,
                status="in-progress",
            ),
        ]
    )
    service.show_change.return_value = OpenSpecChangeStatus(
        changeName="mvp-foundation",
        schemaName="spec-driven",
        isComplete=False,
        artifacts=[
            OpenSpecArtifact(id="proposal", outputPath="proposal.md", status="done"),
            OpenSpecArtifact(id="design", outputPath="design.md", status="done"),
        ],
    )
    service.read_all_artifacts.return_value = [
        OpenSpecArtifactContent(
            id="proposal",
            output_path="proposal.md",
            status="done",
            content="# Proposal\n\nContent here.",
        ),
    ]
    return service


@pytest.fixture(autouse=True)
def _override_deps():
    """Override OpenSpecService dependency for all tests in this module."""
    mock = _mock_openspec_service()
    app.dependency_overrides[get_openspec_service] = lambda: mock
    yield mock
    app.dependency_overrides.clear()


def test_list_specs(client: TestClient) -> None:
    """GET /api/specs should return all changes."""
    response = client.get("/api/specs")
    assert response.status_code == 200
    data = response.json()
    assert len(data["changes"]) == 1
    assert data["changes"][0]["name"] == "mvp-foundation"


def test_get_spec(client: TestClient) -> None:
    """GET /api/specs/{name} should return change status."""
    response = client.get("/api/specs/mvp-foundation")
    assert response.status_code == 200
    data = response.json()
    assert data["changeName"] == "mvp-foundation"
    assert len(data["artifacts"]) == 2


def test_get_spec_not_found(client: TestClient) -> None:
    """GET /api/specs/{name} should return 502 for CLI failure."""
    mock = AsyncMock()
    mock.show_change.side_effect = RuntimeError("openspec status failed: not found")
    app.dependency_overrides[get_openspec_service] = lambda: mock
    response = client.get("/api/specs/nonexistent")
    assert response.status_code == 502


def test_get_spec_artifacts(client: TestClient) -> None:
    """GET /api/specs/{name}/artifacts should return artifact contents."""
    response = client.get("/api/specs/mvp-foundation/artifacts")
    assert response.status_code == 200
    data = response.json()
    assert len(data) == 1
    assert data[0]["id"] == "proposal"
    assert "# Proposal" in data[0]["content"]


def test_list_specs_includes_archived(client: TestClient) -> None:
    """GET /api/specs should include is_archived flag."""
    mock = AsyncMock()
    mock.list_changes.return_value = OpenSpecChangeList(
        changes=[
            OpenSpecChangeSummary(
                name="active-feature",
                completedTasks=2,
                totalTasks=5,
                status="in-progress",
                is_archived=False,
            ),
            OpenSpecChangeSummary(
                name="old-feature",
                completedTasks=0,
                totalTasks=0,
                status="archived",
                is_archived=True,
            ),
        ]
    )
    app.dependency_overrides[get_openspec_service] = lambda: mock
    response = client.get("/api/specs")
    assert response.status_code == 200
    data = response.json()
    assert len(data["changes"]) == 2
    assert data["changes"][0]["is_archived"] is False
    assert data["changes"][1]["is_archived"] is True
    app.dependency_overrides.clear()
