from __future__ import annotations

import shutil
import subprocess
import sys
import tarfile
import tempfile
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal

if TYPE_CHECKING:
    from collections.abc import Iterator, Mapping

__all__ = ["default_sdist_ignore", "get_uv", "sdist_files"]


def get_uv() -> str | None:
    """
    Find uv and return the arguments to use it.
    """
    try:
        # pylint: disable-next=import-outside-toplevel
        import uv  # noqa: PLC0415

        return uv.find_uv_bin()
    except ModuleNotFoundError:
        return shutil.which("uv")


def sdist_files(
    source_dir: Path, *, isolated: bool, installer: Literal["uv", "pip"]
) -> frozenset[str]:
    """Return the files that would be (are) placed in the SDist."""

    with tempfile.TemporaryDirectory() as outdir:
        if installer == "pip":
            cmd = [
                sys.executable,
                "-m",
                "build",
                "--sdist",
                "--outdir",
                outdir,
                f"--installer={installer}" if isolated else "--no-isolation",
            ]
        else:
            uv = get_uv()
            assert uv is not None, "uv must be found to reach this point!"
            cmd = [
                uv,
                "build",
                "--sdist",
                "--python",
                sys.executable,
                "--out-dir",
                outdir,
            ]

        subprocess.run(cmd, check=True, cwd=source_dir)

        (outpath,) = Path(outdir).glob("*.tar.gz")

        with tarfile.open(outpath) as tar:
            prefixes = {n.split("/", maxsplit=1)[0] for n in tar.getnames()}
            if len(prefixes) != 1:
                msg = f"malformed SDist, contains multiple packages {prefixes}"
                raise AssertionError(msg)
            return frozenset(
                t.name.split("/", maxsplit=1)[1]
                for t in tar.getmembers()
                if t.isfile() or t.issym()
            )


def default_sdist_ignore(pyproject: Mapping[str, Any]) -> Iterator[str]:
    """Return the default ignore patterns for SDists."""
    yield "*.dist-info"
    build_backend = pyproject.get("build-system", {}).get(
        "build-backend", "setuptools.build_meta.__legacy__"
    )
    if build_backend in {
        "setuptools.build_meta",
        "setuptools.build_meta.__legacy__",
    }:
        yield "*.egg-info"
        yield "setup.cfg"

    # Look for some common autogenerated version files
    # [tool.setuptools_scm]
    # version-file = "_version.py"
    setuptools_scm = (
        pyproject.get("tool", {}).get("setuptools_scm", {}).get("version_file", None)
    )
    if setuptools_scm is not None:
        yield setuptools_scm

    # [tool.hatch.build.hooks.vcs]
    # version-file = "_version.py"
    hatch_vcs = (
        pyproject.get("tool", {})
        .get("hatch", {})
        .get("build", {})
        .get("hooks", {})
        .get("vcs", {})
        .get("version-file", None)
    )
    if hatch_vcs is not None:
        yield hatch_vcs

    # [tool.pdm.version]
    # write_to = "_version.py"
    pdm_version = (
        pyproject.get("tool", {})
        .get("pdm", {})
        .get("version", {})
        .get("write_to", None)
    )
    if pdm_version is not None:
        yield pdm_version

    # [[tool.scikit-build.generate]]
    # path = "_version.py"
    for skbg in pyproject.get("tool", {}).get("scikit-build", {}).get("generate", []):
        scikit_build_generate = skbg.get("path", None)
        if scikit_build_generate is not None:
            yield scikit_build_generate


if __name__ == "__main__":
    print(*sorted(sdist_files(Path.cwd(), isolated=True, installer="pip")), sep="\n")
