import csv
import pathlib
import sys
from typing import Dict
from typing import List
from typing import Set

from packaging.version import parse as parse_version


sys.path.append(str(pathlib.Path(__file__).parent.parent.resolve()))
import riotfile  # noqa:E402


OUT_FILENAME = "min_compatible_versions.csv"
OUT_DIRECTORIES = (".", "lib-injection/sources")
IGNORED_PACKAGES = {"setuptools", "attrs", "pytest-randomly", "pillow", "botocore", "pytest-asyncio"}


def _format_version_specifiers(spec: Set[str]) -> Set[str]:
    return set([part for v in [v.split(",") for v in spec if v] for part in v if "!=" not in part])


def tree_pkgs_from_riot() -> Dict[str, Set[str]]:
    return _tree_pkgs_from_riot(riotfile.venv)


def _tree_pkgs_from_riot(node: riotfile.Venv) -> Dict[str, Set]:
    result = {
        pkg: _format_version_specifiers(set(versions))
        for pkg, versions in node.pkgs.items()
        if pkg not in IGNORED_PACKAGES
    }
    for child_venv in node.venvs:
        child_pkgs = _tree_pkgs_from_riot(child_venv)
        for pkg_name, versions in child_pkgs.items():
            if pkg_name in IGNORED_PACKAGES:
                continue
            if pkg_name in result:
                result[pkg_name] = result[pkg_name].union(versions)
            else:
                result[pkg_name] = versions
    return result


def min_version_spec(version_specs: List[str]) -> str:
    min_numeric = ""
    min_spec = ""
    for spec in version_specs:
        numeric = parse_version(spec.strip("~==<>"))
        if not min_numeric or numeric < min_numeric:
            min_numeric = numeric
            min_spec = spec
    return min_spec


def write_out(all_pkgs: Dict[str, Set[str]], outfile: str) -> None:
    with open(outfile, "w") as csvfile:
        csv_writer = csv.writer(csvfile, delimiter=",")
        csv_writer.writerow(["This file was generated by scripts/min_compatible_versions.py"])
        csv_writer.writerow(["pkg_name", "min_version"])
        for pkg, versions in sorted(all_pkgs.items()):
            min_version = "0"
            if versions:
                min_version = str(min_version_spec(versions)).strip()
            print("%s\n\tTested versions: %s\n\tMinimum: %s" % (pkg, sorted(list(versions)), min_version))
            csv_writer.writerow([pkg, min_version])


def main():
    """Discover the minimum version of every package referenced in the riotfile

    Writes to stdout and min_versions.csv
    """
    pkgs = tree_pkgs_from_riot()
    for directory in OUT_DIRECTORIES:
        write_out(pkgs, pathlib.Path(directory) / OUT_FILENAME)


main()
