"""
Unit tests for version detection functionality.
"""

from pathlib import Path

import pytest

from persuade.version_detection import (
    VersionComparison,
    VersionInfo,
    check_outdated_files,
    compare_file_versions,
    compare_versions,
    extract_version,
    format_version_warning,
    parse_version,
)


class TestParseVersion:
    """Tests for parse_version function."""

    def test_parse_valid_version(self) -> None:
        """Valid semantic version should parse correctly."""
        assert parse_version("1.0.0") == (1, 0, 0)
        assert parse_version("2.5.3") == (2, 5, 3)
        assert parse_version("10.20.30") == (10, 20, 30)

    def test_parse_invalid_version(self) -> None:
        """Invalid version formats should raise ValueError."""
        with pytest.raises(ValueError):
            parse_version("1.0")

        with pytest.raises(ValueError):
            parse_version("1.0.0.0")

        with pytest.raises(ValueError):
            parse_version("invalid")


class TestCompareVersions:
    """Tests for compare_versions function."""

    def test_source_newer(self) -> None:
        """Source newer than target should return 1."""
        assert compare_versions("2.0.0", "1.0.0") == 1
        assert compare_versions("1.1.0", "1.0.0") == 1
        assert compare_versions("1.0.1", "1.0.0") == 1

    def test_source_older(self) -> None:
        """Source older than target should return -1."""
        assert compare_versions("1.0.0", "2.0.0") == -1
        assert compare_versions("1.0.0", "1.1.0") == -1
        assert compare_versions("1.0.0", "1.0.1") == -1

    def test_versions_equal(self) -> None:
        """Equal versions should return 0."""
        assert compare_versions("1.0.0", "1.0.0") == 0
        assert compare_versions("2.5.3", "2.5.3") == 0


class TestExtractVersion:
    """Tests for extract_version function."""

    def test_extract_from_yaml_file(self, tmp_path: Path) -> None:
        """Extract version from YAML file with hash comment."""
        yaml_file = tmp_path / "workflow.yml"
        yaml_file.write_text("# persuade-version: 1.2.3\nname: test\n")

        result = extract_version(yaml_file)

        assert result.version == "1.2.3"
        assert result.file_path == yaml_file

    def test_extract_from_shell_file(self, tmp_path: Path) -> None:
        """Extract version from shell script."""
        shell_file = tmp_path / "script.sh"
        shell_file.write_text("#!/bin/bash\n# persuade-version: 2.0.0\necho hello\n")

        result = extract_version(shell_file)

        assert result.version == "2.0.0"

    def test_extract_from_markdown_file(self, tmp_path: Path) -> None:
        """Extract version from markdown file with XML comment."""
        md_file = tmp_path / "README.md"
        md_file.write_text("<!-- persuade-version: 1.5.0 -->\n# README\n")

        result = extract_version(md_file)

        assert result.version == "1.5.0"

    def test_extract_no_version(self, tmp_path: Path) -> None:
        """File without version should return None."""
        file = tmp_path / "test.txt"
        file.write_text("No version here\n")

        result = extract_version(file)

        assert result.version is None

    def test_extract_nonexistent_file(self, tmp_path: Path) -> None:
        """Nonexistent file should return None version."""
        nonexistent = tmp_path / "nonexistent.txt"

        result = extract_version(nonexistent)

        assert result.version is None


class TestCompareFileVersions:
    """Tests for compare_file_versions function."""

    def test_both_have_versions_outdated(self, tmp_path: Path) -> None:
        """Target with older version should be marked outdated."""
        source = tmp_path / "source.yml"
        target = tmp_path / "target.yml"

        source.write_text("# persuade-version: 2.0.0\nname: test\n")
        target.write_text("# persuade-version: 1.0.0\nname: test\n")

        result = compare_file_versions(source, target)

        assert result.is_outdated is True
        assert result.source_version == "2.0.0"
        assert result.target_version == "1.0.0"

    def test_both_have_versions_current(self, tmp_path: Path) -> None:
        """Target with same or newer version should not be marked outdated."""
        source = tmp_path / "source.yml"
        target = tmp_path / "target.yml"

        source.write_text("# persuade-version: 1.0.0\nname: test\n")
        target.write_text("# persuade-version: 1.0.0\nname: test\n")

        result = compare_file_versions(source, target)

        assert result.is_outdated is False

    def test_source_has_version_target_does_not(self, tmp_path: Path) -> None:
        """Target without version when source has version is outdated."""
        source = tmp_path / "source.yml"
        target = tmp_path / "target.yml"

        source.write_text("# persuade-version: 1.0.0\nname: test\n")
        target.write_text("name: test\n")

        result = compare_file_versions(source, target)

        assert result.is_outdated is True
        assert result.source_version == "1.0.0"
        assert result.target_version is None


class TestCheckOutdatedFiles:
    """Tests for check_outdated_files function."""

    def test_finds_outdated_files(self, tmp_path: Path) -> None:
        """Should find and return outdated files."""
        source_root = tmp_path / "source"
        target_root = tmp_path / "target"
        source_root.mkdir()
        target_root.mkdir()

        # Create source file with newer version
        (source_root / "test.yml").write_text("# persuade-version: 2.0.0\n")
        # Create target file with older version
        (target_root / "test.yml").write_text("# persuade-version: 1.0.0\n")

        result = check_outdated_files(source_root, target_root, ["test.yml"])

        assert len(result) == 1
        assert result[0].is_outdated is True

    def test_skips_nonexistent_target_files(self, tmp_path: Path) -> None:
        """Should skip files that don't exist in target."""
        source_root = tmp_path / "source"
        target_root = tmp_path / "target"
        source_root.mkdir()
        target_root.mkdir()

        (source_root / "test.yml").write_text("# persuade-version: 1.0.0\n")

        result = check_outdated_files(source_root, target_root, ["test.yml"])

        assert len(result) == 0


class TestFormatVersionWarning:
    """Tests for format_version_warning function."""

    def test_format_single_file(self, tmp_path: Path) -> None:
        """Format warning for single outdated file."""
        comparison = VersionComparison(
            source_version="2.0.0",
            target_version="1.0.0",
            is_outdated=True,
            source_path=tmp_path / "source.yml",
            target_path=tmp_path / "target.yml",
        )

        result = format_version_warning([comparison])

        assert "target.yml" in result
        assert "1.0.0" in result
        assert "2.0.0" in result
        assert "persuade sync --force-update" in result

    def test_format_empty_list(self) -> None:
        """Empty list should return empty string."""
        result = format_version_warning([])

        assert result == ""


class TestVersionInfo:
    """Tests for VersionInfo dataclass."""

    def test_valid_version(self, tmp_path: Path) -> None:
        """Valid version should be accepted."""
        info = VersionInfo(version="1.2.3", file_path=tmp_path / "test.txt")
        assert info.version == "1.2.3"

    def test_none_version(self, tmp_path: Path) -> None:
        """None version should be accepted."""
        info = VersionInfo(version=None, file_path=tmp_path / "test.txt")
        assert info.version is None

    def test_invalid_version_format(self, tmp_path: Path) -> None:
        """Invalid version format should raise ValueError."""
        with pytest.raises(ValueError):
            VersionInfo(version="invalid", file_path=tmp_path / "test.txt")
