import argparse
import hashlib
import os
import platform
import re
import subprocess
import sys
import tarfile
import uuid
import getpass

from datetime import datetime, date, timedelta
from em_version_handler import EmVersionHandler
from file_management_handler import FileManagementHandler
from em_encryption_handler import EmEncryptionManager
from error_handler import ExportErrorMessages
from logging_handler import LoggerHandler
from qualifications_validator import QualificationsValidator
from archive_handler import ArchiveHandler

# Sibling import: import-side helper used to fetch CTM names via `em util`.
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                "..", "import", "scripts"))
from exp_file_filter_import import ExpFileFilterImporter


class SaasMigrationExport:
    """
    This class handling the saas migration export

    """

    def __init__(self, logs_dir, log_level,skip_verify_ctm_os):
        self.em_home = None
        self.install_home = None
        self.em_export_directory = None
        self.em_version = None
        self.operating_system = None
        self.log_directory = None
        self.log_file_name = None
        self.timezone_files_path = None
        # Initialize EM home and logger:
        # Note: its important to initialize the logger before any logger print to prevent missing log file.
        self.initialize_em_home()
        self.export_uuid = uuid.uuid4()
        self.initialize_logger(logs_dir=logs_dir, log_level=log_level)
        self.export_version_file_name = 'ExportScriptVersion.txt'
        self.export_folder_name = 'EMExportForSaaS'
        self.installed_versions_file_name = 'installed-versions.txt'
        self.em_export_script_version = '9.0.27.083'
        self.em_version_file_name = 'em_version.txt'
        self.checksum_file_name = 'checksum.txt'
        self.qualification_report_file_name = 'EMSaaSQualification.xlsx'
        self.timezone_dat_file_name = 'TimeZone.dat'
        self.timezone_json_file_name = 'TimeZone.json'
        self.ctm_package_filename = 'ctm-package'
        self.qualifications_validator = QualificationsValidator(skip_verify_ctm_os)

    def initialize_env_for_running_export_script(self, export_file_location, alternative_tz_files_location):
        """
        1.  Initialize operating system global variable
        2.  Initialize installed_versions_path variable
        3.  Initialize path variable of timezone files
        4.  Initialize export directory variable
        5.  Initialize em_version global variable
        6.  Removing EMExportForSaaS directory if exist
        7.  Creating EMExportForSaaS directory
        """
        self.initialize_operating_system()
        self.initialize_em_install_versions_path()
        self.initialize_timezone_files_path(alternative_tz_files_location)
        self.initialize_export_directory(export_file_location)
        self.initialize_em_latest_installed_version(self.install_home, self.installed_versions_file_name)
        SaasMigrationExport.remove_export_for_saas_dir(self.em_export_directory, self.export_folder_name)
        SaasMigrationExport.create_export_for_saas_dir(export_file_location, self.export_folder_name)

    def initialize_timezone_files_path(self, alternative_tz_files_location):
        """
        initialize timezone_files path.
        """
        if alternative_tz_files_location != 'default':
            self.timezone_files_path = alternative_tz_files_location
        elif self.operating_system == 'Windows':
            self.timezone_files_path = os.path.join(str(self.install_home), "Data")
        else:
            self.timezone_files_path = os.path.join(str(self.install_home), "ctm_em/etc/cli/data")
        if self.install_home is None:
            raise Exception(ExportErrorMessages.HOME_ENV_VARIABLE_IS_MISSING.value)

    def initialize_logger(self, logs_dir, log_level):
        """
        Initialize the logger for the script
        """
        self.log_directory = logs_dir
        if logs_dir is None:
            if platform.system() == 'Windows':
                self.log_directory = os.path.join(self.em_home, 'Log')
            else:
                self.log_directory = os.path.join(self.em_home, 'log')
        elif not os.path.exists(self.log_directory):
            os.makedirs(self.log_directory)

        self.log_file_name = f"{self.log_directory}{os.sep}saas_migration_export_script_" \
                             f"{datetime.now().strftime('%Y_%m_%d_%H_%M_%S')}_{self.export_uuid}.log"
        LoggerHandler.init_logger(log_level=log_level,
                                  log_format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
                                  log_file_name=self.log_file_name)

    def initialize_operating_system(self):
        """

        :return: The operating system of the machine
        """
        LoggerHandler.print_message("Retrieving current operating system", logger_level='debug')
        if platform.system() == 'Linux':
            self.operating_system = 'Linux'
            LoggerHandler.print_message("Current operating system is Linux", logger_level='debug')
        elif platform.system() == 'Windows':
            self.operating_system = 'Windows'
            LoggerHandler.print_message("Current operating system is Windows", logger_level='debug')
        elif platform.system() == 'AIX':
            self.operating_system = 'AIX'
            LoggerHandler.print_message("Current operating system is AIX", logger_level='debug')

    def initialize_export_directory(self, export_file_location):
        """

        :return: initialize EMExportForSaaS global variable
        """
        self.em_export_directory = os.path.join(export_file_location, str(self.export_folder_name))

    def initialize_em_home(self):
        """
        Initialize the EM_HOME global variable.
        :return: initialize em_home global variable
        """
        self.em_home = os.getenv('EM_HOME')
        if self.em_home is None:
            raise Exception(ExportErrorMessages.EM_HOME_ENV_VARIABLE_IS_MISSING.value)

    def initialize_em_install_versions_path(self):
        """
        initialize install_home path.
        """
        if self.operating_system == 'Windows':
            self.install_home = os.getenv('EM_HOME')
        else:
            self.install_home = os.getenv('HOME')
        if self.install_home is None:
            raise Exception(ExportErrorMessages.HOME_ENV_VARIABLE_IS_MISSING.value)

    @staticmethod
    def remove_export_for_saas_dir(directory, folder_name):
        """
        Delete ExportForSaaS directory only if it exists.
        """
        return FileManagementHandler.remove_dir(directory, folder_name)

    @staticmethod
    def create_export_for_saas_dir(directory_path, folder_name):
        """
        Create a directory and its parent directories if they do not exist.

        Parameters:
        - directory_path (str): The path to the directory to be created.
        """
        FileManagementHandler.create_folder(directory_path, folder_name)
        LoggerHandler.print_message(f"Finished initializing '{folder_name}' under '{directory_path}'")

    @staticmethod
    def display_output(output):
        """
        :param output: receive em util export output
        :return: display em util export output after removing all progress bar prints
        """
        output_lines = output.split('\n')
        # Remove lines containing '%'
        filtered_lines = [line for line in output_lines if '%' not in line and line != '']
        # Display the result
        for line in filtered_lines:
            LoggerHandler.print_message(line)

    # Resource directive that makes 'em util' dump SERVICE_LOG through a server-side DB
    # cursor instead of loading the whole table into memory (avoids OOM on large logs).
    DEFAULTS_RSC_SERVICE_LOG_CURSOR = 'UTIL_sel_cursor SERVICE_LOG POSTGRESQL Y'

    def ensure_service_log_select_cursor_in_defaults_rsc(self):
        """
        Ensure the EM resource file '$EM_HOME/etc/resource/Defaults.rsc' contains the
        SERVICE_LOG select-cursor directive before exporting data.

        'em util' reads 'UTIL_sel_cursor <TABLE> POSTGRESQL Y' from Defaults.rsc and, when
        present, streams that table via a server-side DB cursor rather than materializing
        it in memory. SERVICE_LOG can be very large, so this prevents out-of-memory
        failures while it is dumped during the export.

        The directive is appended only if it is not already present (idempotent). Any
        failure here is logged but does NOT abort the export.
        """
        defaults_rsc_path = os.path.join(self.em_home, 'etc', 'resource', 'Defaults.rsc')
        try:
            if not os.path.isfile(defaults_rsc_path):
                LoggerHandler.print_message(
                    f"Defaults.rsc not found at '{defaults_rsc_path}', skipping SERVICE_LOG select-cursor update.",
                    logger_level='warning')
                return

            with open(defaults_rsc_path, 'r') as defaults_file:
                existing_lines = defaults_file.readlines()

            for line in existing_lines:
                tokens = line.split()
                if len(tokens) >= 2 and tokens[0] == 'UTIL_sel_cursor' and tokens[1] == 'SERVICE_LOG':
                    LoggerHandler.print_message(
                        f"'{self.DEFAULTS_RSC_SERVICE_LOG_CURSOR}' already present in '{defaults_rsc_path}', no change needed.")
                    return

            needs_leading_newline = bool(existing_lines) and not existing_lines[-1].endswith('\n')
            with open(defaults_rsc_path, 'a') as defaults_file:
                if needs_leading_newline:
                    defaults_file.write('\n')
                defaults_file.write(self.DEFAULTS_RSC_SERVICE_LOG_CURSOR + '\n')
            LoggerHandler.print_message(
                f"Added '{self.DEFAULTS_RSC_SERVICE_LOG_CURSOR}' to '{defaults_rsc_path}'.", screen_print=True)
        except Exception as e:
            LoggerHandler.print_message(
                f"Failed to update Defaults.rsc at '{defaults_rsc_path}' with the SERVICE_LOG select-cursor directive: {str(e)}",
                logger_level='warning')

    def run_em_util_export(self, net_history_days_4_export):
        """
        :return: This method run em util export for all data types
        """
        # Make 'em util' dump SERVICE_LOG via a server-side DB cursor (see Defaults.rsc).
        # Must run before the export below, which is the data step shared by both the
        # full migration and the single-CTM migration.
        self.ensure_service_log_select_cursor_in_defaults_rsc()
        FileManagementHandler.create_folder(str(self.em_export_directory), "data")
        em_export_data_path = os.path.join(str(self.em_export_directory), "data")
        today = date.today()
        net_cmd = ''
        if net_history_days_4_export > 0:
            net_cmd = '-type net'
        for i in range(net_history_days_4_export):
            day = today - timedelta(days=i)
            net_cmd = net_cmd + ' -name ' + day.strftime("%Y%m%d")[2:] + '*'
        cmd = f"em util -mcs -export -type def -type cal -type dc -type user -type gc -type bim_forecast " \
              f"-type statistics -type workload {net_cmd} " \
              f"-type collect -type filter -type view -type hier -type sys -dir \"{em_export_data_path}\""
        LoggerHandler.print_message(f"Running '{cmd}'", screen_print=True)
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True,
                                   universal_newlines=True)
        # Wait for the process to finish and capture the output
        stdout, stderr = process.communicate()

        # Check the return code to see if the process was successful
        return_code = process.returncode
        LoggerHandler.print_message(f"em util return code: {return_code}")

        SaasMigrationExport.display_output(stdout)

        if return_code != 0:
            LoggerHandler.print_message("Standard Error:" + stderr)
            err = f"{ExportErrorMessages.RUNNING_EM_UTIL_EXPORT_FAILED.value} with return code: {return_code}" \
                  f" error: {stderr}"
            raise Exception(err)

    def encrypt_em_db_enc_key_file(self):
        """
        This method encrypt em_db_enc_key.txt using util command line
        :return:
        """
        LoggerHandler.print_message(f"Encrypting db_enc_key.txt file")
        EmEncryptionManager.encrypt_em_db_enc_key_file(self.em_export_directory, self.operating_system,
                                                       self.em_home, self.em_version)

    def initialize_em_latest_installed_version(self, directory, file_name):
        """

        :param directory: directory path
        :param file_name: file name
        initializing the em_version global variable
        """
        installed_version_path = os.path.join(directory, file_name)
        if not os.path.exists(installed_version_path):
            LoggerHandler.print_message(f"The file '{installed_version_path}' does not exist.")
            raise Exception(ExportErrorMessages.INSTALLED_VERSION_FILE_NOT_FOUND.value)

        version = EmVersionHandler.get_latest_version(installed_version_path, EmVersionHandler.get_em_product_suffix())
        LoggerHandler.print_message(f"EM_VERSION: {version}")
        self.em_version = version
        if self.em_version is None:
            raise Exception(ExportErrorMessages.FAILED_TO_RETRIEVE_EM_VERSION.value)

    @staticmethod
    def create_em_version_file(directory, file_name, version):
        """

        :param directory: directory path
        :param file_name:  file name
        :param version: em version
        :return: creates em_versions.txt file with under the  with the em version
        """
        FileManagementHandler.create_file(directory, file_name, version)
        if not os.path.exists(os.path.join(directory, file_name)):
            raise Exception(ExportErrorMessages.FAILED_TO_CREATE_EM_VERSION_FILE.value)

    @staticmethod
    def create_export_version_file(directory, file_name, version):
        """

        :param directory: directory path
        :param file_name: file name
        :param version: export version
        :return:
        """
        FileManagementHandler.create_file(directory, file_name, version)
        if not os.path.exists(os.path.join(directory, file_name)):
            raise Exception(ExportErrorMessages.FAILED_TO_CREATE_EXPORT_SCRIPT_VERSION_FILE.value)

    @staticmethod
    def create_tar_gz(source_folder, output_folder, output_filename):
        """

        :param source_folder: source folder path
        :param output_folder: tar creation folder path
        :param output_filename: tar filename
        :return: create taf from source folder at output_folder with output_filename
        """
        # Ensure source_folder exists
        if not os.path.exists(source_folder):
            raise FileNotFoundError(f"Source folder '{source_folder}' not found.")

        # Full path to the output tar.gz file
        output_path = os.path.join(output_folder, f"{output_filename}.tar.gz")

        # Create a tar.gz file
        with tarfile.open(output_path, "w:gz") as tar:
            tar.add(source_folder, arcname=os.path.basename(source_folder))

        if not os.path.exists(os.path.join(output_folder, output_filename + ".tar.gz")):
            raise Exception(ExportErrorMessages.FAILED_TO_CREATE_EXPORT_FOR_SAAS_TAR_GZ_FILE.value)

        LoggerHandler.print_message(f"'{output_filename}.tar.gz' file created at: '{output_path}'")
        return output_path

    @staticmethod
    def get_ctm_names_from_comm():
        names = [str(name).strip() for name in ExpFileFilterImporter.get_data_centers()]
        return sorted(names)

    @staticmethod
    def create_ctm_package_tar_gz(inner_em_tar_gz_path, output_folder, ctm_names, package_basename='ctm-package'):
        """
        Build ctm-package.tar.gz containing em/EMExportForSaaS.tar.gz and empty servers/<ctm>/ dirs.
        """
        if not os.path.isfile(inner_em_tar_gz_path):
            raise FileNotFoundError(f"Inner EM export archive not found: '{inner_em_tar_gz_path}'")

        # Full path to the outer ctm-package.tar.gz
        output_path = os.path.join(output_folder, f"{package_basename}.tar.gz")
        inner_arcname = f"em/{os.path.basename(inner_em_tar_gz_path)}"

        with tarfile.open(output_path, "w:gz") as tar:
            tar.add(inner_em_tar_gz_path, arcname=inner_arcname)

            servers_root_info = tarfile.TarInfo(name="servers/")
            servers_root_info.type = tarfile.DIRTYPE
            servers_root_info.mode = 0o755
            tar.addfile(servers_root_info)

            for ctm in ctm_names:
                dir_path = f"servers/{ctm}/"
                info = tarfile.TarInfo(name=dir_path)
                info.type = tarfile.DIRTYPE
                info.mode = 0o755
                tar.addfile(info)

        if not os.path.exists(output_path):
            raise Exception(ExportErrorMessages.FAILED_TO_CREATE_CTM_PACKAGE_TAR_GZ_FILE.value)

        LoggerHandler.print_message(f"'{package_basename}.tar.gz' file created at: '{output_path}'")
        return output_path

    def copy_log_file_to_export_dir(self, log_path, log_name):
        """

        :param log_path: log directory
        :param log_name: log filename
        :return: copy log file from log_path into export_directory
        """
        LoggerHandler.print_message(f"Copy log file '{log_name}' to '{self.em_export_directory}'")
        FileManagementHandler.copy_file(os.path.join(log_path, log_name), self.em_export_directory)

    @staticmethod
    def create_check_sum_file(source_folder, output_folder, output_filename, checksum_filename):
        """

        :param source_folder: source folder path for creating checksum file
        :param output_folder: output folder path which will create the tar file to calculate the checksum
        :param output_filename: the name of the tar gz file which is created under output_folder path
        :param checksum_filename: checksum filename
        :return:
        This method creates a tar.gz file from source_folder under output_folder path with output_filename,
        then creates checksum.txt file based on the tar.gz file which was created.
        then removes the tar.gz file which was created.

        This method is created so that the import process could verify that there is no data loss
        from the EMExportForSaas.tar.gz
        """
        LoggerHandler.print_message(f"Start creating '{checksum_filename}' under '{source_folder}'")
        tar_gz_file_path = SaasMigrationExport.create_tar_gz(source_folder, output_folder, output_filename)
        # open file for reading with gzip
        tar_ptr = tarfile.open(tar_gz_file_path, "r:gz")
        check_sum_value = SaasMigrationExport.hash_file(tar_ptr)
        tar_ptr.close()
        FileManagementHandler.create_file(source_folder, checksum_filename, check_sum_value)
        FileManagementHandler.remove_file(tar_gz_file_path)

    @staticmethod
    def hash_file(tar_file_ptr):
        sha512_hash = hashlib.sha512()
        for member in (tar_file_ptr.getmembers()):
            file_obj = tar_file_ptr.extractfile(member)
            if file_obj:
                for chunk in iter(lambda: file_obj.read(4096), b''):
                    sha512_hash.update(chunk)
        return sha512_hash.hexdigest()

    def call_remove_export_for_saas_dir(self, log_level):
        if log_level != 'debug':
            self.remove_export_for_saas_dir(self.em_export_directory, self.export_folder_name)

    def run_all_export_steps(self, log_dir, log_name, test_mode, export_file_location,
                             alternative_tz_files_location_requested, net_history_days_4_export):
        """

        1.  Create EmExportForSaas dir
        2.  Export the relevant types for SaaS :
            em util -mcs -export -type def -type cal -type dc -type user -type gc -type statistics
                                 -type workload -type collect -type filter -type view -type hier
                                 -type sys -dir $HOME/ExportForSaaS
        3.  Encrypt the em_db_enc_key.txt
        4.  Create version file  with the customer Onprem version in $HOME/ExportForSaaS.
        5.  Create a file with version number 1.0 so the import will know what is the required logic in the other side
        6.  Create checksum.txt file
        7.  Copy log file
        8.  If QualificationReport.xlsx exist under EM_HOME Copy it to ExportForSaaS dir
        9.  TAR and Zip $HOME/ExportForSaaS folder into EMExportForSaaS.tar.gz.
        10. Wrap that archive into ctm-package.tar.gz (em/ + servers/<ctm>/ from comm, empty server dirs).
        11. Remove standalone EMExportForSaaS.tar.gz from the export directory (only ctm-package remains).
        12. If log level is not in test mode - remove EMExportForSaas folder

        :return:
        """
        self.run_em_util_export(net_history_days_4_export)
        self.encrypt_em_db_enc_key_file()
        self.create_em_version_file(self.em_export_directory, self.em_version_file_name,
                                    EmVersionHandler.remove_fix_pack_from_em_version(self.em_version))
        self.create_export_version_file(self.em_export_directory, self.export_version_file_name,
                                        self.em_export_script_version)
        self.copy_timezone_files(alternative_tz_files_location_requested)
        self.create_check_sum_file(self.em_export_directory, export_file_location, self.export_folder_name,
                                   self.checksum_file_name)
        self.copy_log_file_to_export_dir(log_dir, log_name)
        self.copy_qualification_report_if_exist()
        inner_tar_path = self.create_tar_gz(self.em_export_directory, export_file_location, self.export_folder_name)
        ctm_names = SaasMigrationExport.get_ctm_names_from_comm()
        LoggerHandler.print_message(
            f"Adding {len(ctm_names)} CTMS server folder(s) under servers/ in {self.ctm_package_filename}.tar.gz")
        export_tar_path = SaasMigrationExport.create_ctm_package_tar_gz(
            inner_em_tar_gz_path=inner_tar_path,
            output_folder=export_file_location,
            ctm_names=ctm_names,
            package_basename=self.ctm_package_filename)
        # Inner archive is embedded under em/; do not leave a duplicate on disk
        FileManagementHandler.remove_file(inner_tar_path)
        LoggerHandler.print_message(f"Removed standalone inner archive after packaging: '{inner_tar_path}'")
        self.call_remove_export_for_saas_dir(test_mode)
        return export_tar_path

    def copy_timezone_files(self, alternative_tz_files_location_requested):
        """
        This method copy the timezone.json and timezone.dat into EMExportForSaas dir
        """
        # is_tz_managed = self.qualifications_validator.is_tz_managed()
        if alternative_tz_files_location_requested:
            timezone_dat_file = os.path.join(self.timezone_files_path, self.timezone_dat_file_name)
            if os.path.exists(timezone_dat_file):
                LoggerHandler.print_message(f"'{timezone_dat_file}' exist")
                FileManagementHandler.copy_file(timezone_dat_file, self.em_export_directory)
            else:
                LoggerHandler.print_message(
                    f"'{timezone_dat_file}' does not exist")
                raise FileNotFoundError(f"'{timezone_dat_file}' does not exist")
            timezone_json_file = os.path.join(self.timezone_files_path, self.timezone_json_file_name)
            if os.path.exists(timezone_json_file):
                LoggerHandler.print_message(f"'{timezone_json_file}' exist")
                FileManagementHandler.copy_file(timezone_json_file, self.em_export_directory)
            else:
                LoggerHandler.print_message(
                    f"'{timezone_json_file}' does not exist ")
                raise FileNotFoundError(f"'{timezone_json_file}' does not exist ")

    def copy_qualification_report_if_exist(self):
        """
        This method copy the QualificationReport.xlsx into EMExportForSaas dir if exist
        """
        qualification_report_path = os.path.join(self.em_home, self.qualification_report_file_name)
        if os.path.exists(qualification_report_path):
            LoggerHandler.print_message(f"'{self.qualification_report_file_name}' exist under '{self.em_home}'")
            FileManagementHandler.copy_file(qualification_report_path, self.em_export_directory)
        else:
            LoggerHandler.print_message(
                f"'{self.qualification_report_file_name}' does not exist under '{self.em_home}'")

    def run_qualifications_validation(self, skip_cjk_check, skip_verify_min_ctm_version, skip_verify_ctm_os,
                                      skip_verify_ctm_ha, skip_timezone_check, em_version, validate_servers):
        """
        :return: True if all the qualifications tests for export were validated successfully
        """
        return self.qualifications_validator.qualifications_validate(skip_cjk_check, skip_verify_min_ctm_version,
                                                                     skip_verify_ctm_os, skip_verify_ctm_ha,
                                                                     skip_timezone_check, em_version, validate_servers)

    def handle_archive_rules(self, aapi_url, aapi_credentials):
        """
        :
        """
        if ArchiveHandler.is_archive_active():
            if aapi_credentials == '':
                LoggerHandler.print_message(message=f"Workload Archiving System is detected.\n"
                                                    f"Please supply AAPI Token Or AAPI User and Password ('/' between them) in order to get access to the Archiving rules.\n",
                                            screen_print=True)
                # aapi_credentials = getpass.getpass("AAPI Credentials:")
                aapi_credentials = input("AAPI Credentials:")
                if aapi_credentials == '':
                    LoggerHandler.print_message('AAPI Credentials not supplied.')
                    raise Exception(ExportErrorMessages.AAPI_CREDENTIALS_NOT_SUPPLIED.value)
            ArchiveHandler.export_archive_rules(self.em_export_directory, aapi_url, aapi_credentials)
        else:
            LoggerHandler.print_message('archive is not active')

