"""Tests for the Kubernetes skill."""

from unittest.mock import Mock, patch

import pytest
from oclawma.skills import SkillMetadata

from oclawma_skill_kubernetes import KubernetesSkill


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


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


class TestKubernetesSkill:
    """Test suite for KubernetesSkill."""

    def test_initialization(self, skill, metadata):
        """Test skill initialization."""
        assert skill.metadata == metadata
        assert skill._kubectl_path is None
        assert skill._helm_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/kubectl\n")
        result = skill._find_binary("kubectl")
        assert result == "/usr/bin/kubectl"

    @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_kubectl_caches_result(self, mock_run, skill):
        """Test kubectl path is cached."""
        mock_run.return_value = Mock(returncode=0, stdout="/usr/bin/kubectl\n")

        # First call
        result1 = skill._get_kubectl()
        assert result1 == "/usr/bin/kubectl"

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

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

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

    @patch("subprocess.run")
    def test_run_kubectl_success(self, mock_run, skill):
        """Test successful kubectl command execution."""
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"items": []}',
            stderr=""
        )

        with patch.object(skill, "_get_kubectl", return_value="/usr/bin/kubectl"):
            result = skill._run_kubectl(["get", "pods"])

        assert result["success"] is True
        assert result["output"] == '{"items": []}'
        assert result["exit_code"] == 0

    @patch("subprocess.run")
    def test_run_kubectl_failure(self, mock_run, skill):
        """Test failed kubectl command."""
        mock_run.return_value = Mock(
            returncode=1,
            stdout="",
            stderr="Error: resource not found"
        )

        with patch.object(skill, "_get_kubectl", return_value="/usr/bin/kubectl"):
            result = skill._run_kubectl(["get", "pod", "nonexistent"])

        assert result["success"] is False
        assert "not found" in result["output"]

    @patch("subprocess.run")
    def test_run_kubectl_timeout(self, mock_run, skill):
        """Test kubectl command timeout."""
        mock_run.side_effect = subprocess.TimeoutExpired(cmd="kubectl", timeout=60)

        with patch.object(skill, "_get_kubectl", return_value="/usr/bin/kubectl"):
            result = skill._run_kubectl(["get", "pods"], timeout=60)

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

    @patch("subprocess.run")
    def test_run_helm_not_installed(self, mock_run, skill):
        """Test helm commands when helm is not installed."""
        mock_run.return_value = Mock(returncode=1, stdout="")

        result = skill._run_helm(["list"])

        assert result["success"] is False
        assert "Helm not found" in result["error"]

    @pytest.mark.asyncio
    async def test_kubectl_tool(self, skill):
        """Test kubectl wrapper tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": "node-1\nnode-2",
            "exit_code": 0
        }), patch.object(skill, "_get_kubectl", return_value="/usr/bin/kubectl"):
            result = await skill._kubectl("get nodes")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_get_pods(self, skill):
        """Test get_pods tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": '{"items": []}',
            "exit_code": 0
        }):
            result = await skill._get_pods(namespace="default")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_get_logs(self, skill):
        """Test get_logs tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": "log line 1\nlog line 2",
            "exit_code": 0
        }):
            result = await skill._get_logs(pod="my-pod", tail=50)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_scale_deployment(self, skill):
        """Test scale_deployment tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": "deployment.apps/my-app scaled",
            "exit_code": 0
        }):
            result = await skill._scale_deployment(
                name="my-app",
                replicas=3,
                namespace="default"
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_rollout_deployment_invalid_action(self, skill):
        """Test rollout_deployment with invalid action."""
        result = await skill._rollout_deployment(
            name="my-app",
            action="invalid"
        )

        assert result["success"] is False
        assert "Invalid action" in result["error"]

    @pytest.mark.asyncio
    async def test_helm_list(self, skill):
        """Test helm_list tool."""
        with patch.object(skill, "_get_helm", return_value="/usr/bin/helm"):
            with patch("subprocess.run") as mock_run:
                mock_run.return_value = Mock(
                    returncode=0,
                    stdout='[]',
                    stderr=""
                )
                result = await skill._helm_list()

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_get_contexts(self, skill):
        """Test get_contexts tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": '[{"name": "cluster-1"}]',
            "exit_code": 0
        }):
            result = await skill._get_contexts()

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_get_events(self, skill):
        """Test get_events tool."""
        with patch.object(skill, "_run_kubectl", return_value={
            "success": True,
            "output": '{"items": [{"message": "test"}]}',
            "exit_code": 0
        }):
            result = await skill._get_events(limit=10)

        assert result["success"] is True

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

        expected_tools = [
            "kubectl",
            "get_pods",
            "get_logs",
            "describe_pod",
            "exec_command",
            "get_deployments",
            "scale_deployment",
            "rollout_deployment",
            "apply_manifest",
            "delete_resource",
            "helm_list",
            "helm_install",
            "helm_upgrade",
            "helm_uninstall",
            "helm_status",
            "helm_get_values",
            "get_contexts",
            "set_context",
            "get_events",
            "top_pods",
            "top_nodes",
            "port_forward",
        ]

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