"""Tests for file handler utilities."""

from __future__ import annotations

from pathlib import Path

from prisme.spec.stack import FileStrategy
from prisme.utils.file_handler import (
    ProtectedRegion,
    ensure_directory,
    get_relative_import,
    merge_protected_regions,
    parse_protected_regions,
    should_write_file,
    write_file_with_strategy,
)


class TestParseProtectedRegions:
    """Tests for parse_protected_regions."""

    def test_no_regions(self) -> None:
        content = "line1\nline2\nline3"
        result = parse_protected_regions(content)
        assert result.protected_regions == {}

    def test_single_region_python(self) -> None:
        content = (
            "line1\n"
            "# PRISM:PROTECTED:START - Custom Logic\n"
            "custom code here\n"
            "# PRISM:PROTECTED:END\n"
            "line5"
        )
        result = parse_protected_regions(content)
        assert "Custom Logic" in result.protected_regions
        assert result.protected_regions["Custom Logic"].content == "custom code here"

    def test_multiple_regions(self) -> None:
        content = (
            "# PRISM:PROTECTED:START - Region A\n"
            "code A\n"
            "# PRISM:PROTECTED:END\n"
            "middle\n"
            "# PRISM:PROTECTED:START - Region B\n"
            "code B\n"
            "# PRISM:PROTECTED:END\n"
        )
        result = parse_protected_regions(content)
        assert len(result.protected_regions) == 2
        assert "Region A" in result.protected_regions
        assert "Region B" in result.protected_regions

    def test_region_without_end_marker(self) -> None:
        content = "# PRISM:PROTECTED:START - Open Region\ncode here\nmore code\n"
        result = parse_protected_regions(content)
        assert "Open Region" in result.protected_regions

    def test_jsx_comment_regions(self) -> None:
        content = (
            "{/* PRISM:PROTECTED:START - JSX Region */}\njsx code\n{/* PRISM:PROTECTED:END */}\n"
        )
        result = parse_protected_regions(content)
        assert len(result.protected_regions) == 1

    def test_js_comment_regions(self) -> None:
        content = "// PRISM:PROTECTED:START - JS Region\njs code\n// PRISM:PROTECTED:END\n"
        result = parse_protected_regions(content)
        assert "JS Region" in result.protected_regions


class TestMergeProtectedRegions:
    """Tests for merge_protected_regions."""

    def test_no_old_regions(self) -> None:
        content = "new content"
        result = merge_protected_regions(content, {})
        assert result == content

    def test_merge_existing_region(self) -> None:
        new_content = (
            "before\n"
            "# PRISM:PROTECTED:START - Custom Logic\n"
            "new default\n"
            "# PRISM:PROTECTED:END\n"
            "after"
        )
        old_regions = {
            "Custom Logic": ProtectedRegion(
                name="Custom Logic",
                content="user custom code",
                start_line=1,
                end_line=3,
            )
        }
        result = merge_protected_regions(new_content, old_regions)
        assert "user custom code" in result
        assert "new default" not in result

    def test_no_old_regions_returns_unchanged(self) -> None:
        """When no old regions exist, content is returned unchanged."""
        new_content = "# PRISM:PROTECTED:START - New Region\ndefault code\n# PRISM:PROTECTED:END\n"
        old_regions = {}
        result = merge_protected_regions(new_content, old_regions)
        assert result == new_content


class TestShouldWriteFile:
    """Tests for should_write_file."""

    def test_always_overwrite(self, tmp_path: Path) -> None:
        existing = tmp_path / "file.txt"
        existing.write_text("old")
        assert should_write_file(existing, FileStrategy.ALWAYS_OVERWRITE) is True

    def test_generate_once_new_file(self, tmp_path: Path) -> None:
        new_file = tmp_path / "new.txt"
        assert should_write_file(new_file, FileStrategy.GENERATE_ONCE) is True

    def test_generate_once_existing_file(self, tmp_path: Path) -> None:
        existing = tmp_path / "file.txt"
        existing.write_text("old")
        assert should_write_file(existing, FileStrategy.GENERATE_ONCE) is False

    def test_always_overwrite_new_file(self, tmp_path: Path) -> None:
        new_file = tmp_path / "new.txt"
        assert should_write_file(new_file, FileStrategy.ALWAYS_OVERWRITE) is True


class TestWriteFileWithStrategy:
    """Tests for write_file_with_strategy."""

    def test_writes_new_file(self, tmp_path: Path) -> None:
        path = tmp_path / "new.txt"
        result = write_file_with_strategy(path, "content", FileStrategy.ALWAYS_OVERWRITE)
        assert result is True
        assert path.read_text() == "content"

    def test_overwrites_existing(self, tmp_path: Path) -> None:
        path = tmp_path / "file.txt"
        path.write_text("old")
        result = write_file_with_strategy(path, "new", FileStrategy.ALWAYS_OVERWRITE)
        assert result is True
        assert path.read_text() == "new"

    def test_generate_once_skips_existing(self, tmp_path: Path) -> None:
        path = tmp_path / "file.txt"
        path.write_text("old")
        result = write_file_with_strategy(path, "new", FileStrategy.GENERATE_ONCE)
        assert result is False
        assert path.read_text() == "old"

    def test_dry_run_doesnt_write(self, tmp_path: Path) -> None:
        path = tmp_path / "dry.txt"
        result = write_file_with_strategy(
            path, "content", FileStrategy.ALWAYS_OVERWRITE, dry_run=True
        )
        assert result is True
        assert not path.exists()

    def test_preserves_protected_regions(self, tmp_path: Path) -> None:
        path = tmp_path / "file.py"
        path.write_text(
            "header\n# PRISM:PROTECTED:START - Custom\nuser code\n# PRISM:PROTECTED:END\nfooter\n"
        )
        new_content = (
            "new header\n"
            "# PRISM:PROTECTED:START - Custom\n"
            "default code\n"
            "# PRISM:PROTECTED:END\n"
            "new footer\n"
        )
        write_file_with_strategy(path, new_content, FileStrategy.ALWAYS_OVERWRITE)
        result = path.read_text()
        assert "user code" in result
        assert "new header" in result

    def test_creates_parent_directories(self, tmp_path: Path) -> None:
        path = tmp_path / "a" / "b" / "c" / "file.txt"
        write_file_with_strategy(path, "content", FileStrategy.ALWAYS_OVERWRITE)
        assert path.read_text() == "content"


class TestEnsureDirectory:
    """Tests for ensure_directory."""

    def test_creates_directory(self, tmp_path: Path) -> None:
        target = tmp_path / "new" / "dir"
        ensure_directory(target)
        assert target.exists()
        assert target.is_dir()

    def test_existing_directory_is_fine(self, tmp_path: Path) -> None:
        target = tmp_path / "existing"
        target.mkdir()
        ensure_directory(target)  # Should not raise
        assert target.exists()


class TestGetRelativeImport:
    """Tests for get_relative_import."""

    def test_same_directory(self) -> None:
        result = get_relative_import(
            Path("app/models/user.py"),
            Path("app/models/base.py"),
        )
        assert result == ".base"

    def test_parent_directory(self) -> None:
        result = get_relative_import(
            Path("app/models/user.py"),
            Path("app/utils.py"),
        )
        assert result == "..utils"

    def test_subdirectory(self) -> None:
        result = get_relative_import(
            Path("app/main.py"),
            Path("app/models/user.py"),
        )
        assert result == ".models.user"

    def test_sibling_directory(self) -> None:
        result = get_relative_import(
            Path("app/models/user.py"),
            Path("app/schemas/user.py"),
        )
        assert result == "..schemas.user"
