"""Tests for the Docker skill."""

from unittest.mock import Mock, patch

import pytest
from oclawma.skills import SkillMetadata

from oclawma_skill_docker import DockerSkill


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


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


class TestDockerSkill:
    """Test suite for DockerSkill."""

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

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

        # First call
        result1 = skill._get_docker()
        assert result1 == "/usr/bin/docker"

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

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

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

    @patch("subprocess.run")
    def test_run_docker_success(self, mock_run, skill):
        """Test successful docker command execution."""
        mock_run.return_value = Mock(
            returncode=0,
            stdout="CONTAINER ID   IMAGE\nabc123   nginx",
            stderr=""
        )

        with patch.object(skill, "_get_docker", return_value="/usr/bin/docker"):
            result = skill._run_docker(["ps"])

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

    @patch("subprocess.run")
    def test_run_docker_failure(self, mock_run, skill):
        """Test failed docker command."""
        mock_run.return_value = Mock(
            returncode=1,
            stdout="",
            stderr="Error: No such container"
        )

        with patch.object(skill, "_get_docker", return_value="/usr/bin/docker"):
            result = skill._run_docker(["logs", "nonexistent"])

        assert result["success"] is False
        assert "No such container" in result["output"]

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

        with patch.object(skill, "_get_docker", return_value="/usr/bin/docker"):
            result = skill._run_docker(["pull", "large-image"], timeout=60)

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

    @pytest.mark.asyncio
    async def test_ps(self, skill):
        """Test ps tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "CONTAINER ID   IMAGE\nabc123   nginx",
            "exit_code": 0
        }):
            result = await skill._ps(all=True)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_run(self, skill):
        """Test run tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "container_id_123",
            "exit_code": 0
        }):
            result = await skill._run(
                image="nginx:latest",
                name="test-nginx",
                ports=["8080:80"],
                detach=True
            )

        assert result["success"] is True

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

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_stop(self, skill):
        """Test stop tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "my-container",
            "exit_code": 0
        }):
            result = await skill._stop(container="my-container", timeout=10)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_images(self, skill):
        """Test images tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "REPOSITORY   TAG\nnginx   latest",
            "exit_code": 0
        }):
            result = await skill._images(all=True)

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_pull(self, skill):
        """Test pull tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "Pulling from library/nginx",
            "exit_code": 0
        }):
            result = await skill._pull(image="nginx:latest")

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_network_create(self, skill):
        """Test network_create tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "network_id_123",
            "exit_code": 0
        }):
            result = await skill._network_create(
                name="test-network",
                driver="bridge"
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_volume_create(self, skill):
        """Test volume_create tool."""
        with patch.object(skill, "_run_docker", return_value={
            "success": True,
            "output": "test-volume",
            "exit_code": 0
        }):
            result = await skill._volume_create(
                name="test-volume",
                driver="local"
            )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_compose_up(self, skill):
        """Test compose_up tool."""
        with patch.object(skill, "_get_compose", return_value="/usr/bin/docker-compose"):
            with patch("subprocess.run") as mock_run:
                mock_run.return_value = Mock(
                    returncode=0,
                    stdout="Starting services...",
                    stderr=""
                )
                result = await skill._compose_up(
                    file="docker-compose.yml",
                    detach=True
                )

        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_compose_down(self, skill):
        """Test compose_down tool."""
        with patch.object(skill, "_get_compose", return_value="/usr/bin/docker-compose"):
            with patch("subprocess.run") as mock_run:
                mock_run.return_value = Mock(
                    returncode=0,
                    stdout="Removing containers...",
                    stderr=""
                )
                result = await skill._compose_down(
                    file="docker-compose.yml",
                    volumes=True
                )

        assert result["success"] is True

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

        expected_tools = [
            "run",
            "ps",
            "stop",
            "start",
            "restart",
            "rm",
            "logs",
            "exec",
            "inspect",
            "stats",
            "images",
            "pull",
            "build",
            "rmi",
            "network_ls",
            "network_create",
            "network_rm",
            "volume_ls",
            "volume_create",
            "volume_rm",
            "system_prune",
            "compose_up",
            "compose_down",
        ]

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