"""Tests for complyform.core.iac_binary."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest

from complyform.core.errors import IaCBinaryNotFoundError
from complyform.core.iac_binary import (
    _reset_cache,
    resolve_iac_binary,
)


@pytest.fixture(autouse=True)
def _clear_cache() -> None:
    """Reset module-level cache before each test."""
    _reset_cache()


class TestResolveTofu:
    def test_tofu_preferred_over_terraform(self) -> None:
        mock_result = MagicMock()
        mock_result.stdout = "OpenTofu v1.8.0\n"

        with (
            patch("shutil.which", return_value="/usr/bin/tofu"),
            patch("subprocess.run", return_value=mock_result),
            patch.dict("os.environ", {}, clear=True),
        ):
            binary = resolve_iac_binary()
            assert binary.name == "tofu"
            assert binary.version == "1.8.0"
            assert binary.path == "/usr/bin/tofu"


class TestResolveTerraform:
    def test_terraform_fallback(self) -> None:
        mock_result = MagicMock()
        mock_result.stdout = "Terraform v1.9.0\n"

        def which_side_effect(name: str) -> str | None:
            if name == "tofu":
                return None
            return "/usr/bin/terraform"

        with (
            patch("shutil.which", side_effect=which_side_effect),
            patch("subprocess.run", return_value=mock_result),
            patch.dict("os.environ", {}, clear=True),
        ):
            binary = resolve_iac_binary()
            assert binary.name == "terraform"
            assert binary.version == "1.9.0"


class TestEnvOverride:
    def test_env_var_takes_precedence(self) -> None:
        mock_result = MagicMock()
        mock_result.stdout = "OpenTofu v1.7.0\n"

        with (
            patch(
                "shutil.which",
                return_value="/custom/path/tofu",
            ),
            patch("subprocess.run", return_value=mock_result),
            patch.dict(
                "os.environ",
                {"COMPLYFORM_IAC_BINARY": "/custom/path/tofu"},
            ),
        ):
            binary = resolve_iac_binary()
            assert binary.path == "/custom/path/tofu"


class TestNotFound:
    def test_raises_when_nothing_found(self) -> None:
        with (
            patch("shutil.which", return_value=None),
            patch.dict("os.environ", {}, clear=True),
            pytest.raises(IaCBinaryNotFoundError),
        ):
            resolve_iac_binary()


class TestCaching:
    def test_second_call_returns_cached(self) -> None:
        mock_result = MagicMock()
        mock_result.stdout = "OpenTofu v1.8.0\n"

        with (
            patch("shutil.which", return_value="/usr/bin/tofu") as mock_which,
            patch("subprocess.run", return_value=mock_result),
            patch.dict("os.environ", {}, clear=True),
        ):
            first = resolve_iac_binary()
            second = resolve_iac_binary()
            assert first is second
            # shutil.which only called once (first invocation)
            assert mock_which.call_count == 1
