"""Test candidates.py"""

from pathlib import Path
from typing import Generator, List, Union

import pytest

pytestmark = [pytest.mark.stubber]
from stubber.publish.candidates import (  # DOC_STUBS,; docstub_candidates,
    # COMBO_STUBS,
    best_matching_port,
    filter_list,
    frozen_candidates,
    subfolder_names,
    version_candidates,
)
from stubber.publish.publish import build_worklist


def test_subfoldernames(tmp_path, pytestconfig):
    # test data
    path = pytestconfig.rootpath / "tests/publish/data/stub-version"
    names = subfolder_names(path)
    assert isinstance(names, Generator)
    l = list(names)
    assert len(l) > 6


@pytest.mark.parametrize("suffix,count", [("frozen", 5), ("docstubs", 2)])
def test_version_candidates(pytestconfig, suffix, count):
    # test data
    path: Path = pytestconfig.rootpath / "tests/publish/data/stub-version"
    versions = version_candidates(suffix, path=path)
    assert isinstance(versions, Generator)
    l = list(versions)
    assert len(l) == count


@pytest.mark.parametrize("prefix, suffix,count", [("foobar", "frozen", 1)])
def test_version_prefix(pytestconfig, prefix, suffix, count):
    # test data
    path: Path = pytestconfig.rootpath / "tests/publish/data/stub-version"
    versions = version_candidates(prefix=prefix, suffix=suffix, path=path)
    assert isinstance(versions, Generator)
    l = list(versions)
    assert len(l) == count


# @pytest.mark.parametrize(
#     "family, versions, count",
#     [
#         ("micropython", "latest", 1),
#         ("micropython", "v1.18", 1),
#         ("micropython", "1.18", 1),
#         ("micropython", "auto", 2),
#     ],
# )
# def test_docstub_candidates(pytestconfig, family, versions, count):
#     # test data
#     path: Path = pytestconfig.rootpath / "tests/publish/data/stub-version"
#     docstubs = docstub_candidates(path=path, family=family, versions=versions)
#     assert isinstance(docstubs, Generator)
#     l = list(docstubs)
#     assert len(l) == count
#     if len(l) > 0:
#         assert l[0]["pkg_type"] == DOC_STUBS


@pytest.mark.parametrize(
    "family, versions, ports, boards, count",
    [
        ("foobar", "auto", "foo", "bar", 2),  # self + Generic
        ("foobar", "auto", "foo", "not", 1),  # generic
        # find all candidates
        ("micropython", "auto", "auto", "auto", 19),
        # list GENERIC boards for any version (case sensitive on linux/mac)
        ("micropython", "auto", "auto", "GENERIC", 11),
        ("micropython", "auto", "auto", "generic", 11),
        ("micropython", "preview", "auto", "auto", 7),
        ("micropython", "v1.16", "foo", "GENERIC", 0),  # port folder does not exist
        ("micropython", "v1.16", "foo", "generic", 0),  # port folder does not exist
        # list GENERIC boards for specific version (case sensitive on linux/mac)
        ("micropython", "v1.18", "auto", "GENERIC", 3),
        ("micropython", "v1.18", "esp32", "auto", 2),
        ("micropython", "v1.18", "esp32", "GENERIC", 1),
        ("micropython", "v1.18", "stm32", "GENERIC", 1),
        # list borads for specific port
        ("micropython", "v1.18", "stm32", "auto", 2),
        # list all ports / boards for a version
        ("micropython", "v1.18", "auto", "auto", 5),
        ("micropython", "v1.18", "stm32", "PYBD_SF2", 2),  # Self + Generic
        ("micropython", "v1.18", "stm32", "pybd_sf2", 2),  # Self + Generic
    ],
)
@pytest.mark.xfail()
# "test is overdepedent on the data in the stubber-repo"
def test_frozen_candidates(pytestconfig, family, versions, ports, boards, count):
    # test data
    path = pytestconfig.rootpath / "tests/publish/data/stub-version"
    frozen = frozen_candidates(path=path, family=family, versions=versions, ports=ports, boards=boards)
    assert isinstance(frozen, Generator)
    l = list(frozen)
    print()
    for i in l:
        print(i)
    assert len(l) == count, f"{len(l)} != {count}, {l}"


