# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

from dataclasses import asdict, dataclass, field, replace
import logging
import os
from omegaconf.omegaconf import OmegaConf
import sys
from typing import Any, Dict, List


log = logging.getLogger(__name__)


@dataclass(frozen=True)
class Configuration:
    # TODO: should this be handled via enum?
    activation_method: str = field(default="all")
    compliant_branch: str = field(default="^refs/heads/main$")
    source_branch: str = field(default="")
    component_cli_version: str = field(default="0.1.0.36279725")
    component_specification_glob: str = field(default="**/spec.yaml")
    # TODO: consider a way of supporting both this and `*.yaml` as defaults.
    configuration_file: str = field(default="aml-build-configuration.yml")
    log_format: str = field(default="%(message)s")
    shell_command_timeout_in_seconds: int = field(default=240)
    # TODO: should this be handled via enum?
    signing_mode: str = field(default="aml")
    verbose: bool = field(default=False)
    working_directory: str = field(default_factory=lambda: os.getcwd())
    workspaces: List[str] = field(default_factory=lambda: [])
    makecat_directory: str = field(default=r"C:\Program Files (x86)\Windows Kits")
    makecat_default: str = field(default=r"10\bin\x64\makecat.exe")
    # allow_duplicate_versions is on path to deprecation. Please avoid using it
    allow_duplicate_versions: bool = field(default=False)
    fail_if_version_exists: bool = field(default=False)
    use_build_number: bool = field(default=False)
    all_component_version: str = field(default="")
    disable_telemetry: bool = field(default=False)


def load_configuration() -> Configuration:
    """
    Create configuration object from "implicit" command line arguments and
    environment variables.
    """
    # Strip away the first argument, which is the name of the file being
    # executed.
    args = sys.argv[1:]
    env = os.environ
    rv = load_configuration_from_args_and_env(args, dict(env))
    return rv


def load_configuration_from_args(args) -> dict:
    """
    Load a "minimal" configuration dictionary from command line arguments. This
    strips away any values which are default, so that merging with the default
    and file-based configuration objects works properly.
    """
    from argparse_dataclass import ArgumentParser

    default_config = Configuration()
    parser = ArgumentParser(Configuration)

    cli_config = parser.parse_args(args)
    cli_config = asdict(cli_config)

    for key in list(cli_config.keys()):
        if cli_config[key] == getattr(default_config, key):
            del cli_config[key]

    return cli_config


def load_configuration_from_args_and_env(
    args: List[str], env: Dict[str, Any]
) -> Configuration:
    """
    Load configuration file from provided command line arguments and environment
    variables.

    Priority is documented at https://aka.ms/aml/amlbuild , from lowest to
    highest:
    - default value
    - configuration file
    - command line arguments (highest priority)
    """

    default_config = Configuration()

    cli_config = load_configuration_from_args(args)

    cli_config_path = cli_config.get("configuration_file")
    if cli_config_path is not None and os.path.isfile(cli_config_path):
        log.info("Loading user provided configuration file")
        file_config = OmegaConf.load(cli_config_path)
    elif os.path.isfile(default_config.configuration_file):
        log.info(
            "Configuration file does not exist. Loading default configuration file aml-build-configuration.yml."
        )
        file_config = OmegaConf.load(default_config.configuration_file)
    else:
        log.info(
            "User provided/default configuration file does not exist. Using default configuration."
        )
        file_config = None

    if file_config is None:
        log.info("Configuration file is empty. Using default configuration.")
        cli_and_file_config = cli_config
    else:
        log.info("Overriding default configuration by configuration file.")
        cli_and_file_config = OmegaConf.merge(file_config, cli_config)

    if cli_and_file_config.get("workspaces") is None:  # type: ignore
        log.error(
            "Workspace is not configured. Please update in your configuration file."
        )

    if cli_and_file_config.get("allow_duplicate_versions") is not None:  # type: ignore
        if cli_and_file_config.get("fail_if_version_exists") is None:  # type: ignore
            cli_and_file_config.update(
                {
                    "fail_if_version_exists": not cli_and_file_config.get(
                        "allow_duplicate_versions"  # type: ignore
                    )
                }
            )
            log.warning(
                "We recommend against using the parameter allow_duplicate_versions. Please specify fail_if_version_exists instead."
            )
        else:
            log.error(
                "Please don't specify both allow_duplicate_versions and fail_if_version_exists."
            )
        log.info("Please refer to https://aka.ms/aml/amlbuild for more information.")

    config = OmegaConf.merge(default_config, cli_and_file_config)
    config = Configuration(**config)  # type: ignore

    # Load the environment variable of source branch into config
    if "BUILD_SOURCEBRANCH" in env.keys():
        config = replace(config, source_branch=env["BUILD_SOURCEBRANCH"])
    else:
        log.warning("BUILD_SOURCEBRANCH is not in the environment variable list.")

    # Load the environment variable of build number into config, if user_build_number=True
    if config.use_build_number:
        if "BUILD_BUILDNUMBER" in env.keys():
            if config.all_component_version:
                log.warning(
                    f"The build number {env['BUILD_BUILDNUMBER']} overwrites the value of all_component_version {config.all_component_version}"
                )
            config = replace(config, all_component_version=env["BUILD_BUILDNUMBER"])
        else:
            log.error("BUILD_BUILDNUMBER is not in the environment variable list.")

    return config
