#!/usr/bin/env python
"""
All things specific to Python (pyproject.toml, PyPI, etc.)
"""

import difflib
import logging
import pathlib
import tomllib
from urllib.parse import quote as urlquote

import tomli_w
import tomli_w._writer
from spdx_license_list import LICENSES as SPDX_LICENSES
from trove_classifiers import classifiers as py_classifiers

from ..file import update_content
from ..requests import get_json_or_none
from .venv import VirtualEnvironment

LOGGER = logging.getLogger(__name__)


def get_pypi_url(name):
    """
    Get the URL to the project's page on PyPI if it exists.

    Args:
        name:
            The project name.

    Returns:
        The project URL, or None if it does not exist.
    """
    url = f"https://pypi.org/pypi/{urlquote(name)}/json"
    LOGGER.debug("Querying %s", url)
    resp = get_json_or_none(url)
    if resp is None:
        return None
    try:
        return resp["info"]["package_url"]
    except KeyError:
        return None


def get_license_classifier(spdx_id):
    """
    Get the Python license classifier for the given SPDX license ID.

    Args:
        spdx_id:
            The SPXD license ID.

    Returns:
        The corresponding Python trove classifier if found, else None.
    """
    spdx_data = SPDX_LICENSES.get(spdx_id)
    if not spdx_data:
        LOGGER.warning("%s is not a recognized SPDX license ID.", spdx_id)
        return None
    name = spdx_data.name
    if spdx_data.osi_approved:
        expected = f"License :: OSI Approved :: {name}"
    else:
        expected = "License :: {name}"
    if expected in py_classifiers:
        return expected
    closest = difflib.get_close_matches(expected, py_classifiers)
    if not closest:
        LOGGER.warning(
            "Failed to map SPDX license ID %s to a Python trove classifier.", spdx_id
        )
        return None
    return closest[0]


def _join_names(given_names, family_names):
    """
    Join given and family names to a single string.

    Args:
        given_names:
            The given names.

        family_names:
            The family names.

    Returns:
        A string with the joined names.
    """
    if given_names and family_names:
        return f"{given_names} {family_names}"
    if given_names:
        return str(given_names)
    if family_names:
        return str(family_names)
    return None


def update_pyproject_toml(project):
    """
    Update the URLs in a pyproject.toml file.

    Args:
        project:
            The Project instance.
    """
    path = project.pyproject_toml_path
    data = tomllib.loads(path.read_text(encoding="utf-8"))

    urls = data["project"]["urls"]
    #  urls.clear()
    urls.update(project.urls)

    data["project"]["authors"] = [
        {
            "name": _join_names(author["given-names"], author["family-names"]),
            "email": author["email"],
        }
        for author in project.config.config["authors"]
    ]

    classifiers = set(
        classifier
        for classifier in data["project"].get("classifiers", [])
        if not classifier.startswith("License :: ")
    )

    spdx_id = project.spdx_license
    if spdx_id:
        license_classifier = get_license_classifier(spdx_id)
        if license_classifier:
            classifiers.add(license_classifier)
    else:
        LOGGER.warning("No license detected for project.")

    data["project"]["classifiers"] = sorted(classifiers)

    content = tomli_w.dumps(data, multiline_strings=True)
    update_content(content, path)
    return urls


def get_version(project_dir):
    """
    Get the version of a project from its directory.

    This will create a temporary virtual environment and install the project in
    it to get the version. This ensures that VCS-versions are correctly handled.

    This should not be necessary but at the time or writing the current version
    of CodeMetaPy fails to detect versions.
    """
    project_dir = pathlib.Path(project_dir).resolve()
    with VirtualEnvironment() as venv:
        venv.run_pip_in_venv(["install", "--no-deps", "-U", str(project_dir)])

        data = tomllib.loads(
            (project_dir / "pyproject.toml").read_text(encoding="utf-8")
        )
        name = data["project"]["name"]
        return (
            venv.run_python_in_venv(
                [
                    "-c",
                    f'from importlib.metadata import version; print(version("{name}"))',
                ],
                capture_output=True,
            )
            .stdout.decode()
            .strip()
        )
