# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
#
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is
# regenerated.
# --------------------------------------------------------------------------
# pylint: disable=too-many-lines
# pylint: disable=unused-argument
# pylint: disable=line-too-long

import os
import signal
import subprocess
from knack.prompting import prompt_pass
from azure.cli.core.azclierror import MutuallyExclusiveArgumentError
from azure.cli.core.azclierror import RequiredArgumentMissingError
from azure.cli.core.azclierror import UnclassifiedUserFault
from azext_datamigration.manual import helper


# -----------------------------------------------------------------------------------------------------------------
# Assessment Command Implementation.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_assessment(connection_string=None,
                             output_folder=None,
                             overwrite=False,
                             config_file_path=None):

    try:

        # Setup the console app
        defaultOutputFolder, exePath = helper.console_app_setup()

        # Specifying both parameters is an error
        if connection_string is not None and config_file_path is not None:
            raise MutuallyExclusiveArgumentError("Both connection_string and config_file_path are mutually exclusive arguments. Please provide only one of these arguments.")

        # When Connection string.
        if connection_string is not None:

            # Formating for multiple connection string
            connection_string = " ".join(f"\"{i}\"" for i in connection_string)

            # Joining parameters in a string
            cmd = f'{exePath} Assess --sqlConnectionStrings {connection_string} ' if output_folder is None else f'{exePath} Assess --sqlConnectionStrings {connection_string} --outputFolder "{output_folder}" '
            cmd += '--overwrite False' if overwrite is False else ''
            subprocess.call(cmd, shell=False)

        # When config file.
        elif config_file_path is not None:
            helper.validate_config_file_path(config_file_path, "assess")
            cmd = f'{exePath} --configFile "{config_file_path}"'
            subprocess.call(cmd, shell=False)

        # if no parameter is provided.
        else:
            raise RequiredArgumentMissingError('No valid parameter set used. Please provide any one of the these prameters: connection_string, config_file_path')

        # Printing log file path
        logFilePath = os.path.join(defaultOutputFolder, "Logs")
        print(f"Event and Error Logs Folder Path: {logFilePath}")

    except Exception as e:
        raise e


# -----------------------------------------------------------------------------------------------------------------
# Performance Data Collection Command Implementation.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_performance_data_collection(connection_string=None,
                                              output_folder=None,
                                              perf_query_interval=30,
                                              static_query_interval=3600,
                                              number_of_iteration=20,
                                              config_file_path=None,
                                              time=None):

    try:

        # Setup the console app
        defaultOutputFolder, exePath = helper.console_app_setup()

        if connection_string is not None and config_file_path is not None:
            raise MutuallyExclusiveArgumentError("Both sql_connection_string and config_file_path are mutually exclusive arguments. Please provide only one of these arguments.")

        # When Connection string.
        if connection_string is not None:

            # Formating for multiple connection string
            connection_string = " ".join(f"\"{i}\"" for i in connection_string)

            # parameter set for Perfornace data collection
            parameterList = {
                "--outputFolder": output_folder,
                "--perfQueryIntervalInSec": perf_query_interval,
                "--staticQueryIntervalInSec": static_query_interval,
                "--numberOfIterations": number_of_iteration
            }

            # joining paramaters together in a string
            cmd = f'{exePath} PerfDataCollection --sqlConnectionStrings {connection_string}'
            for param in parameterList:
                if parameterList[param] is not None:
                    cmd += f' {param} "{parameterList[param]}"'

            # If time parameter is specified, catch TimeoutExpired exception and terminate the process
            if time is None:
                subprocess.call(cmd, shell=False)
            else:
                sp = subprocess.Popen(cmd, shell=False)
                try:
                    outs, errs = sp.communicate(timeout=time)
                except subprocess.TimeoutExpired:
                    sp.send_signal(signal.SIGTERM)
                    outs, errs = sp.communicate()

        # When Config file.
        elif config_file_path is not None:
            helper.validate_config_file_path(config_file_path, "perfdatacollection")
            cmd = f'{exePath} --configFile "{config_file_path}"'

            # If time parameter is specified, catch TimeoutExpired exception and terminate the process
            if time is None:
                subprocess.call(cmd, shell=False)
            else:
                sp = subprocess.Popen(cmd, shell=False)
                try:
                    outs, errs = sp.communicate(timeout=time)
                except subprocess.TimeoutExpired:
                    sp.send_signal(signal.SIGTERM)
                    outs, errs = sp.communicate()

        else:
            raise RequiredArgumentMissingError('No valid parameter set used. Please provide any one of the these prameters: sql_connection_string, config_file_path')

        # Printing log file path
        logFilePath = os.path.join(defaultOutputFolder, "Logs")
        print(f"Event and Error Logs Folder Path: {logFilePath}")

    except Exception as e:
        raise e


