# -*- coding: utf-8 -*-
from setuphelpers import *
import platform


ext_uid_name = "ms-python.vscode-pylance"
vscode_audit_file_sub = f'%s_{ext_uid_name.replace(".", "-")}_vscode_audit.txt'
vscodium_audit_file_sub = f'%s_{ext_uid_name.replace(".", "-")}_vscodium_audit.txt'


def install():
    # Initializing variables
    ext_file_name = glob.glob("*%s*.vsix" % ext_uid_name)[0]
    vscode_dir, vscode_bin_path = get_vscode_path()
    vscodium_dir, vscodium_bin_path = get_vscodium_path()

    # VSCode extension install
    if isfile(vscode_bin_path):
        # Removing old extensions from app_dir
        for ext in glob.glob(makepath(vscode_dir, "%s*.vsix" % ext_uid_name)):
            print("Removing: %s" % ext)
            remove_file(ext)

        print("Copying: %s to: %s" % (ext_file_name, vscode_dir))
        filecopyto(ext_file_name, vscode_dir)

    # VSCodium extension install
    if isfile(vscodium_bin_path):
        # Removing old extensions from app_dir
        for ext in glob.glob(makepath(vscodium_dir, "%s*.vsix" % ext_uid_name)):
            print("Removing: %s" % ext)
            remove_file(ext)

        print("Copying: %s to: %s" % (ext_file_name, vscodium_dir))
        filecopyto(ext_file_name, vscodium_dir)


def session_setup():

    if platform.system() == 'Windows':
        if windows_version() < WindowsVersions.Windows10:
            return
        if not iswin64():
            return

    # Initializing variables
    vscode_dir, vscode_bin_path = get_vscode_path()
    vscodium_dir, vscodium_bin_path = get_vscodium_path()

    # VSCode extension install
    user_name = user_home_directory().rstrip(os.path.sep).split(os.path.sep)[-1]
    if isfile(vscode_bin_path):
        install_vscod_ext_if_needed(
            vscode_dir,
            vscode_bin_path,
            ext_uid_name,
            control.get_software_version(),
            makepath(get_temp_dir(), vscode_audit_file_sub % user_name),
            force_install=force,
        )

    # VSCodium extension install
    if isfile(vscodium_bin_path):
        install_vscod_ext_if_needed(
            vscodium_dir,
            vscodium_bin_path,
            ext_uid_name,
            control.get_software_version(),
            makepath(get_temp_dir(), vscodium_audit_file_sub % user_name),
            force_install=force,
        )


def audit():
    # Initializing variables
    vscode_dir, vscode_bin_path = get_vscode_path()
    vscodium_dir, vscodium_bin_path = get_vscodium_path()
    audit_status = "OK"

    # VSCode extension audit
    if isfile(vscode_bin_path):
        try:
            ext_path = glob.glob(makepath(vscode_dir, "%s*.vsix" % ext_uid_name))[0]
            print("OK: %s extension file present in %s" % (ext_uid_name, vscode_dir))
        except:
            print("ERROR: %s extension file absent of %s" % (ext_uid_name, vscode_dir))
            audit_status = "ERROR"

    # VSCodium extension audit
    if isfile(vscodium_bin_path):
        try:
            ext_path = glob.glob(makepath(vscodium_dir, "%s*.vsix" % ext_uid_name))[0]
            print("OK: %s extension file present in %s\n" % (ext_uid_name, vscodium_dir))
        except:
            print("ERROR: %s extension file absent of %s\n" % (ext_uid_name, vscodium_dir))
            audit_status = "ERROR"

    # Checking if extension is installed in all users
    vscod_ext_dict = users_installed_vscod_ext_to_dict(ext_uid_name, vscode_audit_file_sub, vscodium_audit_file_sub, ignored_users=[])
    for key in vscod_ext_dict.keys():
        if type(vscod_ext_dict[key]) != dict:
            continue
        if vscod_ext_dict[key]["VSCode"] or vscod_ext_dict[key]["VSCodium"]:
            print(f'"{ext_uid_name}" extension for user "{key}" installed version(s): {vscod_ext_dict[key]}')
        else:
            print(f'"{ext_uid_name}" extension for user "{key}" is not installed')
    WAPT.write_audit_data_if_changed(
        control.package.split("-", 1)[-1], control.package.split("-", 1)[-1], vscod_ext_dict, max_count=10, keep_days=100
    )

    return audit_status


def uninstall():
    # Initializing variables
    vscode_dir, vscode_bin_path = get_vscode_path()
    vscodium_dir, vscodium_bin_path = get_vscodium_path()

    # Removing extensions from vscode_dir
    for ext in glob.glob(makepath(vscode_dir, "*%s*.vsix" % ext_uid_name)):
        print("Removing: %s" % ext)
        remove_file(ext)

    # Removing extensions from vscodium_dir
    for ext in glob.glob(makepath(vscodium_dir, "*%s*.vsix" % ext_uid_name)):
        print("Removing: %s" % ext)
        remove_file(ext)

    # Removing extension from user profiles
    killalltasks(["Code", "code"])
    print(remove_tree_for_all_users(makepath(".vscode", "extensions", "%s*" % ext_uid_name)))
    killalltasks(["VSCodium", "codium"])
    print(remove_tree_for_all_users(makepath(".vscode-oss", "extensions", "%s*" % ext_uid_name)))


