from __future__ import annotations

import sys
from json import loads
from pathlib import Path
from shutil import copy, copyfile, copytree
from subprocess import (  # ruff: ignore[suspicious-subprocess-import] no user input
    CalledProcessError,
    run,
)
from typing import TYPE_CHECKING, TypedDict, cast

from pdm.backend.hooks.base import BuildHookInterface
from typing_extensions import override

from build.py3_8.generate_docstubs import main as generate_docstubs

if TYPE_CHECKING:
    from pdm.backend.hooks import Context


class PackageJson(TypedDict):
    bin: dict[str, str]


# https://github.com/pdm-project/pdm-backend/issues/247
class Hook(BuildHookInterface):  # pyright:ignore[reportImplicitAbstractClass]
    @override
    def pdm_build_update_files(self, context: Context, files: dict[str, Path]):
        if context.target not in {"editable", "wheel"}:
            return
        npm_package_dir = Path("packages/pyright")
        pypi_package_dir = Path("basedpyright")
        dist_dir = Path("dist")
        package_json = "package.json"
        npm_script_paths = cast(PackageJson, loads((npm_package_dir / package_json).read_text()))[
            "bin"
        ].values()
        if context.builder.config_settings.get("regenerate_docstubs") != "false":
            generate_docstubs(overwrite=True)

        command = "pnpm run build:cli:dev"
        # subprocess.run always uses cmd which doesn't work with ./
        gg_executable = "gg.cmd" if sys.platform == "win32" else "./gg.cmd"
        try:
            # need shell because gg.cmd doesn't work otherwise for some reason...
            _ = run(  # ruff: ignore[subprocess-popen-with-shell-equals-true]
                f"{gg_executable} {command}", capture_output=True, check=True, text=True, shell=True
            )
        except CalledProcessError as e:
            # whats the point of capture_output if `CalledProcessError` doesn't include the stderr
            # anyway and whats the point of typeshed if everything is typed as `Any`????????????
            raise Exception(f"{e.stdout=}\n{e.stderr=}") from e  # pyright: ignore[reportAny]

        if context.target == "editable":
            copy(npm_package_dir / package_json, pypi_package_dir)
            copytree(npm_package_dir / dist_dir, pypi_package_dir / dist_dir, dirs_exist_ok=True)
            for script_path in npm_script_paths:
                _ = copyfile(npm_package_dir / script_path, pypi_package_dir / script_path)
        else:
            files[str(pypi_package_dir / package_json)] = npm_package_dir / package_json
            for file in (npm_package_dir / dist_dir).rglob("**/*"):
                if file.is_file():
                    files[str(pypi_package_dir / file.relative_to(npm_package_dir))] = file

            for script_path in npm_script_paths:
                files[str(pypi_package_dir / script_path)] = npm_package_dir / script_path


# https://github.com/pdm-project/pdm/issues/2945
pdm_build_update_files = Hook().pdm_build_update_files  # pyright:ignore[reportAbstractUsage]
