"""Tests for the planning module."""

from __future__ import annotations

from pathlib import Path

from prisme.generators.base import GeneratedFile
from prisme.planning.planner import (
    GenerationPlan,
    PlannedFile,
    create_plan,
    load_plan,
    save_plan,
)
from prisme.spec.stack import FileStrategy


class TestPlannedFile:
    """Tests for PlannedFile dataclass."""

    def test_create(self) -> None:
        pf = PlannedFile(path="test.py", action="create", strategy="always_overwrite")
        assert pf.path == "test.py"
        assert pf.action == "create"
        assert pf.strategy == "always_overwrite"

    def test_optional_fields(self) -> None:
        pf = PlannedFile(
            path="test.py",
            action="modify",
            strategy="always_overwrite",
            content_hash="abc123",
            diff_summary="changed content",
        )
        assert pf.content_hash == "abc123"
        assert pf.diff_summary == "changed content"


class TestGenerationPlan:
    """Tests for GenerationPlan dataclass."""

    def test_creates_property(self) -> None:
        plan = GenerationPlan(
            files=[
                PlannedFile(path="new.py", action="create", strategy="always_overwrite"),
                PlannedFile(path="old.py", action="modify", strategy="always_overwrite"),
                PlannedFile(path="skip.py", action="skip", strategy="generate_once"),
            ]
        )
        assert len(plan.creates) == 1
        assert plan.creates[0].path == "new.py"

    def test_modifies_property(self) -> None:
        plan = GenerationPlan(
            files=[
                PlannedFile(path="new.py", action="create", strategy="always_overwrite"),
                PlannedFile(path="old.py", action="modify", strategy="always_overwrite"),
            ]
        )
        assert len(plan.modifies) == 1
        assert plan.modifies[0].path == "old.py"

    def test_skips_property(self) -> None:
        plan = GenerationPlan(
            files=[
                PlannedFile(path="skip.py", action="skip", strategy="generate_once"),
            ]
        )
        assert len(plan.skips) == 1

    def test_to_dict(self) -> None:
        plan = GenerationPlan(
            created_at="2024-01-01",
            spec_path="specs/models.py",
            project_path="specs/project.py",
            files=[
                PlannedFile(path="test.py", action="create", strategy="always_overwrite"),
            ],
        )
        data = plan.to_dict()
        assert data["created_at"] == "2024-01-01"
        assert data["spec_path"] == "specs/models.py"
        assert data["project_path"] == "specs/project.py"
        assert len(data["files"]) == 1

    def test_from_dict(self) -> None:
        data = {
            "created_at": "2024-01-01",
            "spec_path": "specs/models.py",
            "project_path": "specs/project.py",
            "files": [
                {"path": "test.py", "action": "create", "strategy": "always_overwrite"},
            ],
        }
        plan = GenerationPlan.from_dict(data)
        assert plan.created_at == "2024-01-01"
        assert plan.spec_path == "specs/models.py"
        assert plan.project_path == "specs/project.py"
        assert len(plan.files) == 1

    def test_from_dict_defaults(self) -> None:
        plan = GenerationPlan.from_dict({})
        assert plan.created_at == ""
        assert plan.spec_path == ""
        assert plan.project_path is None
        assert plan.files == []

    def test_roundtrip(self) -> None:
        plan = GenerationPlan(
            created_at="2024-01-01",
            spec_path="specs/models.py",
            files=[
                PlannedFile(
                    path="test.py",
                    action="create",
                    strategy="always_overwrite",
                    content_hash="abc",
                ),
            ],
        )
        restored = GenerationPlan.from_dict(plan.to_dict())
        assert restored.created_at == plan.created_at
        assert restored.spec_path == plan.spec_path
        assert len(restored.files) == len(plan.files)
        assert restored.files[0].content_hash == "abc"


class TestCreatePlan:
    """Tests for create_plan."""

    def test_new_file_action_is_create(self, tmp_path: Path) -> None:
        generated_files = [
            GeneratedFile(
                path=Path("new.py"),
                content="content",
                strategy=FileStrategy.ALWAYS_OVERWRITE,
            )
        ]
        plan = create_plan(tmp_path, generated_files, spec_path="models.py")
        assert len(plan.files) == 1
        assert plan.files[0].action == "create"

    def test_existing_file_overwrite_action_is_modify(self, tmp_path: Path) -> None:
        (tmp_path / "existing.py").write_text("old content")
        generated_files = [
            GeneratedFile(
                path=Path("existing.py"),
                content="new content",
                strategy=FileStrategy.ALWAYS_OVERWRITE,
            )
        ]
        plan = create_plan(tmp_path, generated_files)
        assert plan.files[0].action == "modify"

    def test_existing_file_generate_once_action_is_skip(self, tmp_path: Path) -> None:
        (tmp_path / "existing.py").write_text("old content")
        generated_files = [
            GeneratedFile(
                path=Path("existing.py"),
                content="new content",
                strategy=FileStrategy.GENERATE_ONCE,
            )
        ]
        plan = create_plan(tmp_path, generated_files)
        assert plan.files[0].action == "skip"

    def test_plan_has_content_hash(self, tmp_path: Path) -> None:
        generated_files = [
            GeneratedFile(
                path=Path("file.py"),
                content="test content",
                strategy=FileStrategy.ALWAYS_OVERWRITE,
            )
        ]
        plan = create_plan(tmp_path, generated_files)
        assert plan.files[0].content_hash is not None

    def test_plan_has_metadata(self, tmp_path: Path) -> None:
        plan = create_plan(
            tmp_path,
            [],
            spec_path="specs/models.py",
            project_path="specs/project.py",
        )
        assert plan.spec_path == "specs/models.py"
        assert plan.project_path == "specs/project.py"
        assert plan.created_at != ""


class TestSaveAndLoadPlan:
    """Tests for save_plan and load_plan."""

    def test_save_creates_file(self, tmp_path: Path) -> None:
        plan = GenerationPlan(
            created_at="2024-01-01",
            spec_path="models.py",
            files=[
                PlannedFile(path="test.py", action="create", strategy="always_overwrite"),
            ],
        )
        path = save_plan(plan, tmp_path)
        assert path.exists()
        assert ".prisme/plan.json" in str(path)

    def test_load_returns_plan(self, tmp_path: Path) -> None:
        plan = GenerationPlan(
            created_at="2024-01-01",
            spec_path="models.py",
            files=[
                PlannedFile(path="test.py", action="create", strategy="always_overwrite"),
            ],
        )
        save_plan(plan, tmp_path)
        loaded = load_plan(tmp_path)
        assert loaded is not None
        assert loaded.created_at == "2024-01-01"
        assert len(loaded.files) == 1

    def test_load_returns_none_when_no_plan(self, tmp_path: Path) -> None:
        result = load_plan(tmp_path)
        assert result is None

    def test_save_creates_parent_directory(self, tmp_path: Path) -> None:
        plan = GenerationPlan(files=[])
        path = save_plan(plan, tmp_path)
        assert path.parent.exists()