def users_installed_vscod_ext_to_dict(ext_uid_name, vscode_audit_file_sub, vscodium_audit_file_sub, ignored_users=None, ignore_system_users=True):
    """Return a dict of dict of the installed extensions version for all users

    Args:
        ext_uid_name (str): VSCode and VSCodium extension identifier
        vscode_audit_file_sub (str): name of file in Temp user folder containing VSCode extension version
        vscodium_audit_file_sub (str): name of file in Temp user folder containing VSCodium extension version

    Returns:
        users_ext_version_dict: Dict of user and the corresponding version in this format:
            {
                "user1": {'VSCode': '0.81.8', 'VSCodium': '0.81.8'},
                "user2": {'VSCode': '', 'VSCodium': '0.81.8'},
                "user3": {'VSCode': '', 'VSCodium': ''},
            }
    """
    skipped_users = get_skipped_users_list(ignored_users, ignore_system_users)

    if get_os_name() == "Windows":
        users_dir = makepath(systemdrive, "Users")
    elif get_os_name() == "Linux":
        users_dir = "/home"
    elif get_os_name() == "Darwin":
        users_dir = "/Users"

    # Getting a dict of all users installed extension version
    users_ext_version_dict = {}
    higher_version = "0.0"
    for user_profile in glob.glob("%s/*/" % users_dir):
        skip = False
        user_name = user_profile.rstrip(os.path.sep).split(os.path.sep)[-1]
        for skipped_user in skipped_users:
            if user_name == skipped_user:
                skip = True
                break

        if not skip:
            # Declaring user based variable
            vscode_audit_file_path = makepath(get_temp_dir(), vscode_audit_file_sub % user_name)
            vscodium_audit_file_path = makepath(get_temp_dir(), vscodium_audit_file_sub % user_name)
            user_profile_ext_version_dict = {
                "VSCode": None,
                "VSCodium": None,
            }

            # Getting the version from the files in user Temp directory
            if isfile(vscode_audit_file_path):
                with open(vscode_audit_file_path, "r") as f:
                    user_profile_ext_version_dict["VSCode"] = f.readline()
                    if Version(higher_version) < Version(user_profile_ext_version_dict["VSCode"]):
                        higher_version = user_profile_ext_version_dict["VSCode"]
            if isfile(vscodium_audit_file_path):
                with open(vscodium_audit_file_path, "r") as f:
                    user_profile_ext_version_dict["VSCodium"] = f.readline()
                    if Version(higher_version) < Version(user_profile_ext_version_dict["VSCodium"]):
                        higher_version = user_profile_ext_version_dict["VSCodium"]

            # Attributing user's version dict to global dict
            if not user_profile_ext_version_dict:
                user_profile_ext_version_dict = None
            users_ext_version_dict[user_name] = user_profile_ext_version_dict
    users_ext_version_dict["higher_version"] = higher_version
    users_ext_version_dict["version"] = control.get_software_version()

    return users_ext_version_dict


def get_installed_vscod_ext_version(app_bin_path, ext_uid_name):
    installed_exts = run(f'"{app_bin_path}" --list-extensions --show-versions').splitlines()
    installed_ext_version = None

    for ext in installed_exts:
        if ext_uid_name in ext:
            installed_ext_version = ext.split("@")[-1]
    return installed_ext_version


def install_vscod_ext_if_needed(app_dir, app_bin_path, ext_uid_name, min_version, audit_file, force_install=False):
    """Will check installed version and install if needed

    Args:
        app_dir (str): path to VSCode or VSCodium directory
        app_bin_path (str): path to VSCode or VSCodium binary
        ext_uid_name (str): VSCode and VSCodium extension identifier
        min_version (str): min_version needed to be installed
        audit_file (str): name of file in Temp user folder containing VSCodium extension version
        force_install (str): option to force extension installation

    """
    installed_ext_version = get_installed_vscod_ext_version(app_bin_path, ext_uid_name)

    # Checking if a potential installed extension is up-to-date
    print(f"Installing {ext_uid_name} for {app_dir.split(os.path.sep)[-1]}")
    if installed_ext_version is not None:
        if Version(installed_ext_version, 4) >= Version(min_version, 4):
            print(f"INFO: Installed extension {ext_uid_name}({installed_ext_version}) is up-to-date")
            # Creating file for audit
            with open(audit_file, "w") as f:
                f.write(installed_ext_version)
            return
        else:
            force_install = True

    ext_path = glob.glob(makepath(app_dir, "%s*.vsix" % ext_uid_name))[0]

    # Installing extension in user env
    print("Installing: %s extension in user env" % (ext_path))
    vsix_cmd = f'"{app_bin_path}" --install-extension "{ext_path}"'
    if force_install:
        vsix_cmd += " --force"
    result = run(vsix_cmd)
    print(result)

    # Checking if vscode version is too old
    if "as it is not compatible with VS Code" in result:  # Error message with "VS Code" same for VSCode and VSCodium
        vscode_version = result.split("as it is not compatible with VS Code")[1].split("'")[1]
        error(f"ERROR: Try updating your current VSCode ({vscode_version})")
    # Checking if extension installation failed
    if "Failed Installing Extensions" in result:
        error("ERROR: Unable to install extension with: %s" % ext_path)

    # Creating file for audit
    with open(audit_file, "w") as f:
        f.write(get_installed_vscod_ext_version(app_bin_path, ext_uid_name))


