"""Tests for the Terraform skill."""

from unittest.mock import Mock, patch

import pytest
from oclawma.skills import SkillMetadata

from oclawma_skill_terraform import TerraformSkill


@pytest.fixture
def metadata():
    """Create test metadata."""
    return SkillMetadata(
        name="terraform",
        version="1.0.0",
        description="Test skill",
        author="Test",
        entry_point="test"
    )


@pytest.fixture
def skill(metadata):
    """Create a test skill instance."""
    return TerraformSkill(metadata)


class TestTerraformSkill:
    """Test suite for TerraformSkill."""

    def test_initialization(self, skill, metadata):
        """Test skill initialization."""
        assert skill.metadata == metadata
        assert skill._terraform_path is None

    @patch("subprocess.run")
    def test_find_binary_success(self, mock_run, skill):
        """Test finding binary when it exists."""
        mock_run.return_value = Mock(returncode=0, stdout="/usr/bin/terraform\n")
        result = skill._find_binary("terraform")
        assert result == "/usr/bin/terraform"

    @patch("subprocess.run")
    def test_find_binary_not_found(self, mock_run, skill):
        """Test finding binary when it doesn't exist."""
        mock_run.return_value = Mock(returncode=1, stdout="")
        result = skill._find_binary("notfound")
        assert result is None

    @patch("subprocess.run")
    def test_get_terraform_caches_result(self, mock_run, skill):
        """Test terraform path is cached."""
        mock_run.return_value = Mock(returncode=0, stdout="/usr/bin/terraform\n")

        # First call
        result1 = skill._get_terraform()
        assert result1 == "/usr/bin/terraform"

        # Second call should not run subprocess again
        result2 = skill._get_terraform()
        assert result2 == "/usr/bin/terraform"
        assert mock_run.call_count == 1

    @patch("subprocess.run")
    def test_get_terraform_not_found(self, mock_run, skill):
        """Test error when terraform not found."""
        mock_run.return_value = Mock(returncode=1, stdout="")

        from oclawma.skills import SkillLoadError
        with pytest.raises(SkillLoadError):
            skill._get_terraform()

    @patch("subprocess.run")
    def test_run_terraform_success(self, mock_run, skill):
        """Test successful terraform command execution."""
        mock_run.return_value = Mock(
            returncode=0,
            stdout="Terraform initialized successfully!",
            stderr=""
        )

        with patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = skill._run_terraform(["init"])

        assert result["success"] is True
        assert "initialized" in result["output"]
        assert result["exit_code"] == 0

    @patch("subprocess.run")
    def test_run_terraform_plan_exit_code_2(self, mock_run, skill):
        """Test plan command with exit code 2 (changes present)."""
        mock_run.return_value = Mock(
            returncode=2,
            stdout="Plan: 2 to add, 1 to change, 0 to destroy.",
            stderr=""
        )

        with patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = skill._run_terraform(["plan"])

        # Exit code 2 for plan should still be success
        assert result["success"] is True

    @patch("subprocess.run")
    def test_run_terraform_failure(self, mock_run, skill):
        """Test failed terraform command."""
        mock_run.return_value = Mock(
            returncode=1,
            stdout="",
            stderr="Error: Invalid configuration"
        )

        with patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = skill._run_terraform(["apply"])

        assert result["success"] is False
        assert "Invalid configuration" in result["output"]

    @patch("subprocess.run")
    def test_run_terraform_timeout(self, mock_run, skill):
        """Test terraform command timeout."""
        mock_run.side_effect = subprocess.TimeoutExpired(cmd="terraform", timeout=300)

        with patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = skill._run_terraform(["apply"], timeout=300)

        assert result["success"] is False
        assert "timed out" in result["error"]

    def test_build_var_args(self, skill):
        """Test building variable arguments."""
        vars = {
            "region": "us-east-1",
            "instance_type": "t2.micro",
            "tags": {"env": "prod"}
        }

        args = skill._build_var_args(vars)

        assert "-var" in args
        assert 'region=us-east-1' in args
        assert 'instance_type=t2.micro' in args
        assert '-var' in args

    @pytest.mark.asyncio
    async def test_init(self, skill):
        """Test init tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Terraform initialized!",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._init(path=".", backend=True, upgrade=False)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_validate(self, skill):
        """Test validate tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Success! Configuration is valid.",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._validate(path=".")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_plan(self, skill):
        """Test plan tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Plan: 2 to add, 0 to change, 0 to destroy.",
            "exit_code": 2
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._plan(
                path=".",
                vars={"region": "us-east-1"},
                detailed_exitcode=True
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_apply(self, skill):
        """Test apply tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Apply complete! Resources: 2 added.",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._apply(
                path=".",
                auto_approve=True,
                vars={"region": "us-east-1"}
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_destroy(self, skill):
        """Test destroy tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Destroy complete! Resources: 2 destroyed.",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._destroy(
                path=".",
                auto_approve=True
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_fmt(self, skill):
        """Test fmt tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "main.tf",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._fmt(path=".", recursive=True)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_state_list(self, skill):
        """Test state_list tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "aws_instance.web\naws_s3_bucket.data",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._state_list(path=".")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_state_show(self, skill):
        """Test state_show tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "# aws_instance.web:",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._state_show(
                address="aws_instance.web",
                path="."
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_import(self, skill):
        """Test import tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Import successful!",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._import(
                address="aws_instance.web",
                id="i-1234567890",
                path="."
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_output(self, skill):
        """Test output tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": '{"instance_ip": {"value": "1.2.3.4"}}',
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._output(path=".", json=True)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_workspace_list(self, skill):
        """Test workspace_list tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "* default\n  dev\n  prod",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._workspace_list(path=".")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_workspace_select(self, skill):
        """Test workspace_select tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Switched to workspace \"prod\".",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._workspace_select(name="prod", path=".")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_version(self, skill):
        """Test version tool."""
        with patch.object(skill, "_run_terraform", return_value={
            "success": True,
            "output": "Terraform v1.6.0",
            "exit_code": 0
        }), patch.object(skill, "_get_terraform", return_value="/usr/bin/terraform"):
            result = await skill._version()

        assert result["success"] is True

    def test_load_registers_tools(self, skill):
        """Test that _load registers all expected tools."""
        skill._load()

        expected_tools = [
            "init",
            "validate",
            "plan",
            "apply",
            "destroy",
            "fmt",
            "show",
            "state_list",
            "state_show",
            "state_mv",
            "state_rm",
            "import",
            "taint",
            "untaint",
            "output",
            "refresh",
            "workspace_list",
            "workspace_select",
            "workspace_new",
            "workspace_delete",
            "providers",
            "version",
        ]

        for tool in expected_tools:
            assert tool in skill._tools, f"Tool {tool} not registered"
