"""Tests for SpecManager — Spec 文档管理器"""

from __future__ import annotations

import time
from pathlib import Path

import pytest

from agentkit.core.spec_manager import Spec, SpecManager, SpecStep


@pytest.fixture
def specs_dir(tmp_path: Path) -> str:
    """Provide a temporary directory for spec files."""
    return str(tmp_path / "specs")


@pytest.fixture
def mgr(specs_dir: str) -> SpecManager:
    """Create a SpecManager with a temporary directory."""
    return SpecManager(specs_dir=specs_dir)


def make_spec(spec_id: str = "test-spec", goal: str = "test goal") -> Spec:
    """Create a test Spec."""
    return Spec(
        spec_id=spec_id,
        goal=goal,
        steps=[
            SpecStep(step_id="s1", name="Step 1", description="First step"),
            SpecStep(step_id="s2", name="Step 2", description="Second step", dependencies=["s1"]),
        ],
    )


class TestSpecManagerCreateAndGet:
    """Test create and get a spec."""

    def test_create_and_get(self, mgr: SpecManager):
        spec = make_spec()
        path = mgr.create(spec)
        assert path.exists()

        loaded = mgr.get(spec.spec_id)
        assert loaded is not None
        assert loaded.spec_id == spec.spec_id
        assert loaded.goal == spec.goal
        assert len(loaded.steps) == 2
        assert loaded.steps[0].step_id == "s1"
        assert loaded.steps[1].dependencies == ["s1"]

    def test_create_writes_yaml_file(self, mgr: SpecManager, specs_dir: str):
        spec = make_spec(spec_id="yaml-test")
        mgr.create(spec)
        yaml_path = Path(specs_dir) / "yaml-test.yaml"
        assert yaml_path.exists()

    def test_get_returns_from_cache(self, mgr: SpecManager):
        spec = make_spec(spec_id="cached")
        mgr.create(spec)
        # Second get should hit cache
        loaded = mgr.get("cached")
        assert loaded is not None
        assert loaded.spec_id == "cached"


class TestSpecManagerUpdate:
    """Test update spec fields."""

    def test_update_goal(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)

        updated = mgr.update(spec.spec_id, goal="new goal")
        assert updated is not None
        assert updated.goal == "new goal"

    def test_update_steps(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)

        new_steps = [
            {"step_id": "s1", "name": "Step 1 Updated", "description": "Updated first step"},
        ]
        updated = mgr.update(spec.spec_id, steps=new_steps)
        assert updated is not None
        assert len(updated.steps) == 1
        assert updated.steps[0].name == "Step 1 Updated"

    def test_update_metadata(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)

        updated = mgr.update(spec.spec_id, metadata={"key": "value"})
        assert updated is not None
        assert updated.metadata == {"key": "value"}

    def test_update_nonexistent_returns_none(self, mgr: SpecManager):
        result = mgr.update("nonexistent", goal="x")
        assert result is None


class TestSpecManagerConfirm:
    """Test confirm sets status and confirmed_at."""

    def test_confirm_sets_status_and_timestamp(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)

        assert spec.status == "draft"
        assert spec.confirmed_at is None

        confirmed = mgr.confirm(spec.spec_id)
        assert confirmed is not None
        assert confirmed.status == "confirmed"
        assert confirmed.confirmed_at is not None

    def test_confirm_marks_pending_steps_as_confirmed(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)

        confirmed = mgr.confirm(spec.spec_id)
        assert confirmed is not None
        for step in confirmed.steps:
            assert step.status == "confirmed"

    def test_confirm_nonexistent_returns_none(self, mgr: SpecManager):
        result = mgr.confirm("nonexistent")
        assert result is None


class TestSpecManagerList:
    """Test list_specs returns specs sorted by created_at desc."""

    def test_list_specs_sorted_by_created_at_desc(self, mgr: SpecManager):
        spec_a = make_spec(spec_id="spec-a", goal="A")
        mgr.create(spec_a)

        # Small delay to ensure different timestamps
        time.sleep(0.01)

        spec_b = make_spec(spec_id="spec-b", goal="B")
        mgr.create(spec_b)

        specs = mgr.list_specs()
        assert len(specs) == 2
        # Most recent first
        assert specs[0].spec_id == "spec-b"
        assert specs[1].spec_id == "spec-a"

    def test_list_specs_filter_by_status(self, mgr: SpecManager):
        spec_a = make_spec(spec_id="draft-spec")
        mgr.create(spec_a)

        spec_b = make_spec(spec_id="confirmed-spec")
        mgr.create(spec_b)
        mgr.confirm(spec_b.spec_id)

        draft_specs = mgr.list_specs(status="draft")
        assert len(draft_specs) == 1
        assert draft_specs[0].spec_id == "draft-spec"

        confirmed_specs = mgr.list_specs(status="confirmed")
        assert len(confirmed_specs) == 1
        assert confirmed_specs[0].spec_id == "confirmed-spec"

    def test_list_specs_empty(self, mgr: SpecManager):
        specs = mgr.list_specs()
        assert specs == []


class TestSpecManagerDelete:
    """Test delete removes the spec."""

    def test_delete_removes_spec(self, mgr: SpecManager):
        spec = make_spec()
        mgr.create(spec)
        assert mgr.get(spec.spec_id) is not None

        result = mgr.delete(spec.spec_id)
        assert result is True
        assert mgr.get(spec.spec_id) is None

    def test_delete_removes_yaml_file(self, mgr: SpecManager, specs_dir: str):
        spec = make_spec(spec_id="delete-me")
        mgr.create(spec)
        yaml_path = Path(specs_dir) / "delete-me.yaml"
        assert yaml_path.exists()

        mgr.delete("delete-me")
        assert not yaml_path.exists()

    def test_delete_nonexistent_returns_false(self, mgr: SpecManager):
        result = mgr.delete("nonexistent")
        assert result is False


class TestSpecManagerGetNonExistent:
    """Test get non-existent spec returns None."""

    def test_get_nonexistent_returns_none(self, mgr: SpecManager):
        result = mgr.get("does-not-exist")
        assert result is None
