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

about_tool = """
Build toolchains using Spack.\n

This function builds toolchains for MPSD-HPC at the appropriate directory, \n
for given system architecture and MPSD software stack version.\n
The toolchains are built using the bash script spack_setup.sh, and the results are logged.
"""
config_vars = {
    "cmd_log_file": "logs/install-software-environment.log",
    "build_log_file": f"build_toolchains_mpsd_spack_ver_{time.strftime('%Y%m%d-%H%M%S')}.log", # TODO: modify toolchains,mpsd_spack_ver when the variable is available
    "spack_environments_repo": "https://gitlab.gwdg.de/mpsd-cs/spack-environments.git",

}
shared_var ={
    "script_branch": subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE).stdout.decode().strip(),
    "script_commit_hash": subprocess.run(["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE).stdout.decode().strip(),
    "spe_branch": None,
    "spe_commit_hash": None,
    "available_toolchains": None,
    "toolchain_base_dir": None,

}

# 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)

def setup_log_cmd(shared_var, msg=None):
    # Create log directory if it doesn't exist
    if not os.path.exists("logs"):
        os.makedirs("logs")
    # Write to the log file with the following format
    # --------------------------------------------------
    # 2023-02-29T23:32:01, install-software-environment.py --release dev-23a --install ALL
    # Software environment installer branch: script_branch (commit hash: script_commit_hash)
    # Spack environments branch: dev-23a (commit hash: spe_commit_hash)
    # MSGs
    with open(config_vars['cmd_log_file'], "a") as f:
        if msg:
            f.write(msg + "\n")
        else:
            f.write("-" * 50 + "\n")
            cmd_line = " ".join(sys.argv)
            f.write(f"{datetime.datetime.now().isoformat()}, {cmd_line}\n")
            f.write(
                f"Software environment installer branch: {shared_var['script_branch']} (commit hash: {shared_var['script_commit_hash']})\n"
            )
            f.write(
                f"Spack environments branch: {shared_var['spe_branch']} (commit hash: {shared_var['spe_commit_hash']})\n"
            )

def prepare_env(toolchain_base_dir, mpsd_spack_ver, skip_dir_check, shared_var):

    if not os.path.exists(toolchain_base_dir):
        os.makedirs(toolchain_base_dir)
    else:
        if not skip_dir_check:
            raise ValueError(
                f"Error: Target directory {toolchain_base_dir} already exists. \n\
                Please remove it and try again."
            )
    with os_chdir(toolchain_base_dir):
        subprocess.run(
            [
                "git",
                "clone",
                config_vars['spack_environments_repo'],
            ]
        )
        with os_chdir("spack-environments"):
            subprocess.run(
                [
                    "git",
                    "checkout",
                    mpsd_spack_ver,
                ]
            )

            # Get the branch and commit hash of the spack-environments repo and store them in shared_var
            shared_var['spe_commit_hash'] = (
                subprocess.run(
                    ["git", "rev-parse", "HEAD"], stdout=subprocess.PIPE
                )
                .stdout.decode()
                .strip()
            )
            shared_var['spe_branch'] = (
                subprocess.run(
                    ["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE
                )
                .stdout.decode()
                .strip()
            )
            shared_var['available_toolchains'] = os.listdir("toolchains")
    setup_log_cmd(shared_var)
    return shared_var

def install(release, toolchains, target_dir,force_reinstall,skip_build_cache):
    print(f"Installing release {release} with toolchains {toolchains} to {target_dir}")
    shared_var = prepare_env(target_dir, release, force_reinstall, shared_var)

def remove(release, toolchains, target_dir):
    print(f"Removing release {release} with toolchains {toolchains} from {target_dir}")



def main():
    parser = argparse.ArgumentParser(description=about_tool)
    subparsers = parser.add_subparsers(dest='action', title='actions', description='valid actions', required=True)
    subparsers.required = True
    list_of_cmds = [
        ('prepare', 'Prepare the environment for installation on the disk'),
        ('install', 'Install a software environment'),
        ('reinstall', 'Reinstall a software environment'),
        ('remove', 'Remove a software environment or toolchains from an environment'),
        ('start-new', 'Start a new software environment version')
    ]
    for cmd, help_text in list_of_cmds:
        subp = subparsers.add_parser(cmd, help=help_text)

        if cmd == 'start-new':
            subp.add_argument('--from-release', dest='from_release', type=str, required=True, help='Release version to start from')
            subp.add_argument('--to-release', dest='to_release', type=str, required=True, help='Release version to create')

        else:
            subp.add_argument('release', type=str, help='Release version to install or remove')
            if cmd in ['install', 'reinstall']:
                subp.add_argument('--toolchains', type=str, nargs='*', default='ALL', help='List of toolchains to install (use ALL to install all toolchains)')
                subp.add_argument('--enable-build-cache', action='store_true', help='Enable Spack build cache. Useful for reinstallation but consumes time and disk space')
    # Carry out the action
    args = parser.parse_args()
    
    if args.action == 'remove':
        remove(args.release, args.toolchains,args.target_dir)
    elif args.action == 'start-new':
        start_new(args.from_release, args.to_release, args.target_dir)
    else:
        if args.target_dir:
            target_dir = args.target_dir
        else:
            mpsd_os = os.getenv('MPSD_OS','UNKNOWN_OS')
            mpsd_arch = os.getenv('MPSD_ARCH','UNKNOWN_ARCH')
            if os.path.exists('/opt_mpsd'):
                root_dir = '/opt_mpsd'
            else:
                root_dir = os.path.expanduser('~') + '/mpsd_toolchain'
            target_dir = os.path.join(root_dir, mpsd_os , args.release, mpsd_arch)

        if args.action == 'install':
            install(args.release, args.toolchains, target_dir, args.force_reinstall, args.skip_build_cache)
        else:
            prepare_env(target_dir,args.release, False, shared_var)

if __name__ == "__main__":
    main()