#!/usr/bin/env python3
import os
import subprocess
import time
import argparse
import sys
from pathlib import Path

about_tool = """
Build toolchains using Spack.

This function builds toolchains for toolchains at the appropriate directory for the current system architecture
and MPSD software stack version.
The toolchains are built using the bash script spack_setup.sh, and the results are logged.
"""


# Helper class to change directory via context manager
class os_chdir:
    def __init__(self, new_dir):
        self.new_dir = new_dir
        self.saved_dir = os.getcwd()

    def __enter__(self):
        os.chdir(self.new_dir)

    def __exit__(self, exc_type, exc_val, exc_tb):
        os.chdir(self.saved_dir)


class builder:
    def __init__(
        self, release, cmd, toolchain_list=None, target_dir=None, skip_build_cache=False
    ) -> None:
        # Variables taken from cli arguments
        self.mpsd_os = os.getenv("MPSD_OS", "UNKNOWN_OS")
        self.mpsd_microarch = os.getenv("MPSD_MICROARCH", "UNKNOWN_MICROARCH")
        self.mpsd_spack_ver = release
        self.toolchain_list = toolchain_list
        if target_dir == "DEFAULT" or target_dir is None:
            target_dir = (
                f"/opt_mpsd/{self.mpsd_os}/{self.mpsd_spack_ver}/{self.mpsd_microarch}"
            )
        self.toolchain_base_dir = Path(target_dir)
        self.skip_build_cache = skip_build_cache
        self.skip_dir_check = False
        self.current_dir = os.getcwd()
        self.run_mode = cmd
        # Spack environments related variables
        self.spe_branch = None  # To be set after cloning the spack-environments repo
        self.spe_commit_hash = (
            None  # To be set after cloning the spack-environments repo
        )
        # script related variables
        self.script_branch = (
            subprocess.run(
                ["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE
            )
            .stdout.decode()
            .strip()
        )
        self.script_commit_hash = (
            subprocess.run(
                ["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE
            )
            .stdout.decode()
            .strip()
        )
        # Two log files, one for logging commands and the other for logging the build process
        self.cmd_log_file = "logs/install-software-environment.log"
        self.build_log_file = f"build_toolchains_{self.mpsd_spack_ver}_{time.strftime('%Y%m%d-%H%M%S')}.log"

        # run the script
        # self.run() # better to call this seperately after creating the object

    def run(self):
        if self.run_mode == "remove":
            self.remove_toolchains()
        elif self.run_mode == "start_new":
            self.start_new_env()
        elif self.run_mode == "install":
            self.build_toolchains()
        else:
            self.prepare_env()

    def prepare_env(self):
        if not os.path.exists(self.toolchain_base_dir):
            os.makedirs(self.toolchain_base_dir)
        else:
            if not self.skip_dir_check:
                raise ValueError(
                    f"Error: Target directory {self.toolchain_base_dir} already exists. \n\
                    Please remove it and try again."
                )
        with os_chdir(self.toolchain_base_dir):
            subprocess.run(
                [
                    "git",
                    "clone",
                    "https://gitlab.gwdg.de/mpsd-cs/spack-environments.git",
                ]
            )
            with os_chdir("spack-environments"):
                subprocess.run(["git", "checkout", self.mpsd_spack_ver])

    def install_toolchains(
        release, install, target_dir, force_reinstall, skip_build_cache, skip_dir_check
    ):
        prepare_env(release, target_dir, skip_dir_check)

    def start_new_env(set_up, from_release):
        pass

    def remove_toolchain(release, remove):
        pass


def main():
    parser = argparse.ArgumentParser(description=about_tool)
    parser.add_argument(
        "--release", type=str, help="Specify the release version to install"
    )
    parser.add_argument(
        "--target-directory",
        type=str,
        help="Specify the target directory for installation (use DEFAULT to use /opt_mpsd/<MPSD_OS>/<MPSD_RELEASE>/<MPSD_MICROARCH)",
    )
    parser.add_argument(
        "--install",
        nargs="+",
        help="Specify toolchain(s) to install eg foss2021a-mpi (use ALL to install all available toolchains)",
    )
    parser.add_argument("--remove", type=str, help="Specify toolchain to remove")
    parser.add_argument(
        "--set-up",
        type=str,
        help="Start a new software environment version, must specify --from <release>",
    )
    parser.add_argument(
        "--from",
        dest="from_release",
        type=str,
        help="Specify the release version to start from",
    )
    parser.add_argument(
        "--force-reinstall",
        action="store_true",
        help="Delete and reinstall an existing toolchain directory",
    )
    parser.add_argument(
        "--skip-build-cache",
        action="store_true",
        help="Skip Spack build cache during installation",
    )
    parser.add_argument(
        "--skip-dir-check",
        action="store_true",
        help="Skip checking if the target directory already exists",
    )

    args = parser.parse_args()

    if args.release is None:
        parser.print_help()
        sys.exit(1)

    if args.remove:
        remove_toolchain(args.release, args.remove)
    elif args.set_up:
        start_new_env(args.set_up, args.from_release)
    else:
        target_dir = args.target_directory

        if args.install:
            install_toolchains(
                args.release,
                args.install,
                target_dir,
                args.force_reinstall,
                args.skip_build_cache,
                args.skip_dir_check,
            )
        else:
            prepare_env(args.skip_dir_check, args.release, target_dir)


if __name__ == "__main__":
    main()