#!/usr/bin/env python
# -----------------------------------------------------------------------------
# The proprietary software and information contained in this file is
# confidential and may only be used by an authorized person under a valid
# licensing agreement from Arm Limited or its affiliates.
#
# Copyright (C) 2025. Arm Limited or its affiliates. All rights reserved.
#
# This entire notice must be reproduced on all copies of this file and
# copies of this file may only be made by an authorized person under a valid
# licensing agreement from Arm Limited or its affiliates.
# -----------------------------------------------------------------------------

import argparse
from os import path

from datetime import datetime
from importlib import import_module

import asct.core.constants as constants
import asct.core.logger as log

from asct.core.asct_env import ASCTGlobalSettings as AGS
from asct.core.recipes.recipe_info import get_all_tags, TAG_DEFAULT
from asct.core.cmd.helpers.version_helpers import get_version
from asct.core.cmd.helpers.help_helpers import CustomHelpFormatter, RunHelpFormatter
from asct.core.resources.output_folder import OutputFolder
from asct.core.resources.resource_manager import ResourceManager
from asct.core.ubench_reporter import get_reporter


def handle_common_args(args):
    output_dir = getattr(args, "output_dir", None)
    if output_dir is None:
        return

    quiet = getattr(args, "quiet", False)
    log_file = getattr(args, "log_file", None)

    # ResourceManager follows the singleton pattern, for this reason
    # only one instance is used throughout the application
    resource_manager = ResourceManager()
    output_folder_resource = OutputFolder(output_dir, args.force)
    resource_manager.register(output_folder_resource, global_scope=True)
    resource_manager.apply_all(global_scope=True)
    get_reporter().output_dir = output_folder_resource.get_output_folder_path()

    # by default write logs to output/asct.log based on current log level
    # quiet flag gives the user the option to turn off logging to file as well
    if not quiet and log_file is None:
        log_file = path.join(args.output_dir, "asct.log")

    log.initialize(args.log_level, log_file, quiet)


def exec_cmd(command, args):
    handle_common_args(args)

    module = import_module(f"asct.core.cmd.{command}")
    module.run(args)


def exec_cmd_version(args):
    exec_cmd("version", args)


def exec_cmd_help(args):
    exec_cmd("asct_help", args)


def exec_cmd_list_benchmarks(args):
    exec_cmd("list_benchmarks", args)


def exec_cmd_run(args):
    exec_cmd("run", args)


def exec_cmd_system_info(args):
    exec_cmd("system_info", args)


def main():
    parser = argparse.ArgumentParser(
        description=f"ASCT: Arm System Characterization Tool (version: {get_version()})",
        add_help=False,
        usage="%(prog)s command [command_args…]",
        prog="asct",
        formatter_class=CustomHelpFormatter,
    )
    commands = parser.add_subparsers(dest="command", title=None, metavar="ASCT commands", required=True)

    # common args for commands that do not produce any file output
    common_no_wr_args = argparse.ArgumentParser(add_help=False)
    common_no_wr_args.add_argument(
        "--log-level",
        "-L",
        choices=log.get_log_levels(),
        type=str.lower,
        default=log.DEFAULT_LOG_LEVEL,
        help="Logging level (default: info)",
        metavar=f"[{','.join(map(str, log.get_log_levels()))}]",
    )

    # common args for commands that produce file output
    common_wr_args = argparse.ArgumentParser(add_help=False)
    common_wr_args.add_argument(
        "--log-file",
        type=str,
        default=None,
        help="File to output logging messages (default: asct.log)",
    )
    common_wr_args.add_argument(
        "--format",
        "-f",
        choices=constants.FORMAT_OPTIONS,
        type=str.lower,
        default="stdout",
        help="Output format (default: stdout)",
        metavar=f"[{','.join(map(str, constants.FORMAT_OPTIONS))}]",
    )
    common_wr_args.add_argument(
        "--output-dir",
        "-o",
        type=str,
        default=f"data.{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}",
        help="Path to the output directory (default: 'data.<YYYYMMDD_HHMMSS_microseconds>' in cwd)",
    )
    common_wr_args.add_argument(
        "--force", action="store_true", default=False, help="Reuse the output directory if it exists"
    )
    common_wr_args.add_argument(
        "--quiet",
        "-q",
        action="store_true",
        help="Disable default logging to the asct.log file, and all output to stdout/stderr,"
        " including critical errors and log messages. Use --log-file to capture and view logs.",
    )

    # 'version' command
    version_cmd = commands.add_parser(
        "version", add_help=False, parents=[common_no_wr_args], help="Display the version of ASCT"
    )
    version_cmd.set_defaults(func=exec_cmd_version)

    # 'help' command
    help_cmd = commands.add_parser(
        "help", add_help=False, parents=[common_no_wr_args], help="Display the general ASCT help page"
    )
    help_cmd.set_defaults(func=exec_cmd_help, parser=parser)

    # 'list' command
    list_bm_cmd = commands.add_parser(
        "list",
        usage="asct list [options...]",
        parents=[common_no_wr_args, common_wr_args],
        help="Get a list of available benchmarks",
    )
    list_bm_cmd.set_defaults(func=exec_cmd_list_benchmarks)

    # 'system-info' command
    sysreport_cmd = commands.add_parser(
        "system-info",
        usage="asct system-info [options...]",
        parents=[common_no_wr_args, common_wr_args],
        help="Get system information",
    )
    sysreport_cmd.set_defaults(func=exec_cmd_system_info)

    # 'run' command
    run_cmd = commands.add_parser(
        "run",
        usage="asct run keyword1 keyword2... [options...]",
        parents=[common_no_wr_args, common_wr_args],
        help="Runs a list of benchmarks based on a provided list of keywords",
        formatter_class=RunHelpFormatter,
    )

    run_cmd.add_argument(
        "benchmarks",
        nargs="*",
        default=TAG_DEFAULT,
        choices=get_all_tags(),
        help=argparse.SUPPRESS,
    )

    run_cmd.add_argument(
        "--dev-mode",
        action="store_true",
        help=argparse.SUPPRESS,  # hidden option - only for development, don't expose to users
    )

    run_cmd.add_argument(
        "--quick-mode",
        action="store_true",
        help=argparse.SUPPRESS,  # hidden option - only for testing, don't expose to users
    )

    run_cmd.add_argument(
        "--dry-run",
        action="store_true",
        help="Prints a list of benchmarks that would run based on the provided "
        "command line arguments and exits immediately",
    )

    run_cmd.add_argument(
        "--no-progress-bar",
        action="store_true",
        help="Disable the progress bar, use single line update messages instead",
    )
    run_cmd.set_defaults(func=exec_cmd_run)

    AGS().read_env_vars()

    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
