"""SDist of the Python Module"""
from pathlib import Path
import re
import shutil
from textwrap import dedent

from build import ProjectBuilder
from build.env import IsolatedEnvBuilder

from buildutils import logger

Import("env")

localenv = env.Clone()

sdist_targets = []


def sdist(targets):
    sdist_targets.extend(targets)
    return targets


def copy_ext_src(target, source, env):
    target_ext = Path(target[0].abspath)
    source_ext = Path(source[0].abspath)
    if target_ext.is_dir():
        shutil.rmtree(target_ext)

    error_message = ("Please run 'git submodule update --init --recursive' to clone "
                     "these files.")

    # fmt library
    FMT_ROOT = target_ext / "fmt"
    FMT_ROOT.mkdir(parents=True)  # Needed to be able to use copy2
    FMT_SOURCE = source_ext / "fmt"
    if not FMT_SOURCE.is_dir():
        raise ValueError("Missing fmt submodule. " + error_message)
    for cc_file in (FMT_SOURCE / "src").glob("*.cc"):
        if cc_file.name == "fmt.cc":
            continue
        shutil.copy2(cc_file, FMT_ROOT)
    shutil.copytree(FMT_SOURCE / "include" / "fmt", FMT_ROOT / "fmt")

    # yaml-cpp library
    YAML_ROOT = target_ext / "yaml-cpp"
    YAML_SOURCE = source_ext / "yaml-cpp"
    if not YAML_SOURCE.is_dir():
        raise ValueError("Missing yaml-cpp submodule. " + error_message)
    shutil.copytree(YAML_SOURCE / "src", YAML_ROOT)
    shutil.copytree(YAML_SOURCE / "include" / "yaml-cpp", YAML_ROOT / "yaml-cpp")

    # SUNDIALS library
    SUNDIALS_ROOT = target_ext / "sundials"
    SUNDIALS_SOURCE = source_ext / "sundials"
    if not SUNDIALS_SOURCE.is_dir():
        raise ValueError("Missing sundials submodule. " + error_message)
    subdirs = ["sundials", "nvector/serial", "cvodes", "idas", "sunmatrix/band",
               "sunmatrix/dense", "sunmatrix/sparse", "sunlinsol/dense",
               "sunlinsol/band", "sunlinsol/spgmr", "sunnonlinsol/newton"]
    ignores = shutil.ignore_patterns("fsun*", "CMake*", "fmod", "fcmix")
    for subdir in subdirs:
        shutil.copytree(
            SUNDIALS_SOURCE / "src" / subdir,
            SUNDIALS_ROOT / subdir,
            ignore=ignores,
        )


def replace_git_hash(target, source, env):
    # Avoid having to set a C preprocessor define at compile time, since
    # the git commit is unknown from the sdist
    target = Path(target[0].abspath)
    source = Path(source[0].abspath)
    git_commit_replaced = re.sub("#ifdef GIT_COMMIT.*?#endif",
                                 f"""    return "{env['git_commit']}";""",
                                 source.read_text(),
                                 flags=re.DOTALL)
    target.write_text(git_commit_replaced)


# Use RecursiveInstall to be able to exclude files and folders.
sdist(localenv.RecursiveInstall(
    "src",
    "#src",
    exclude=["fortran", "matlab", "clib", r"global\.cpp", "SCons.*", r"canteraStatic\.cpp"],
))

sdist(localenv.Command("src/base/global.cpp", "#src/base/global.cpp",
                       replace_git_hash))
sdist(localenv.Command("include/cantera/cython/utils_utils.h", "#include/cantera/cython/utils_utils.h",
                       replace_git_hash))

# This is the only bit of the clib that we need for the Python interface
clib_defs_target = sdist(localenv.Command(
    "include/cantera/clib/clib_defs.h",
    "#include/cantera/clib/clib_defs.h",
    Copy("$TARGET", "$SOURCE")
))

# The clib is handled by clib_defs_target. The include/cantera/ext folder is handled by
# ext_include_target. config.h needs to be filled on the user's machine by setup.py and
# the template file is handled by config_h_in_target.
include_target = sdist(localenv.RecursiveInstall(
    "include",
    "#include",
    exclude=[
        "clib$",
        "ext$",
        r"config\.h\.in",
        r"utils_utils\.h",
    ],
))
localenv.Depends(clib_defs_target, include_target)
localenv.Depends(include_target, env["config_h_target"])

ext_include_target = sdist(localenv.Command(
    "include/cantera/ext",
    "#include/cantera/ext",
    Copy("$TARGET", "$SOURCE"),
))
localenv.Depends(ext_include_target, env["ext_include_copies_target"])
localenv.Depends(ext_include_target, include_target)

ext_target = sdist(localenv.Command("ext", "#ext", copy_ext_src))

sdist(localenv.UnitsInterfaceBuilder(
    "cantera/with_units/solution.py",
    "#interfaces/cython/cantera/with_units/solution.py.in",
))
# Use RecursiveInstall to make sure that files are not overwritten during the copy.
# A normal Copy Action would fail because of the existing directories.
sdist(localenv.RecursiveInstall("cantera",
                                "#interfaces/cython/cantera",
                                exclude=["__pycache__"]))
sdist(localenv.RecursiveInstall("cantera/data",
                                "#build/data"))

# Copy the minimal Sundials configuration template into the sdist so that
# it can be filled in at compile time on the user's machine
sdist(localenv.Command("sundials_config.h.in", "#ext/sundials_config.h.in",
                       Copy("$TARGET", "$SOURCE")))

license = sdist(localenv.Command("LICENSE.txt", "#build/ext/LICENSE.txt",
                                 Copy("$TARGET", "$SOURCE")))
localenv.Depends(license, localenv["license_target"])

sdist(localenv.SubstFile("setup.cfg", "setup.cfg.in"))
sdist(localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE")))


def build_sdist(target, source, env):
    build_dir = Path(source[0].abspath).parent
    builder = ProjectBuilder(str(build_dir))
    with IsolatedEnvBuilder() as build_env:
        builder.python_executable = build_env.executable
        builder.scripts_dir = build_env.scripts_dir
        # first install the build dependencies
        build_env.install(builder.build_system_requires)
        # then get the extra required dependencies from the backend
        build_env.install(builder.get_requires_for_build("sdist"))
        builder.build("sdist", str(build_dir / "dist"), {})


def finish_sdist_message(target, source, env):
    sdist = Path(source[0].path).name
    message = dedent(f"""
        *******************************************************
        Python sdist '{sdist}' created successfully.
        The sdist file is in the 'build/python_sdist/dist'
        directory.
        *******************************************************
    """)
    logger.info(message, print_level=False)


sdist_target = f"dist/Cantera-{env['cantera_version']}.tar.gz"
sdist_sources = ("setup.py", "pyproject.toml", "MANIFEST.in")
built_sdist = localenv.Command(sdist_target, sdist_sources, build_sdist)
finish_sdist = localenv.Command("finish_sdist", sdist_target, finish_sdist_message)
localenv.Depends(built_sdist, sdist_targets)
env.Alias("sdist", finish_sdist)
