from __future__ import annotations

from pathlib import Path

import pytest

from codeflash_python._model import (
    FunctionParent,
    FunctionSource,
    FunctionToOptimize,
)


class TestFunctionParent:
    """FunctionParent data and display."""

    def test_str_representation(self) -> None:
        """String format is 'type:name'."""
        parent = FunctionParent(name="MyClass", type="ClassDef")
        assert str(parent) == "ClassDef:MyClass"

    def test_frozen(self) -> None:
        """Instances are immutable."""
        parent = FunctionParent(name="MyClass", type="ClassDef")
        with pytest.raises(AttributeError):
            parent.name = "Other"  # type: ignore[misc]


class TestFunctionToOptimize:
    """FunctionToOptimize data and properties."""

    def test_file_path_converts_str(self) -> None:
        """String file paths are converted to Path objects."""
        func = FunctionToOptimize(
            function_name="foo",
            file_path="src/module.py",  # type: ignore[arg-type]
        )
        assert isinstance(func.file_path, Path)
        assert func.file_path == Path("src/module.py")

    def test_qualified_name_no_parents(self) -> None:
        """Qualified name is just the function name with no parents."""
        func = FunctionToOptimize(
            function_name="foo",
            file_path=Path("mod.py"),
        )
        assert func.qualified_name == "foo"

    def test_qualified_name_with_parents(self) -> None:
        """Qualified name includes parent names."""
        func = FunctionToOptimize(
            function_name="bar",
            file_path=Path("mod.py"),
            parents=(
                FunctionParent(name="Outer", type="ClassDef"),
                FunctionParent(name="Inner", type="ClassDef"),
            ),
        )
        assert func.qualified_name == "Outer.Inner.bar"

    def test_class_name_from_class_parent(self) -> None:
        """class_name returns the nearest enclosing ClassDef."""
        func = FunctionToOptimize(
            function_name="method",
            file_path=Path("mod.py"),
            parents=(
                FunctionParent(name="Outer", type="ClassDef"),
                FunctionParent(name="Inner", type="ClassDef"),
            ),
            is_method=True,
        )
        assert func.class_name == "Inner"

    def test_class_name_none_for_top_level(self) -> None:
        """class_name is None for top-level functions."""
        func = FunctionToOptimize(
            function_name="foo",
            file_path=Path("mod.py"),
        )
        assert func.class_name is None

    def test_frozen(self) -> None:
        """Instances are immutable."""
        func = FunctionToOptimize(
            function_name="foo",
            file_path=Path("mod.py"),
        )
        with pytest.raises(AttributeError):
            func.function_name = "bar"  # type: ignore[misc]


class TestFunctionParentSerialization:
    """FunctionParent to_dict/from_dict roundtrip."""

    def test_roundtrip(self) -> None:
        """from_dict(to_dict(x)) == x."""
        parent = FunctionParent(name="MyClass", type="ClassDef")
        assert FunctionParent.from_dict(parent.to_dict()) == parent


class TestFunctionToOptimizeSerialization:
    """FunctionToOptimize to_dict/from_dict roundtrip."""

    def test_roundtrip_with_parents(self) -> None:
        """Full roundtrip preserves all fields."""
        func = FunctionToOptimize(
            function_name="bar",
            file_path=Path("src/mod.py"),
            parents=(FunctionParent(name="Outer", type="ClassDef"),),
            starting_line=10,
            ending_line=20,
            starting_col=4,
            ending_col=0,
            is_async=True,
            is_method=True,
            doc_start_line=11,
        )
        restored = FunctionToOptimize.from_dict(func.to_dict())
        assert restored == func

    def test_roundtrip_minimal(self) -> None:
        """Minimal function roundtrips correctly."""
        func = FunctionToOptimize(
            function_name="foo",
            file_path=Path("mod.py"),
        )
        assert FunctionToOptimize.from_dict(func.to_dict()) == func


class TestFunctionSourceSerialization:
    """FunctionSource to_dict/from_dict roundtrip."""

    def test_roundtrip(self) -> None:
        """Full roundtrip preserves all fields."""
        src = FunctionSource(
            file_path=Path("src/mod.py"),
            qualified_name="MyClass.method",
            fully_qualified_name="pkg.mod.MyClass.method",
            source_code="def method(self): pass\n",
            only_function_name="method",
            definition_type="FunctionDef",
        )
        assert FunctionSource.from_dict(src.to_dict()) == src

    def test_roundtrip_minimal(self) -> None:
        """Minimal source roundtrips correctly."""
        src = FunctionSource(
            file_path=Path("m.py"),
            qualified_name="f",
            fully_qualified_name="m.f",
            source_code="def f(): ...\n",
        )
        assert FunctionSource.from_dict(src.to_dict()) == src
