import subprocess
from unittest.mock import MagicMock
from PySide6.QtGui import QIcon
import pytest
from src.views.app import App
from src.path_resolver import PathResolver


def test_load_tools_list(qtbot, tools, subprocess_mock):
    widget = App()
    qtbot.addWidget(widget)
    docker_tools = widget.tool_list.topLevelItem(0)
    first_item = docker_tools.child(0)  # type: ignore
    second_item = docker_tools.child(1)  # type: ignore
    assert docker_tools.childCount() == 2  # type: ignore
    assert widget.tool_list.topLevelItemCount() == 1
    assert widget.tool_list.topLevelItem(0).text(  # type: ignore
        0) == "Docker Tools"
    assert first_item.text(0) == "Metasploit"  # type: ignore
    assert second_item.text(0) == "Nmap"  # type: ignore


@pytest.mark.parametrize(
    "click,website,size,version,exp_description,exp_examples,flags", [
        (
            True,
            'Website: <a href="https://nmap.org/">https://nmap.org/</a>',
            "Size: ~642 MB",
            "Version: 7.92",
            "Nmap",
            [
                "nmap -sP 192.168.1.0/24", "nmap -sS -p 80,443 192.168.1.1",
                "nmap -sV 192.168.1.1"
            ],
            "Enter flags (e.g., -p 80 192.168.0.1)",
        ),
        (
            False,
            'Website: <a href="https://www.metasploit.com/">https://www.metasploit.com/</a>',
            "Size: ~1.69 GB",
            "Version: 6.1.29",
            "Metasploit description",
            ["msfconsole -q\nuse exploit/multi/handler"],
            "",
        ),
    ])
def test_load_tools_details(
    qtbot,
    tools,
    subprocess_mock,
    click,
    website,
    size,
    version,
    exp_description,
    exp_examples,
    flags,
):
    widget = App()
    qtbot.addWidget(widget)
    docker_tools = widget.tool_list.topLevelItem(0)
    second_item = docker_tools.child(1)  # type: ignore
    if click:
        widget.tool_list.setCurrentItem(second_item)
    tool_display = widget.tool_display
    card = tool_display.currentWidget()
    card_layout = card.layout()  # type: ignore
    details_layout = card_layout.itemAt(0)  # type: ignore
    # Image layout
    _ = details_layout.itemAt(0)  # type: ignore # image layout
    info_layout = details_layout.itemAt(1)  # type: ignore
    website_label = info_layout.itemAt(0).itemAt(
        0).widget().text()  # type: ignore
    size_label = info_layout.itemAt(0).itemAt(
        1).widget().text()  # type: ignore
    version_label = info_layout.itemAt(0).itemAt(
        2).widget().text()  # type: ignore
    description = info_layout.itemAt(1).itemAt(0).widget()
    examples = card_layout.itemAt(2).widget()  # type: ignore
    assert website_label == website
    assert size_label == size
    assert version_label == version
    assert description.toPlainText() == exp_description
    if len(exp_examples) > 0:
        assert examples.toPlainText(  # type: ignore
        ) == "\n\n".join(exp_examples)
    items_count = 4
    if len(flags) > 0:
        flags_layout = card_layout.itemAt(3).widget()  # type: ignore
        assert flags_layout.placeholderText(  # type: ignore
        ) == flags
        items_count += 1
    assert card_layout.count() == items_count  # type: ignore


@pytest.fixture
def docker_running(tools):

    def fake_subprocess_run(*args, **kwargs):
        mock_result = MagicMock()
        if len(args[0]) < 4:
            return mock_result
        if args[0][1] == 'images' and args[0][3] == tools[0]["docker_image"]:
            mock_result.stdout = ""
            return mock_result
        elif args[0][1] == 'images' and args[0][3] == tools[1]["docker_image"]:
            mock_result.stdout = "123"
            return mock_result

        return mock_result

    return fake_subprocess_run


@pytest.fixture
def docker_not_running(tools):

    def fake_subprocess_run(*args, **kwargs):
        raise subprocess.CalledProcessError(-1, "docker")

    return fake_subprocess_run


@pytest.mark.parametrize("docker_mock,expected_icons", [
    ("docker_running", [
        "resources/assets/cross.png",
        "resources/assets/check.png",
    ]),
    ("docker_not_running", [
        None,
        None,
    ]),
])
def test_docker_images_icons(docker_mock, expected_icons, qtbot, tools,
                             subprocess_mock, request):
    subprocess_run, _, _ = subprocess_mock

    subprocess_run.side_effect = request.getfixturevalue(docker_mock)
    widget = App()
    qtbot.addWidget(widget)
    tool_list = widget.tool_list
    first = tool_list.topLevelItem(0).child(0)  # type: ignore
    icon = first.icon(0)
    assert_details_icon(icon, expected_icons[0])
    second = tool_list.topLevelItem(0).child(1)  # type: ignore
    icon = second.icon(0)
    assert_details_icon(icon, expected_icons[1])


def assert_details_icon(icon, expected_icon):
    pixmap = icon.pixmap(16, 16)
    if expected_icon is None:
        assert pixmap.isNull()
        return
    check = QIcon(PathResolver.resource_path(expected_icon))
    assert check.pixmap(16, 16).toImage() == pixmap.toImage()


@pytest.mark.skip("It always find None for the download_status_label")
def test_docker_fails_during_images(qtbot, tools, subprocess_mock):
    subprocess_run, _, _ = subprocess_mock
    subprocess_run.side_effect = [
        0, subprocess.CalledProcessError(-1, "docker")
    ]
    widget = App()
    qtbot.addWidget(widget)
    tool_list = widget.tool_list
    first = tool_list.topLevelItem(0).child(0)  # type: ignore
    icon = first.icon(0)
    pixmap = icon.pixmap(16, 16)
    expected = QIcon(PathResolver.resource_path('resources/assets/cross.png'))
    assert expected.pixmap(16, 16).toImage() == pixmap.toImage()
