import subprocess
from unittest.mock import MagicMock, patch
from PySide6.QtWidgets import QDialog, QComboBox
from src.views.app import App
import pytest


@pytest.fixture(autouse=True)
def suppress_qprogess_dialog():
    """
    Remove tedious dialogs popping up during tests
    """
    with patch("src.utils.tool_card.docker_controller.QProgressDialog"):
        yield


def test_stop_one_container(qtbot, tools, subprocess_mock):
    subprocess_run, _, _ = subprocess_mock

    def fake_subprocess_run(*args, **kwargs):
        mock_result = MagicMock()
        arguments = args[0]
        if len(arguments) > 1 and arguments[1] == 'ps':
            mock_result.stdout = tools[0]["docker_image"].encode()
        return mock_result

    subprocess_run.side_effect = fake_subprocess_run

    widget = App()
    qtbot.addWidget(widget)
    docker_tools = widget.tool_list.topLevelItem(0)
    first_item = docker_tools.child(0)  # type: ignore
    widget.tool_list.setCurrentItem(first_item)
    tool_display = widget.tool_display
    card = tool_display.currentWidget()
    card_layout = card.layout()  # type: ignore
    buttons_layout = card_layout.itemAt(3)  # type: ignore
    stop_button = buttons_layout.itemAt(1).widget()  # type: ignore
    stop_button.click()

    subprocess_run.assert_called_with(
        ['docker', 'stop', tools[0]["docker_image"]],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        check=True)


@patch("src.utils.tool_card.docker_controller.QVBoxLayout", autospec=True)
def test_stop_multiple_containers(_, qtbot, tools, subprocess_mock):
    subprocess_run, _, _ = subprocess_mock

    def fake_subprocess_run(*args, **kwargs):
        mock_result = MagicMock()
        arguments = args[0]
        if len(arguments) > 1 and arguments[1] == 'ps':
            mock_result.stdout = (tools[0]["docker_image"].encode() +
                                  b"\nWhatever")
        return mock_result

    subprocess_run.side_effect = fake_subprocess_run

    widget = App()
    qtbot.addWidget(widget)
    docker_tools = widget.tool_list.topLevelItem(0)
    first_item = docker_tools.child(0)  # type: ignore
    widget.tool_list.setCurrentItem(first_item)
    tool_display = widget.tool_display
    card = tool_display.currentWidget()
    card_layout = card.layout()  # type: ignore
    buttons_layout = card_layout.itemAt(3)  # type: ignore
    stop_button = buttons_layout.itemAt(1).widget()  # type: ignore
    with patch(
            "src.utils.tool_card.docker_controller.QDialog") as mock_dialog:
        instance_mock = MagicMock(spec=QDialog)
        instance_mock.result.return_value = QDialog.DialogCode.Accepted
        mock_dialog.return_value = instance_mock
        mock_dialog.DialogCode.Accepted = QDialog.DialogCode.Accepted
        with patch("src.utils.tool_card.docker_controller.QComboBox",
                   spec=QComboBox) as mock_combo_box:
            instance_mock = MagicMock(spec=QComboBox)
            instance_mock.currentText.return_value = tools[0]["docker_image"]
            mock_combo_box.return_value = instance_mock
            stop_button.click()

            subprocess_run.assert_called_with(
                ['docker', 'stop', tools[0]["docker_image"]],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                check=True)


@patch("src.utils.tool_card.docker_controller.QHBoxLayout", autospec=True)
@patch("src.utils.tool_card.docker_controller.QVBoxLayout", autospec=True)
def test_stop_all_containers(_, hbox_mock, qtbot, tools, subprocess_mock):
    subprocess_run, _, _ = subprocess_mock
    hbox_instance_mock = MagicMock()
    hbox_mock.return_value = hbox_instance_mock

    buttons = []

    def fake_add_widget(widget):
        buttons.append(widget)

    hbox_instance_mock.addWidget.side_effect = fake_add_widget

    def fake_subprocess_run(*args, **kwargs):
        mock_result = MagicMock()
        arguments = args[0]
        if len(arguments) > 1 and arguments[1] == 'ps':
            mock_result.stdout = (b"Whatever1" + b"\nWhatever")
        return mock_result

    subprocess_run.side_effect = fake_subprocess_run

    widget = App()
    qtbot.addWidget(widget)
    docker_tools = widget.tool_list.topLevelItem(0)
    first_item = docker_tools.child(0)  # type: ignore
    widget.tool_list.setCurrentItem(first_item)
    tool_display = widget.tool_display
    card = tool_display.currentWidget()
    card_layout = card.layout()  # type: ignore
    buttons_layout = card_layout.itemAt(3)  # type: ignore
    stop_button = buttons_layout.itemAt(1).widget()  # type: ignore
    with patch(
            "src.utils.tool_card.docker_controller.QDialog") as mock_dialog:
        instance_mock = MagicMock(spec=QDialog)
        instance_mock.result.return_value = QDialog.DialogCode.Accepted
        mock_dialog.return_value = instance_mock
        mock_dialog.DialogCode.Accepted = QDialog.DialogCode.Accepted
        stop_button.click()
        assert len(buttons) == 3
        stop_all_button = buttons[0]
        stop_all_button.click()

        print(subprocess_run.call_args_list)
        subprocess_run.assert_any_call(['docker', 'stop', "Whatever1"],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       check=True)

        subprocess_run.assert_any_call(['docker', 'stop', "Whatever"],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       check=True)