@pytest.mark.parametrize(
    "family, versions, ports, boards, count",
    [
        # find no candidates
        ("nono", "auto", "auto", "auto", 0),
        # find all candidates
    ],
)
@pytest.mark.skip("WIP")
def test_frozen_candidates_err(pytestconfig, family, versions, ports, boards, count):
    # test data
    path = pytestconfig.rootpath / "tests/publish/data/stub-version"
    with pytest.raises(Exception) as exc_info:
        _ = frozen_candidates(path=path, family=family, versions=versions, ports=ports, boards=boards)
    assert exc_info.type == NotImplementedError


@pytest.mark.parametrize(
    "family, versions, ports, boards, count",
    [
        ("nono", "auto", "auto", "auto", 0),  # find no candidates
        ("nono", "auto", "auto", "auto", 0),  # find no candidates
        ("micropython", "v1.18", "auto", "GENERIC", 17),  # find v1.18 ports
        ("micropython", "v1.18", "esp32", "GENERIC", 1),  # find v1.18 ESP32 ports
        ("micropython", "v1.19.1", "esp8266", "GENERIC", 1),  # find v1.18 ESP8266 ports
        ("micropython", "v1.19.1", "esp8266", "generic", 1),  # find v1.18 ESP8266 ports
        ("micropython", "v1.18", "stm32", "auto", 56),  # find v1.18 STM32 boards
        ("micropython", "v1.17", "auto", "auto", 125),  # find all v1.17 ports & boards
        ("micropython", "v1.18", "auto", "auto", 140),  # find all v1.18 ports & boards
        ("micropython", "v1.19.1", "auto", "auto", 158),  # find all v1.18 ports & boards
        ("micropython", "v1.18", "auto", "NUCLEO_F091RC", 1),  # find v1.18 NUCLEO_F091RC boards
        (
            "micropython",
            ["v1.18"],
            "auto",
            "NUCLEO_F091RC",
            1,
        ),  # find v1.18 NUCLEO_F091RC fix test numbers
        ("micropython", ["latest"], "auto", "NUCLEO_F091RC", 1),  # find v1.18 NUCLEO_F091RC boards
        (
            "micropython",
            ["latest", "v1.18"],
            "auto",
            "NUCLEO_F091RC",
            2,
        ),  # find v1.18 NUCLEO_F091RC boards
    ],
)
def test_worklist(family: str, versions: Union[List[str], str], ports: str, boards: str, count: int):
    wl = build_worklist(family=family, versions=versions, ports=ports, boards=boards)
    assert isinstance(wl, list)
    msg = ", ".join(sorted([f"{l['version']}-{l['port']}-{l['board']}" for l in wl]))
    # print(msg)

    # Candidate counts may vary by 5% or 2 entries as the source tree evolves.
    assert len(wl) == pytest.approx(count, rel=0.05, abs=2), f"expected {count}, found {len(wl)} {msg}."


def test_filter_list_default_alias_matches_generic_candidate():
    worklist = [
        {"family": "micropython", "version": "v1.28.0", "port": "rp2", "board": "GENERIC"},
        {"family": "micropython", "version": "v1.28.0", "port": "rp2", "board": "RPI_PICO_W"},
    ]

    filtered = filter_list(worklist, ports=["rp2"], boards=["DEFAULT"])
    assert len(filtered) == 1
    assert filtered[0]["board"] == "GENERIC"


@pytest.mark.parametrize(
    "port, expected",
    [
        ("nrf", "nrf"),  # exact match of a real port
        ("esp32", "esp32"),  # exact match of a real port
        ("nrf52", "nrf"),  # out-of-tree port name reduced to best matching prefix
        ("bogus", None),  # no matching MicroPython port
    ],
)
def test_best_matching_port(port, expected):
    assert best_matching_port(port) == expected
