from pathlib import Path

import pytest
from mock import MagicMock
from pytest_mock import MockerFixture

from stubber.codemod.enrich import enrich_file, enrich_folder

# mark all tests
pytestmark = [pytest.mark.stubber, pytest.mark.codemod]


@pytest.mark.parametrize(
    "source_file, target_file, expected",
    [
        (
            Path("./tests/data/stub_merge/micropython-v1_18-docstubs/esp32.pyi"),
            Path("./tests/data/stub_merge/micropython-v1_18-esp32/esp32.pyi"),
            True,
        )
    ],
)
def test_enrich_file_with_stub(source_file: Path, target_file: Path, expected: bool):
    #    source_file = Path("./tests/data/stub_merge/micropython-v1_18-esp32/esp32.py")
    diffs = []
    try:
        diff_gen = enrich_file(source_file, target_file, diff=True, write_back=False)
        diffs = list(diff_gen)
    except FileNotFoundError:
        assert not expected, "docstub File not found but expected"

    if not expected:
        assert len(diffs) == 0, f"no change to the stub was expected but found: \n{diffs}"
    else:
        assert diffs, "change to the stub was expected but not found"


@pytest.mark.parametrize(
    "ID, source_folder, target_folder, expected_count",
    [
        (
            1,
            Path("./tests/data/stub_merge/micropython-v1_18-docstubs"),
            Path("./tests/data/stub_merge/micropython-v1_18-esp32"),
            9,
        ),
        (
            2,
            Path("./tests/data/stub_merge/micropython-v1_18-docstubs"),
            Path("./tests/data/stub_merge/micropython-v1_18-esp32/micropython.pyi"),
            1,
        ),
        (
            3,
            Path("./tests/data/stub_merge/micropython-v1_18-docstubs"),
            Path("./tests/data/stub_merge/micropython-v1_18-esp32/uplatform.pyi"),
            0,
        ),
        (
            4,
            Path("./tests/data/stub_merge/micropython-v1_18-docstubs/micropython.pyi"),
            Path("./tests/data/stub_merge/micropython-v1_18-esp32/micropython.pyi"),
            1,
        ),
        # test new stubs with multiple files / module
        (
            15,
            Path("./tests/data/stub_merge/micropython-v1_24_1-docstubs"),
            Path("./tests/data/stub_merge/micropython-v1_24_1-rp2-RPI_PICO/machine.pyi"),
            18,
        ),
        # Add more test cases if needed
    ],
)
def test_enrich_folder(
    ID,
    source_folder: Path,
    target_folder: Path,
    expected_count: int,
    mocker: MockerFixture,
):
    m_enrich_file = mocker.patch("stubber.codemod.enrich.enrich_file", return_value="OK", autospec=True)
    m_format_stubs = mocker.patch("stubber.codemod.enrich.format_stubs", return_value=None, autospec=True)
    count = enrich_folder(
        source_folder,
        target_folder,
        show_diff=False,
        write_back=False,
    )
    m_format_stubs.assert_called_once()
    # enrich_file is now called once per target, merging all matching doc-stubs in a
    # single pass. Count the total number of doc-stubs merged across all calls.
    total_sources = sum(len(call.args[0]) for call in m_enrich_file.call_args_list)
    assert total_sources >= expected_count, f"Expected at least {expected_count} doc-stubs to be merged but found {total_sources}"


def test_enrich_folder_raises_after_file_error(mocker: MockerFixture, tmp_path: Path):
    source = tmp_path / "source"
    target = tmp_path / "target"
    source.mkdir()
    target.mkdir()
    (source / "module.pyi").write_text("value: int\n", encoding="utf-8")
    (target / "module.pyi").write_text("value = 1\n", encoding="utf-8")
    mocker.patch("stubber.codemod.enrich.enrich_file", side_effect=ValueError("parse failed"), autospec=True)
    m_format_stubs = mocker.patch("stubber.codemod.enrich.format_stubs", autospec=True)

    with pytest.raises(ValueError, match="Failed to enrich 1 file"):
        enrich_folder(source, target)

    m_format_stubs.assert_not_called()
