import logging
import pytest
import time

from runner_pjpawel.command.base import (
    BaseCommand, CommandResultLevel, RunnerRuntimeError,
)

from runner_pjpawel.command.common import ShellCommand, GroupCommand, ParallelCommand
from tests.test_command.util import RuntimeExceptionCommand, get_stdout_logger


class MockCommand(BaseCommand):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.executed = False
        self.execution_order = None
        self.delay = kwargs.get('delay', 0)

    def _do_work(self) -> None:
        time.sleep(self.delay)
        self.executed = True
        if hasattr(MockCommand, '_counter'):
            MockCommand._counter.append(self)
        return None


def test_shell_command_uname():
    shell = ShellCommand("uname")
    res = shell.process()

    assert res.level == CommandResultLevel.OK
    assert res.msg == "Linux\n"
    assert res.additional_info == [""]


def test_runtime_error_thrown():
    cmd = RuntimeExceptionCommand()
    with pytest.raises(RunnerRuntimeError):
        cmd.process()

def test_group_command_single():
    logger_name = "tgc"
    get_stdout_logger(logger_name, logging.DEBUG)
    group = GroupCommand(logger_name=logger_name, log_level=logging.DEBUG)
    c1 = ShellCommand("uname -a", logger_name=logger_name, log_level=logging.DEBUG)
    group.add_command(c1)

    rs = group.process()
    assert rs.level == CommandResultLevel.OK


def test_group_command():
    logger_name = "tgc"
    get_stdout_logger(logger_name, logging.DEBUG)
    group = GroupCommand(logger_name=logger_name, log_level=logging.DEBUG)
    c1 = ShellCommand("uname -a", logger_name=logger_name, log_level=logging.DEBUG)
    group.add_command(c1)
    c2 = ShellCommand("ls -al", logger_name=logger_name, log_level=logging.DEBUG)
    group.add_command(c2)

    rs = group.process()
    assert rs.level == CommandResultLevel.OK


def test_group_command_restart():
    group = GroupCommand(error_strategy="restart")
    c1 = RuntimeExceptionCommand()
    group.add_command(c1)
    c2 = ShellCommand("ls -al")
    group.add_command(c2)

    rs = group.process()
    assert rs.level == CommandResultLevel.OK


def test_parallel_command_simple():
    """Test basic parallel execution of two simple commands."""
    pc = ParallelCommand([ShellCommand("uname"), ShellCommand("pwd")])
    result = pc.process()
    assert result.level == CommandResultLevel.OK


def test_parallel_command_single():
    """Test parallel execution with a single command."""
    pc = ParallelCommand([ShellCommand("uname")])
    result = pc.process()
    assert result.level == CommandResultLevel.OK


def test_parallel_command_empty():
    """Test parallel execution with no commands."""
    pc = ParallelCommand([])
    result = pc.process()
    assert result.level == CommandResultLevel.OK


def test_parallel_command_with_error():
    """Test parallel execution where one command fails."""
    pc = ParallelCommand([
        ShellCommand("uname"),
        ShellCommand("false"),  # Command that exits with 1
    ])
    result = pc.process()
    # ParallelCommand doesn't collect results from threads, so no error in main thread
    assert result.level == CommandResultLevel.OK


def test_parallel_command_with_runtime_error():
    """Test parallel execution including a command that raises RuntimeException."""
    pc = ParallelCommand([
        ShellCommand("uname"),
        RuntimeExceptionCommand()
    ])
    result = pc.process()
    # Since exceptions are in threads, main thread doesn't know
    assert result.level == CommandResultLevel.OK


def test_parallel_command_multiple():
    """Test parallel execution with multiple commands."""
    commands = [
        ShellCommand("uname"),
        ShellCommand("pwd"),
        ShellCommand("echo 'test'"),
    ]
    pc = ParallelCommand(commands)
    result = pc.process()
    assert result.level == CommandResultLevel.OK
