"""Core VCS regression tests."""

from __future__ import annotations

import sys
import warnings
from collections.abc import Sequence
from dataclasses import replace
from pathlib import Path

import pytest
from vcs_versioning import Configuration
from vcs_versioning._backends._git import parse
from vcs_versioning._run_cmd import run
from vcs_versioning._scm_version import meta
from vcs_versioning.test_api import WorkDir


@pytest.mark.skipif(sys.platform != "win32", reason="this bug is only valid on windows")
def test_case_mismatch_on_windows_git(tmp_path: Path) -> None:
    """Case insensitive path checks on Windows"""
    camel_case_path = tmp_path / "CapitalizedDir"
    camel_case_path.mkdir()
    run("git init", camel_case_path)
    res = parse(str(camel_case_path).lower(), Configuration())
    assert res is not None


@pytest.mark.skipif(sys.platform != "win32", reason="this bug is only valid on windows")
def test_case_mismatch_nested_dir_windows_git(tmp_path: Path) -> None:
    """Test case where we have a nested directory with different casing"""
    # Create git repo in my_repo
    repo_path = tmp_path / "my_repo"
    repo_path.mkdir()
    wd = WorkDir(repo_path).setup_git()

    # Create a nested directory with specific casing
    nested_dir = repo_path / "CasedDir"
    nested_dir.mkdir()

    # Create a pyproject.toml in the nested directory
    wd.write(
        "CasedDir/pyproject.toml",
        """
[build-system]
requires = ["setuptools>=64", "setuptools-scm"]
build-backend = "setuptools.build_meta"

[project]
name = "test-project"
dynamic = ["version"]

[tool.setuptools_scm]
""",
    )

    # Add and commit the file
    wd.add_and_commit("Initial commit")

    # Now try to parse from the nested directory with lowercase path
    # This simulates: cd my_repo/caseddir (lowercase) when actual dir is CasedDir
    lowercase_nested_path = str(nested_dir).replace("CasedDir", "caseddir")

    # This should trigger the assertion error in _git_toplevel
    try:
        res = parse(lowercase_nested_path, Configuration())
        # If we get here without assertion error, the bug is already fixed or not triggered
        print(f"Parse succeeded with result: {res}")
    except AssertionError as e:
        print(f"AssertionError caught as expected: {e}")
        # Re-raise so the test fails, showing we reproduced the bug
        raise


def test_write_to_absolute_path_passes_when_subdir_of_root(tmp_path: Path) -> None:
    c = Configuration(root=tmp_path, write_to=tmp_path / "VERSION.py")
    v = meta("1.0", config=c)
    from vcs_versioning._get_version_impl import write_version_files

    with pytest.warns(DeprecationWarning, match=".*write_to=.* is a absolute.*"):
        write_version_files(c, "1.0", v)
    write_version_files(replace(c, write_to="VERSION.py"), "1.0", v)
    subdir = tmp_path / "subdir"
    subdir.mkdir()
    with pytest.warns(UserWarning, match=r"resolves outside of"):
        write_version_files(replace(c, root=subdir), "1.0", v)


@pytest.mark.issue(468)
@pytest.mark.parametrize("scm", ["git", "hg"])
def test_warn_when_version_file_tracked(tmp_path: Path, scm: str) -> None:
    """Writing a version file that is tracked should emit a warning."""
    wd = WorkDir(tmp_path)
    if scm == "git":
        wd.setup_git()
    else:
        wd.setup_hg()
    version_file = tmp_path / "_version.py"
    version_file.write_text("__version__ = '0.0.0'\n")
    wd.add_and_commit("add tracked version file")

    c = Configuration(root=tmp_path, write_to="_version.py")
    v = meta("1.0", config=c)

    from vcs_versioning._get_version_impl import write_version_files

    with pytest.warns(UserWarning, match="tracked by version control"):
        write_version_files(c, "1.0", v)


@pytest.mark.issue(468)
@pytest.mark.parametrize("scm", ["git", "hg"])
def test_no_warn_when_version_file_not_tracked(tmp_path: Path, scm: str) -> None:
    """An untracked version file should not trigger the warning."""
    wd = WorkDir(tmp_path)
    if scm == "git":
        wd.setup_git()
    else:
        wd.setup_hg()
    wd.write("dummy.txt", "hello")
    wd.add_and_commit("initial commit")

    c = Configuration(root=tmp_path, write_to="_version.py")
    v = meta("1.0", config=c)

    from vcs_versioning._get_version_impl import write_version_files

    with warnings.catch_warnings(record=True) as caught:
        warnings.simplefilter("always")
        write_version_files(c, "1.0", v)
    tracked_warnings = [
        w for w in caught if "tracked by version control" in str(w.message)
    ]
    assert tracked_warnings == []


@pytest.mark.issue(1451)
def test_warn_if_tracked_relative_target_no_crash(tmp_path: Path) -> None:
    """_warn_if_tracked must not crash when target is relative and root is absolute."""
    from vcs_versioning._get_version_impl import _warn_if_tracked

    wd = WorkDir(tmp_path).setup_git()
    version_file = tmp_path / "pkg" / "__version__.py"
    version_file.parent.mkdir()
    version_file.write_text("__version__ = '0.0.0'\n")
    wd.add_and_commit("add tracked version file")

    c = Configuration(root=tmp_path)
    relative_target = Path("pkg/__version__.py")

    with pytest.warns(UserWarning, match="tracked by version control"):
        _warn_if_tracked(relative_target, tmp_path, c)


@pytest.mark.issue(1451)
def test_warn_if_tracked_warns_when_target_escapes_root(tmp_path: Path) -> None:
    """_warn_if_tracked must warn when target resolves outside root."""
    from vcs_versioning._get_version_impl import _warn_if_tracked

    project = tmp_path / "project"
    project.mkdir()
    WorkDir(project).setup_git()

    c = Configuration(root=project)
    escaping_target = Path("../../etc/passwd")

    with pytest.warns(UserWarning, match="resolves outside of"):
        _warn_if_tracked(escaping_target, project, c)


@pytest.mark.issue(1423)
def test_parse_version_importable_from_get_version_impl() -> None:
    """setuptools-scm <=10.0.x imports parse_version from this module."""
    from vcs_versioning._get_version_impl import parse_version

    assert callable(parse_version)


@pytest.mark.issue(1423)
def test_parse_version_shim_returns_version(tmp_path: Path) -> None:
    """The compat shim should resolve and return a ScmVersion."""
    wd = WorkDir(tmp_path).setup_git()
    wd.add_and_commit("initial")
    wd("git tag -a v1.0.0 -m v1.0.0")

    c = Configuration(root=tmp_path)

    from vcs_versioning._get_version_impl import parse_version
    from vcs_versioning._scm_version import ScmVersion

    result = parse_version(c)
    assert isinstance(result, ScmVersion)


@pytest.mark.parametrize(
    ("input", "expected"),
    [
        ("1.0", (1, 0)),
        ("1.0a2", (1, 0, "a2")),
        ("1.0.b2dev1", (1, 0, "b2", "dev1")),
        ("1.0.dev1", (1, 0, "dev1")),
    ],
)
def test_version_as_tuple(input: str, expected: Sequence[int | str]) -> None:
    from vcs_versioning._version_cls import _version_as_tuple

    assert _version_as_tuple(input) == expected