# -----------------------------------------------------------------------------------------------------------------
#  Get SKU Recommendation Command Implementation.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_get_sku_recommendation(output_folder=None,
                                         target_platform="Any",
                                         target_sql_instance=None,
                                         target_percentile=95,
                                         scaling_factor=100,
                                         start_time=None,
                                         end_time=None,
                                         overwrite=False,
                                         display_result=False,
                                         elastic_strategy=False,
                                         database_allow_list=None,
                                         database_deny_list=None,
                                         config_file_path=None):

    try:

        # Setup Console app
        defaultOutputFolder, exePath = helper.console_app_setup()

        if output_folder is not None and config_file_path is not None:
            raise MutuallyExclusiveArgumentError("Both output_folder and config_file_path are mutually exclusive arguments. Please provide only one of these arguments.")

        # When Config file - Handling this case first to allow no parameter to be specified (runs non config file scenario)
        if config_file_path is not None:
            helper.validate_config_file_path(config_file_path, "getskurecommendation")
            cmd = f'{exePath} --configFile "{config_file_path}"'
            subprocess.call(cmd, shell=False)

        # When non-config file
        else:

            # parameter set for Sku recommendation
            parameterList = {
                "--outputFolder": output_folder,
                "--targetPlatform": target_platform,
                "--targetSqlInstance": target_sql_instance,
                "--scalingFactor": scaling_factor,
                "--targetPercentile": target_percentile,
                "--startTime": start_time,
                "--endTime": end_time,
                "--overwrite": overwrite,
                "--displayResult": display_result,
                "--elasticStrategy": elastic_strategy,
                "--databaseAllowList": database_allow_list,
                "--databaseDenyList": database_deny_list
            }
            cmd = f'{exePath} GetSkuRecommendation'

            # formating the parameter list into a string
            for param in parameterList:
                if parameterList[param] is not None and not param.__contains__("List"):
                    cmd += f' {param} "{parameterList[param]}"'

                # in case the parameter input is list format it accordingly
                elif param.__contains__("List") and parameterList[param] is not None:
                    parameterList[param] = " ".join(f"\"{i}\"" for i in parameterList[param])
                    cmd += f' {param} {parameterList[param]}'
            subprocess.call(cmd, shell=False)

        # Printing log file path
        logFilePath = os.path.join(defaultOutputFolder, "Logs")
        print(f"Event and Error Logs Folder Path: {logFilePath}")

    except Exception as e:
        raise e


# -----------------------------------------------------------------------------------------------------------------
# Register Sql Migration Service on IR command Implementation.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_register_ir(auth_key,
                              ir_path=None,
                              installed_ir_path=None):

    helper.validate_os_env()

    # This command can only be run as admin and in windows
    if not helper.is_user_admin():
        raise UnclassifiedUserFault("Failed: You do not have Administrator rights to run this command. Please re-run this command as an Administrator!")
    helper.validate_input(auth_key)

    # Run installation if ir_path is provided
    if ir_path is not None:
        helper.install_gateway(ir_path)

    # register or re-register Dms on ir
    helper.register_ir(auth_key, installed_ir_path)


# -----------------------------------------------------------------------------------------------------------------
#  Migrate logins from the source Sql Servers to the target Azure Sql Servers.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_login_migration(src_sql_connection_str=None,
                                  tgt_sql_connection_str=None,
                                  csv_file_path=None,
                                  list_of_login=None,
                                  output_folder=None,
                                  aad_domain_name=None,
                                  config_file_path=None):

    try:

        # Setup the console app
        defaultOutputFolder, exePath = helper.loginMigration_console_app_setup()

        if src_sql_connection_str is not None and config_file_path is not None:
            raise MutuallyExclusiveArgumentError("Both src_sql_connection_str and config_file_path are mutually exclusive arguments. Please provide only one of these arguments.")

        # When Connection string.
        if src_sql_connection_str is not None and tgt_sql_connection_str is not None:
            # Formating for multiple source connection string
            src_sql_connection_str = " ".join(f"\"{i}\"" for i in src_sql_connection_str)

            # parameter set for Perfornace data collection
            parameterList = {
                "--targetSqlConnectionString": tgt_sql_connection_str,
                "--csvFilePath": csv_file_path,
                "--listOfLogin": list_of_login,
                "--outputFolder": output_folder,
                "--aadDomainName": aad_domain_name
            }

            # joining paramaters together in a string
            cmd = f'{exePath} LoginsMigration --sourceSqlConnectionString {src_sql_connection_str}'

            # formating the parameter list into a string
            for param in parameterList:
                if parameterList[param] is not None and not param.__contains__("list"):
                    cmd += f' {param} "{parameterList[param]}"'

                # in case the parameter input is list format it accordingly
                elif param.__contains__("list") and parameterList[param] is not None:
                    parameterList[param] = " ".join(f"\"{i}\"" for i in parameterList[param])
                    cmd += f' {param} {parameterList[param]}'
            subprocess.call(cmd, shell=False)

        # When Config file.
        elif config_file_path is not None:
            helper.test_path_exist(config_file_path)
            cmd = f'{exePath} --configFile "{config_file_path}"'
            subprocess.call(cmd, shell=False)

        else:
            raise RequiredArgumentMissingError('No valid parameter set used. Please provide config_file_path or required prameters: src_sql_connection_str, tgt_sql_connection_str, csv_file_path or list_of_login.')

        # Printing log file path
        logFilePath = os.path.join(defaultOutputFolder, "LoginsMigrationLogs")
        from knack.log import get_logger
        logger = get_logger(__name__)
        logger.warning(f"If outputFolder parameter is not provided, the default event and error logs folder path: {logFilePath}")

    except Exception as e:
        raise e


