"""Tests for command executor."""

from __future__ import annotations

import pytest

from curadise_agent.executor.validator import CommandValidator
from curadise_agent.models.api.command import CommandRequest, CommandStatus, CommandType


class TestCommandValidator:
    """Tests for CommandValidator."""

    @pytest.fixture
    def validator(self) -> CommandValidator:
        return CommandValidator(
            allowlist=["echo *", "ls *", "cat **"],
            denylist=["rm *", "rm -rf *"],
            require_signature=False,
        )

    def test_allowed_command(self, validator: CommandValidator) -> None:
        """Test that allowed commands pass validation."""
        command = CommandRequest(
            id="cmd-1",
            command="echo",
            args=["hello"],
        )

        result = validator.validate(command)
        assert result.valid

    def test_denied_command(self, validator: CommandValidator) -> None:
        """Test that denied commands are rejected."""
        command = CommandRequest(
            id="cmd-1",
            command="rm",
            args=["-rf", "/"],
        )

        result = validator.validate(command)
        assert not result.valid
        assert "denylist" in result.reason.lower()

    def test_unlisted_command_rejected(self, validator: CommandValidator) -> None:
        """Test that commands not in allowlist are rejected."""
        command = CommandRequest(
            id="cmd-1",
            command="wget",
            args=["http://example.com"],
        )

        result = validator.validate(command)
        assert not result.valid
        assert "allowlist" in result.reason.lower()

    def test_denylist_takes_precedence(self) -> None:
        """Test that denylist overrides allowlist."""
        validator = CommandValidator(
            allowlist=["rm *"],  # Would allow rm
            denylist=["rm -rf *"],  # But this denies rm -rf
        )

        # Regular rm should be allowed
        cmd1 = CommandRequest(id="cmd-1", command="rm", args=["file.txt"])
        assert validator.validate(cmd1).valid

        # rm -rf should be denied
        cmd2 = CommandRequest(id="cmd-2", command="rm", args=["-rf", "dir"])
        assert not validator.validate(cmd2).valid

    def test_empty_allowlist_allows_all(self) -> None:
        """Test that empty allowlist allows all (except denylist)."""
        validator = CommandValidator(
            allowlist=[],
            denylist=["rm *"],
        )

        cmd1 = CommandRequest(id="cmd-1", command="echo", args=["hello"])
        assert validator.validate(cmd1).valid

        cmd2 = CommandRequest(id="cmd-2", command="rm", args=["file"])
        assert not validator.validate(cmd2).valid

    def test_command_length_limit(self, validator: CommandValidator) -> None:
        """Test command length validation."""
        validator.max_command_length = 50

        command = CommandRequest(
            id="cmd-1",
            command="echo",
            args=["a" * 100],
        )

        result = validator.validate(command)
        assert not result.valid
        assert "length" in result.reason.lower()

    def test_wildcard_patterns(self) -> None:
        """Test glob wildcard pattern matching."""
        validator = CommandValidator(
            allowlist=[
                "systemctl * nginx",  # Single wildcard
                "cat /var/log/**",  # Double wildcard
            ],
            denylist=[],
        )

        # Single wildcard match
        cmd1 = CommandRequest(id="cmd-1", command="systemctl", args=["restart", "nginx"])
        assert validator.validate(cmd1).valid

        # Double wildcard match (matches paths with slashes)
        cmd2 = CommandRequest(id="cmd-2", command="cat", args=["/var/log/nginx/access.log"])
        assert validator.validate(cmd2).valid


class TestCommandRequest:
    """Tests for CommandRequest model."""

    def test_from_dict(self) -> None:
        """Test creating command from dictionary."""
        data = {
            "id": "cmd-123",
            "command": "echo",
            "args": ["hello", "world"],
            "timeout": 60.0,
            "command_type": "shell",
        }

        command = CommandRequest.from_dict(data)

        assert command.id == "cmd-123"
        assert command.command == "echo"
        assert command.args == ["hello", "world"]
        assert command.timeout == 60.0
        assert command.command_type == CommandType.SHELL

    def test_to_dict(self, sample_command: CommandRequest) -> None:
        """Test converting command to dictionary."""
        data = sample_command.to_dict()

        assert data["id"] == sample_command.id
        assert data["command"] == sample_command.command
        assert data["args"] == sample_command.args

    def test_signature_data(self, sample_command: CommandRequest) -> None:
        """Test generating signature data."""
        sig_data = sample_command.get_signature_data()

        assert isinstance(sig_data, bytes)
        assert sample_command.id.encode() in sig_data
        assert sample_command.command.encode() in sig_data


class TestCommandResponse:
    """Tests for CommandResponse model."""

    def test_success_property(self) -> None:
        """Test success property logic."""
        from curadise_agent.models.api.command import CommandResponse

        # Completed with exit 0 = success
        resp1 = CommandResponse(
            command_id="cmd-1",
            status=CommandStatus.COMPLETED,
            exit_code=0,
        )
        assert resp1.success

        # Completed with non-zero = failure
        resp2 = CommandResponse(
            command_id="cmd-2",
            status=CommandStatus.COMPLETED,
            exit_code=1,
        )
        assert not resp2.success

        # Failed status = failure
        resp3 = CommandResponse(
            command_id="cmd-3",
            status=CommandStatus.FAILED,
        )
        assert not resp3.success

    def test_rejected_factory(self) -> None:
        """Test rejected response factory."""
        from curadise_agent.models.api.command import CommandResponse

        resp = CommandResponse.rejected("cmd-1", "Not allowed")

        assert resp.status == CommandStatus.REJECTED
        assert resp.error == "Not allowed"

    def test_timeout_factory(self) -> None:
        """Test timeout response factory."""
        from datetime import UTC, datetime

        from curadise_agent.models.api.command import CommandResponse

        started = datetime.now(UTC)
        resp = CommandResponse.timeout("cmd-1", started, "partial output")

        assert resp.status == CommandStatus.TIMEOUT
        assert resp.stdout == "partial output"
        assert resp.truncated