def get_temp_dir():
    if get_os_name() == "Windows":
        temp_dir_path = makepath("C:", "Windows", "Temp")
    else:
        temp_dir_path = "/var/tmp"

    return temp_dir_path


def get_vscode_path():
    if get_os_name() == "Windows":
        app_dir = makepath(programfiles, "Microsoft VS Code")
        app_bin_path = makepath(app_dir, "bin", "code")
    elif get_os_name() == "Linux":
        app_dir = makepath("/", "usr", "share", "code")
        app_bin_path = makepath(app_dir, "bin", "code")
    elif get_os_name() == "Darwin":
        app_dir = makepath("/", "Applications", "Visual Studio Code.app")
        app_bin_path = makepath(app_dir, "Contents", "Resources", "app", "bin", "code")

    return app_dir, app_bin_path


def get_vscodium_path():
    if get_os_name() == "Windows":
        app_dir = makepath(programfiles, "VSCodium")
        app_bin_path = makepath(app_dir, "bin", "codium")
    elif get_os_name() == "Linux":
        app_dir = makepath("/", "usr", "share", "codium")
        app_bin_path = makepath(app_dir, "bin", "codium")
    elif get_os_name() == "Darwin":
        app_dir = makepath("/", "Applications", "VSCodium.app")
        app_bin_path = makepath(app_dir, "Contents", "Resources", "app", "bin", "codium")

    return app_dir, app_bin_path


def remove_tree_for_all_users(user_folder_relative_path, ignored_users=None, ignore_system_users=True):
    r"""Remove a specific folder or folders for all user's profiles

    Args:
        user_folder_relative_path (str): relative path to user folder, glob patterns can be used
        ignored_users (str or list of str): ignore specified users
        ignore_system_users (bool): ignore default, public, all users, etc. True by default

    Returns:
        list: list of deleted folders

    >>> print(remove_tree_for_all_users(makepath(".vscode", "extensions", "ms-toolsai.jupyter-*")))
    ['C:\\Users\\username\\.vscode\\extensions\\ms-toolsai.jupyter-2022.2.1001817079', 'C:\\Users\\username\\.vscode\\extensions\\ms-toolsai.jupyter-keymap-1.0.0', 'C:\\Users\\username\\.vscode\\extensions\\ms-toolsai.jupyter-renderers-1.0.6']

    >>> print(remove_tree_for_all_users(makepath(".vscode", "extensions", "ms-toolsai.jupyter-")))
    []

    >>> print(remove_tree_for_all_users(makepath(".vscode", "extensions", "ms-toolsai.jupyter-[a-z]*")))
    ['C:\\Users\\username\\.vscode\\extensions\\ms-toolsai.jupyter-keymap-1.0.0', 'C:\\Users\\username\\.vscode\\extensions\\ms-toolsai.jupyter-renderers-1.0.6']

    >>> print(remove_tree_for_all_users(makepath(".vscode", "extensions", "ms-toolsai.jupyter-1.0.0")))
    ['/home/username/.vscode/extensions/ms-toolsai.jupyter-keymap-1.0.0']

    .. versionadded:: 2.3

    """
    skipped_users = get_skipped_users_list(ignored_users, ignore_system_users)
    deleted_folders = []

    if get_os_name() == "Windows":
        users_dir = makepath(systemdrive, "Users")
    elif get_os_name() == "Linux":
        users_dir = "/home"
    elif get_os_name() == "Darwin":
        users_dir = "/Users"

    for user_profile in glob.glob("%s/*/" % users_dir):
        skip = False
        for skipped_user in skipped_users:
            if user_profile.rstrip(os.path.sep).split(os.path.sep)[-1] == skipped_user:
                skip = True

        if not skip:
            for user_folder_to_delete in glob.glob(makepath(user_profile, user_folder_relative_path)):
                deleted_folders.append(user_folder_to_delete)
                remove_tree(user_folder_to_delete)
    return deleted_folders


def get_skipped_users_list(ignored_users=None, ignore_system_users=True):
    system_users_list = ["All Users", "Default", "Default User", "Public", "Shared"]
    if ignored_users is None:
        ignored_users = []
    if type(ignored_users) != list:
        ignored_users = [ignored_users]

    skipped_users = []
    if ignored_users:
        skipped_users.extend(ignored_users)
    if ignore_system_users:
        skipped_users.extend(system_users_list)

    return skipped_users