# -----------------------------------------------------------------------------------------------------------------
#  Migrate TDE certificate from source SQL Server to the target Azure SQL Server.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_tde_migration(source_sql_connection_string=None,
                                target_subscription_id=None,
                                target_resource_group_name=None,
                                target_managed_instance_name=None,
                                network_share_path=None,
                                network_share_domain=None,
                                network_share_user_name=None,
                                network_share_password=None,
                                database_name=None):

    if source_sql_connection_string is None:
        source_sql_connection_string = prompt_pass('Connection String:', confirm=False)

    if network_share_password is None:
        network_share_password = prompt_pass('Network Share Password:', confirm=False)

    try:
        # Setup the console app
        exePath = helper.tdeMigration_console_app_setup()

        if exePath is None:
            return

        if os.path.exists(exePath) is False:
            print("Failed to locate executable.")
            return

        cmd = f'{exePath} --sourceSqlConnectionString "{source_sql_connection_string}" --targetSubscriptionId "{target_subscription_id}" --targetResourceGroupName "{target_resource_group_name}" --targetManagedInstanceName "{target_managed_instance_name}" --networkSharePath "{network_share_path}" --networkShareDomain "{network_share_domain}" --networkShareUserName "{network_share_user_name}" --networkSharePassword "{network_share_password}" --clientType "CLI" --databaseName'

        for db in database_name:
            cmd += " \"" + db + "\""

        subprocess.call(cmd, shell=False)

    except Exception as e:
        raise e


# -----------------------------------------------------------------------------------------------------------------
#  Migrate schema from the source Sql Servers to the target Azure Sql Servers.
# -----------------------------------------------------------------------------------------------------------------
def datamigration_sql_server_schema(action=None,
                                    src_sql_connection_str=None,
                                    tgt_sql_connection_str=None,
                                    input_script_file_path=None,
                                    output_folder=None,
                                    config_file_path=None):

    # Error message strings
    actionError = "Please provide valid action value."
    srcConnectionError = "Please provide src_sql_connection_str value."
    tgtConnectionError = "Please provide tgt_sql_connection_str value."
    inputFilePathError = "Please provide input_script_file_path value for the action of DeploySchema."

    try:
        # Setup the console app
        defaultOutputFolder, exePath = helper.sqlServerSchema_console_app_setup()

        if config_file_path is not None:
            helper.test_path_exist(config_file_path)
            print(f"Run through the provided config file:")
            cmd = f'{exePath} --configFile "{config_file_path}"'
            subprocess.call(cmd, shell=False)
        else:
            if action is None:
                raise RequiredArgumentMissingError(actionError)

            if src_sql_connection_str is None:
                raise RequiredArgumentMissingError(srcConnectionError)

            if tgt_sql_connection_str is None:
                raise RequiredArgumentMissingError(tgtConnectionError)

            if action == "DeploySchema":
                if input_script_file_path is None:
                    raise RequiredArgumentMissingError(inputFilePathError)
                else:
                    helper.test_path_exist(input_script_file_path)

            # parameter set for Perfornace data collection
            parameterList = {
                "--sourceConnectionString": src_sql_connection_str,
                "--targetConnectionString": tgt_sql_connection_str,
                "--inputScriptFilePath": input_script_file_path,
                "--outputFolder": output_folder
            }

            # joining paramaters together in a string
            cmd = f'{exePath} {action}'

            # formating the parameter list into a string
            for param in parameterList:
                if parameterList[param] is not None and not param.__contains__("list"):
                    cmd += f' {param} "{parameterList[param]}"'

                # in case the parameter input is list format it accordingly
                elif param.__contains__("list") and parameterList[param] is not None:
                    parameterList[param] = " ".join(f"\"{i}\"" for i in parameterList[param])
                    cmd += f' {param} {parameterList[param]}'

            subprocess.call(cmd, shell=False)

    except Exception as e:
        raise e