# Custom argument parser
class CustomArgumentParser(argparse.ArgumentParser):
    
    def error(self, message):
        print(f"\nError: {message}\n")
        self.format_help()
    
    def format_help(self):
        print("\nControl-M/EM SaaS Migration Export Utility")
        print("=" * 80)
        print("\nUsage: python saas_migration_export.py [OPTIONS]\n")
        print("Options:")
        print("-" * 80)
        for action in self._actions:
            if action.option_strings:
                option_str = ', '.join(action.option_strings)
                print(f"\n  {option_str}")
                if action.help:
                    # Indent help text for better readability
                    # Strip any argparse-added default suffix (appears on newline)
                    help_text = re.sub(r'\n\s*\(default:.*?\)\s*$', '', str(action.help))
                    print(f"      {help_text}")
        exit(0)


if __name__ == "__main__":
    log_file_name = None
    saas_mig_export = None
    log_directory = None
    is_test_mode = False
    try:
        parser = CustomArgumentParser(description="On-prem SaaS migration export script's args parser",add_help=False)
        parser.add_argument('-h', '--help', '--h','-help', action='store_true',help="Show help message and exit")
        parser.add_argument('--log_level', default='info', choices=['debug', 'info', 'warning', 'error'],
                            help="Script logger level (default: 'info')")
        parser.add_argument('--logs_dir', help="Script logs directory (default dir is under EM_HOME)")
        parser.add_argument('--test_mode', type=str, default='False', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Test mode (default: False)")
        parser.add_argument('--skip_verify_min_ctm_version', type=str, default='False', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip minimum CTM version verification (default: False)")
        parser.add_argument('--skip_verify_ctm_os', type=str, default='True', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip CTM OS verification (default: False)")
        parser.add_argument('--skip_verify_ctm_ha', type=str, default='True', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip CTM HA verification (default: True)")
        parser.add_argument('--skip_cjk_check', type=str, default='False', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip CJK check (default: False)")
        parser.add_argument('--skip_timezone_check', type=str, default='False', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip timezone check (default: False)")
        parser.add_argument('--export_file_location', default='default',
                            help="Export TAR directory (default is current folder)")
        parser.add_argument('--alternative_tz_files_location', default='default',
                            help="Include TimeZone.json and TimeZone.dat from this folder (take from EM installation)")
        parser.add_argument('--skip_archive_rules', type=str, default='False', 
                            choices=['True', 'False', 'true', 'false', 'yes','no','Yes','No'],
                            help="Skip archive rules export (default: False)")
        parser.add_argument('--aapi_url', default='https://localhost:8443/automation-api',
                            help="AAPI URL (default is https://localhost:8443/automation-api)")
        parser.add_argument('--aapi_credentials', default='',
                            help="AAPI Token Or AAPI User and Password ('/' between them). required in order to get access to archive rules")
        parser.add_argument('--net_history_days_4_export', default=3,
                            choices=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14'],
                            help="Count days for history viewpoint nets export (default is 3). use 0 to skip export for all history viewpoints")
        parser.add_argument('--validate_servers', default='all', choices=['all', 'none', 'ask'],
                            help="servers to validate (default: 'all' - for each server)")
        parser.add_argument('--skip_all_validation', type=str, default='True', 
                            choices=['True', 'False', 'true', 'false'],
                            help="Skip all qualification validations (default: True). Use --skip_all_validation False to run export-side validations.")

        # Parse the command-line arguments:
        args = parser.parse_args()

        help_requested = args.help
        # Check if help was requested
        if help_requested:
            parser.format_help()

        is_test_mode = args.test_mode.lower() == 'true'
        # Convert string arguments to booleans
        is_test_mode = args.test_mode.lower() == 'true'
        skip_verify_min_ctm_version = args.skip_verify_min_ctm_version.lower() == 'true'
        skip_verify_ctm_os = args.skip_verify_ctm_os.lower() == 'true'
        skip_verify_ctm_ha = args.skip_verify_ctm_ha.lower() == 'true'
        skip_cjk_check = args.skip_cjk_check.lower() == 'true'
        skip_timezone_check = args.skip_timezone_check.lower() == 'true'
        skip_archive_rules = args.skip_archive_rules.lower() in ['true', 'yes']
        skip_all_validation = args.skip_all_validation.lower() == 'true'


        saas_mig_export = SaasMigrationExport(logs_dir=args.logs_dir,
                                              log_level=args.log_level,
                                              skip_verify_ctm_os=skip_verify_ctm_os)

        export_file_location = args.export_file_location
        validate_servers = args.validate_servers
        net_history_days_4_export = int(args.net_history_days_4_export)
        if export_file_location == 'default':
            export_file_location = os.getcwd()
        alternative_tz_files_location = args.alternative_tz_files_location
        saas_mig_export.initialize_env_for_running_export_script(export_file_location=export_file_location,
                                                                 alternative_tz_files_location=alternative_tz_files_location)
        
        # Qualifications validation before starting the export
        if not skip_all_validation:
            LoggerHandler.print_message(
                message="Verifying that all qualifications tests passed.",
                screen_print=True)
            if not saas_mig_export.run_qualifications_validation(skip_cjk_check, skip_verify_min_ctm_version,
                                                                 skip_verify_ctm_os, skip_verify_ctm_ha,
                                                                 skip_timezone_check, saas_mig_export.em_version, validate_servers):
                LoggerHandler.print_message(message=f"Some qualification tests have failed. \n"
                                                    f"Exiting utility ... ",
                                            screen_print=True,
                                            logger_level='error')
                exit(1)
            LoggerHandler.print_message(message="All qualification tests passed.",
                                        screen_print=True)
        else:
            # print("Skipping all qualification validations.")
            LoggerHandler.print_message(message="Skipping all qualification validations.",
                                        screen_print=True)

        # archive rules
        if not skip_archive_rules:
            saas_mig_export.handle_archive_rules(args.aapi_url, args.aapi_credentials)
        else:
            LoggerHandler.print_message(
                message=f"Skipping export of Workload Archiving rules.",
                screen_print=True,
                logger_level='info')

        LoggerHandler.print_message(message="Starting to export all Control-M/EM data.", screen_print=True)
        export_tar_path = saas_mig_export.run_all_export_steps(saas_mig_export.log_directory,
                                                               saas_mig_export.log_file_name, is_test_mode,
                                                               export_file_location,
                                                               (alternative_tz_files_location != 'default'),
                                                               net_history_days_4_export)
        LoggerHandler.print_message(message=f"Completed exporting Control-M/EM data."
                                            f"The exported data is available on: {export_tar_path} \n"
                                            f"For more information, see the following export log: \n {saas_mig_export.log_file_name} ",
                                    screen_print=True)
        exit(0)
    except Exception as e:
        err_msg = "Failed to export all Control-M/EM data.\n" \
                  f"The exception message is: {str(e)}\n"

        if LoggerHandler.is_logger_configured():
            LoggerHandler.log_script_result(err_msg, logger_level='error', screen_print=True)
            if saas_mig_export.log_file_name is not None and saas_mig_export.log_directory is not None:
                if os.path.exists(saas_mig_export.em_export_directory):
                    saas_mig_export.copy_log_file_to_export_dir(saas_mig_export.log_directory,
                                                                saas_mig_export.log_file_name)
                    saas_mig_export.call_remove_export_for_saas_dir(is_test_mode)
        else:
            print(err_msg)
        
        # Print error message at the end of the run for better visibility
        print("\n" + "="*80)
        print("EXPORT FAILED")
        print("="*80)
        print(f"Error: {str(e)}")
        if saas_mig_export.log_file_name is not None:
            print(f"For more information, see the following export log: {saas_mig_export.log_file_name}")
        print("="*80)
        exit(1)
