"""Build mckit.

The `build` method from this module is called by poetry build system
to set up proper options for actual builder.

The module builds and arrange C-dependency ``nlopt`` before own build.
"""
from __future__ import annotations

from typing import Any

import shutil

from pathlib import Path
from pprint import pprint

from building.build_nlopt import build_nlopt
from building.extension_geometry import GeometryExtension
from building.extension_utils import MACOS, WIN, get_library_dir
from setuptools import Extension
from setuptools.command.build_ext import build_ext
from setuptools.dist import Distribution

DIR = Path(__file__).parent


class BinaryDistribution(Distribution):
    def is_pure(self):
        return False


def get_nlopt_lib_name() -> str:
    """Compute library name: this depends on OS and python version."""
    if MACOS:
        return "libnlopt.dylib"
    if WIN:
        return "Release/nlopt.dll"
    return "libnlopt.so"


class MCKitBuilder(build_ext):
    def __init__(self, dist: Distribution, **kwargs) -> None:
        build_ext.__init__(self, dist, **kwargs)

    def finalize_options(self):
        build_ext.finalize_options(self)
        # Late import to use numpy installed on isolated build
        import numpy as np

        library_dir = get_library_dir()
        self.library_dirs.append(str(library_dir))
        self.include_dirs.append(np.get_include())
        self.include_dirs.append(str(library_dir.parent / "include"))

    def build_extension(self, extension: Extension) -> None:
        nlopt_build_dir = build_nlopt(clean=True)
        nlopt_lib = nlopt_build_dir / get_nlopt_lib_name()
        # log.info(f"---***  builder.build_lib: {self.build_lib}")
        # log.info(f"---***  builder.include_dirs: {self.include_dirs}")
        # log.info(f"---***  builder.library_dirs: {self.library_dirs}")
        # log.info(f"---***  nlopt lib path: {nlopt_lib}")
        ext_dir = Path(self.get_ext_fullpath(extension.name)).parent.absolute()
        # log.info(f"---***  copy nlopt lib to {ext_dir}")
        if ext_dir.exists():
            save_library(ext_dir, nlopt_lib)
        save_library(DIR / "src/mckit", nlopt_lib)
        # log.info("---*** Defined geometry extension:")
        # log.info(str(extension))
        # log.info("---***")
        build_ext.build_extension(self, extension)
        # log.info("---*** Search geometry:")
        # log.info(list(DIR.glob("**/*geometry*")))


def build(setup_kwargs: dict[str, Any]) -> None:
    """Set specific distribution options.

    This function is called with setup.py generated by pip from pyproject.toml.
    """
    update_package_data(setup_kwargs)
    geometry_extension = GeometryExtension()
    setup_kwargs.update(
        {
            "ext_modules": [geometry_extension],
            "cmdclass": {"build_ext": MCKitBuilder},
            "distclass": BinaryDistribution,
            "long_description": Path("README.rst").read_text(encoding="utf8"),
            "src_root": str(Path(__file__).parent),
        }
    )
    save_setup_kwargs(setup_kwargs)


def update_package_data(setup_kwargs: dict[str, Any]) -> None:
    """Fix for poetry issue: it doesn't provide correct specification from `[tool.poetry].input` field."""
    package_data = [
        "data/isotopes.dat",
        "nlopt.dll" if WIN else "libnlopt*",
    ]
    setup_kwargs["package_data"] = {"mckit": package_data}


def save_library(destination: Path, so: Path) -> None:
    if not so.exists():
        raise FileNotFoundError(f"Cannot find shared library {so}")
    shutil.copy(str(so), str(destination))


def save_setup_kwargs(setup_kwargs: dict[str, Any]) -> None:
    """Save resulting setup_kwargs for examining."""
    kwargs_path = Path(__file__).parent / "poetry_setup_kwargs.txt"
    with kwargs_path.open(mode="w") as fid:
        pprint(setup_kwargs, fid, indent=4)
