import os
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from vrobbler import context_processors
from vrobbler.context_processors import version_info


@pytest.fixture(autouse=True)
def reset_git_cache():
    context_processors._GIT_COMMIT = None


@pytest.fixture
def mock_request():
    return MagicMock()


class TestVersionInfo:
    def test_returns_version_and_commit(self, mock_request):
        """Test that git commit is returned when _commit.py doesn't exist"""
        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch(
                "vrobbler.context_processors.subprocess.check_output"
            ) as mock_check_output,
        ):
            mock_get_version.return_value = "1.0.0"
            mock_check_output.return_value = b"abc1234"

            # Mock the import to raise ImportError so git is used
            import builtins

            original_import = builtins.__import__

            def mock_import(name, *args, **kwargs):
                if name == "vrobbler._commit":
                    raise ImportError("No module named 'vrobbler._commit'")
                return original_import(name, *args, **kwargs)

            with patch("builtins.__import__", side_effect=mock_import):
                result = version_info(mock_request)

            assert result["app_version"] == "1.0.0"
            assert result["git_commit"] == "abc1234"

    def test_uses_env_commit_if_set(self, mock_request):
        with (
            patch.dict(os.environ, {"VROBBLER_COMMIT": "env_commit_hash"}),
            patch("vrobbler.context_processors.get_version") as mock_get_version,
        ):
            mock_get_version.return_value = "1.0.0"

            result = version_info(mock_request)

            assert result["git_commit"] == "env_commit_hash"

    def test_uses_commit_from_module_when_available(self, mock_request):
        """Test that commit from _commit.py module is used when available"""
        with (patch("vrobbler.context_processors.get_version") as mock_get_version,):
            mock_get_version.return_value = "1.0.0"

            result = version_info(mock_request)

            # Should use whatever value is in vrobbler/_commit.py
            # Could be "unknown" or an actual commit hash
            assert "git_commit" in result
            assert result["git_commit"] != ""

    def test_uses_commit_from_file_when_module_unavailable(self, mock_request):
        """Test that commit from /var/lib/vrobbler/commit.txt is used"""
        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch("pathlib.Path.exists", return_value=True),
            patch("pathlib.Path.read_text", return_value="file_commit_hash"),
        ):
            mock_get_version.return_value = "1.0.0"

            # Mock the import to raise ImportError
            import builtins

            original_import = builtins.__import__

            def mock_import(name, *args, **kwargs):
                if name == "vrobbler._commit":
                    raise ImportError("No module named 'vrobbler._commit'")
                return original_import(name, *args, **kwargs)

            with patch("builtins.__import__", side_effect=mock_import):
                result = version_info(mock_request)

            assert result["git_commit"] == "file_commit_hash"

    def test_falls_back_to_git_when_file_unavailable(self, mock_request):
        """Test fallback to git when _commit.py and file don't exist"""
        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch("pathlib.Path.exists", return_value=False),
            patch(
                "vrobbler.context_processors.subprocess.check_output"
            ) as mock_check_output,
        ):
            mock_get_version.return_value = "1.0.0"
            mock_check_output.return_value = b"git_commit_hash"

            # Mock the import to raise ImportError
            import builtins

            original_import = builtins.__import__

            def mock_import(name, *args, **kwargs):
                if name == "vrobbler._commit":
                    raise ImportError("No module named 'vrobbler._commit'")
                return original_import(name, *args, **kwargs)

            with patch("builtins.__import__", side_effect=mock_import):
                result = version_info(mock_request)

            assert result["git_commit"] == "git_commit_hash"

    def test_returns_unknown_when_version_fails(self, mock_request):
        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch(
                "vrobbler.context_processors.subprocess.check_output"
            ) as mock_check_output,
        ):
            mock_get_version.side_effect = Exception("not found")
            mock_check_output.return_value = b"abc1234"

            result = version_info(mock_request)

            assert result["app_version"] == "unknown"

    def test_returns_unknown_when_git_fails(self, mock_request):
        import subprocess

        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch(
                "vrobbler.context_processors.subprocess.check_output"
            ) as mock_check_output,
        ):
            mock_get_version.return_value = "1.0.0"
            mock_check_output.side_effect = subprocess.SubprocessError()

            result = version_info(mock_request)

            assert result["git_commit"] == "unknown"

    def test_returns_unknown_when_git_not_found(self, mock_request):
        import subprocess

        with (
            patch("vrobbler.context_processors.get_version") as mock_get_version,
            patch(
                "vrobbler.context_processors.subprocess.check_output"
            ) as mock_check_output,
        ):
            mock_get_version.return_value = "1.0.0"
            mock_check_output.side_effect = FileNotFoundError()

            result = version_info(mock_request)

            assert result["git_commit"] == "unknown"
