from parallels.core.logging import get_logger
from parallels.core.utils.common import is_run_on_windows, mkdir_p, safe_format
from parallels.core.registry import Registry
from parallels.core import messages
from parallels.core.utils.common.ip import is_ip
from parallels.core import MigrationError
from datetime import datetime
try:
    import ConfigParser
except ImportError:
    from configparser import ConfigParser
import os
import sys
import getpass


logger = get_logger(__name__)

# Constants
ALLOWED_SOURCES = ['plesk', 'cpanel', 'confixx', 'ppcpl', 'helm-3']
PATH_TO_DEFAULT_RSA_SSH_KEY = '/root/.ssh/id_rsa'


class ConfigCreator(object):
    # Main
    def create(self, global_context):
        session_folder_name = self._select_session_folder_name(global_context.options.session_name)
        operation_system = self._get_type_os()
        logger.fdebug(messages.CONFIG_CREATOR_DETECTED_OS, system=operation_system)
        if self._check_existing_config_file(session_folder_name):
            avail_samples = self._get_avail_samples(operation_system)
            logger.fdebug(messages.CONFIG_CREATOR_SAMPLES_EXIST, samples=', '.join(avail_samples))
            avail_sources = self._get_avail_sources(avail_samples)
            logger.fdebug(messages.CONFIG_CREATOR_POSSIBLE_SOURCES, sources=', '.join(avail_sources))
            source_panel = self._select_source(avail_sources)
            if source_panel:
                logger.fdebug(messages.CONFIG_CREATOR_SELECTED_SOURCE_PANEL + ' ' + source_panel)
                print(messages.CONFIG_CREATOR_SELECTED_SOURCE_PANEL + ' ' + source_panel)
                config_sample = self._select_sample(source_panel, avail_samples)
                config = self._generate_config(config_sample, session_folder_name)
                self._write_config(config, session_folder_name)
                logger.finfo(messages.CONFIG_CREATOR_CONFIG_DONE,
                             session_folder=self._path_to_session_folder(session_folder_name),
                             exec_path=self._exec_path(),
                             path_to_config=os.path.join(
                                 self._path_to_session_folder(session_folder_name), 'config.ini')
                             )
            else:
                logger.finfo(messages.CONFIG_CREATOR_NO_CONFIG_EXAMPLES_FOR_SELECTED_PANEL)
        else:
            logger.finfo(messages.CONFIG_CREATOR_CONFIG_NOT_CHANGED)

    # Detect and classify OS
    @staticmethod
    def _get_type_os():
        if is_run_on_windows():
            return 'windows'
        else:
            return 'unix'

    # Detect path to samples
    @staticmethod
    def _path_to_samples():
        registry = Registry.get_instance()
        return os.path.join(registry.get_base_dir(), 'conf', 'samples')

    # Generate name of session folder
    @staticmethod
    def _select_session_folder_name(session_name):
        if session_name is None:
            return datetime.now().strftime('%Y%m%d%H%M%S')
        else:
            return session_name

    # Compose path to result config
    @staticmethod
    def _path_to_session_folder(session_folder):
        registry = Registry.get_instance()
        return os.path.join(registry.get_var_dir(), 'sessions', session_folder)

    @staticmethod
    def _exec_path():
        if not is_run_on_windows():
            return str(sys.argv[0])
        else:
            return r'"%PLESK_DIR%\admin\plib\modules\panel-migrator\backend\plesk-migrator.bat"'

    # Detecting available sources for current OS according to existing samples
    def _get_avail_samples(self, operation_system):
        selected_samples = []
        try:
            all_samples = os.listdir(self._path_to_samples())
        except Exception as e:
            logger.ferror(messages.CONFIG_CREATOR_UNABLE_TO_LIST_EXAMPLES_DIRECTORY, reason=e)
            raise
        for sample in all_samples:
            if operation_system in sample:
                selected_samples.append(sample)
            # Add OS specified panel type
            elif operation_system == "windows" and "helm-3" in sample:
                selected_samples.append(sample)
            elif operation_system == "unix":
                if ("confixx" in sample) or ("cpanel" in sample) or ("ppcpl" in sample):
                    selected_samples.append(sample)
        if selected_samples:
            return selected_samples
        else:
            raise Exception(
                messages.CONFIG_CREATOR_NO_CONFIG_EXAMPLES
            )

    # Detecting avail sources according to existing samples
    @staticmethod
    def _get_avail_sources(samples):
        avail_sources = []
        for sample in samples:
            for source in ALLOWED_SOURCES:
                if source in sample and source not in avail_sources:
                    avail_sources.append(source)
        if avail_sources != 'None':
            return avail_sources
        else:
            raise Exception(
                messages.CONFIG_CREATOR_NO_AVAILABLE_SOURCES
            )

    # Select source type
    @staticmethod
    def _select_source(avail_sources):
        print(messages.CONFIG_CREATOR_ALLOWED_SOURCE_TYPES, ', '.join(avail_sources))
        print(messages.CONFIG_CREATOR_ASK_SOURCE_TYPE,)
        user_input_source = raw_input()
        if user_input_source in avail_sources:
            selected_source = user_input_source
        elif user_input_source == '':
            selected_source = 'plesk'
        else:
            selected_source = ''
        return selected_source

    # Selecting sample to use
    @staticmethod
    def _select_sample(source_panel, avail_samples):
        # Select samples for selected panel
        sample_selected = None
        samples = [sample for sample in avail_samples if source_panel in sample]
        # Check simple config
        for sample in samples:
            if "simple" in sample:
                sample_selected = sample
        if sample_selected is not None:
            return sample_selected
        else:
            raise Exception(
                messages.CONFIG_CREATOR_NO_AVAILABLE_SAMPLES
            )

    # Ask IP addresses
    @staticmethod
    def _ask_ip(text):
        input_ip = raw_input(text)
        while not is_ip(input_ip):
            logger.fwarn(messages.CONFIG_CREATOR_IS_NOT_IP)
            input_ip = raw_input(text)
        return input_ip

    @staticmethod
    def _ask_key_auth():
        user_input = raw_input(messages.CONFIG_CREATOR_ASK_TO_USE_SSH_KEYS)
        return user_input.lower() == 'y'

    @staticmethod
    def _ask_path_to_key():
        if os.path.exists(PATH_TO_DEFAULT_RSA_SSH_KEY):
            question = raw_input(safe_format(messages.CONFIG_CREATOR_ASK_TO_USE_DEFAULT_RSA_SSH_KEY,
                                             file=PATH_TO_DEFAULT_RSA_SSH_KEY))
            if question.lower() in ('y', ''):
                return PATH_TO_DEFAULT_RSA_SSH_KEY
        user_input = raw_input(messages.CONFIG_CREATOR_ASK_PATH_TO_SSH_KEY)
        while not os.path.exists(user_input):
            user_input = raw_input(safe_format(messages.CONFIG_CREATOR_ASK_REPEAT_PATH_TO_SSH_KEY, file=user_input))
        return user_input

    def _get_source_ip(self):
        return self._ask_ip(messages.CONFIG_CREATOR_ASK_SOURCE_IP)

    def _get_target_ip(self):
        return self._ask_ip(messages.CONFIG_CREATOR_ASK_TARGET_IP)

    @staticmethod
    def _select_password_message(option, source_type):
        if source_type == 'helm3':
            return messages.CONFIG_CREATOR_ASK_SOURCE_PASSWORD_HELM3
        else:
            if option == 'windows-password':
                return messages.CONFIG_CREATOR_ASK_SOURCE_PASSWORD_WIN
            else:
                return messages.CONFIG_CREATOR_ASK_SOURCE_PASSWORD_UNIX

    def _get_source_password(self, option, source_type):
        password = ''
        password_not_verified = True
        while password_not_verified:
            password = getpass.getpass(str(self._select_password_message(option, source_type)))
            password_check = getpass.getpass(str(messages.CONFIG_CREATOR_ASK_SOURCE_PASSWORD_VERIFY))
            if password == password_check:
                password_not_verified = False
            else:
                logger.fwarn(messages.CONFIG_CREATOR_ASK_SOURCE_PASSWORD_REPEAT)
        return password

    @staticmethod
    def _print_result_config(config):
        print(messages.CONFIG_CREATOR_PRINT_RESULT_CONFIG)
        for section in config.sections():
            print("[" + section + "]")
            for option in config.options(section):
                if 'password' in option:
                    print(option + ' : ***hidden***')
                else:
                    print(option + ' : ' + config.get(section, option))

    # Open sample file to read parameters, ask required values and create
    def _generate_config(self, config_sample, session_folder_name):
        full_path_to_sample = os.path.join(self._path_to_samples(), config_sample)
        logger.fdebug(messages.CONFIG_CREATOR_USING_SAMPLE, sample=full_path_to_sample)
        # Open sample
        sample = ConfigParser.ConfigParser()
        try:
            with open(full_path_to_sample, "r") as sample_file:
                sample.readfp(sample_file)
        except Exception as e:
            logger.exception()
            raise MigrationError(safe_format(messages.CONFIG_CREATOR_UNABLE_TO_READ_EXAMPLE_FILE, reason=e))
        result_config = ConfigParser.ConfigParser()
        for section in sample.sections():
            result_config.add_section(str(section))
            for option in sample.options(section):
                if sample.get(section, option) == "" and option == "ip" and section == "plesk":
                    result_config.set(section, option, self._get_target_ip())
                elif (
                    sample.get(section, option) == "" and option == "ip"
                    and (
                        (sample.get("GLOBAL", "source-type") == section and section != "plesk")
                        or
                        (sample.get("GLOBAL", "source-type") == "plesk" and sample.get("GLOBAL", "sources") == section)
                    )
                ):
                    result_config.set(section, option, self._get_source_ip())
                elif sample.get(section, option) == "" and "password" in option:
                    if self._get_type_os() == 'unix' and self._ask_key_auth():
                        result_config.remove_option(section, option)
                        result_config.set(section, 'ssh-auth-type', 'key')
                        result_config.set(section, 'ssh-key', self._ask_path_to_key())
                    else:
                        result_config.set(section, option,
                                          self._get_source_password(option, sample.get("GLOBAL", "source-type")))
                else:
                    result_config.set(section, option, sample.get(section, option))
        # Enable separate log
        result_config.set('GLOBAL', 'use-separate-log', 'true')
        logger.fdebug(messages.CONFIG_CREATOR_SEPARATE_LOG_ENABLED)
        result_config.set('GLOBAL', 'session-dir', session_folder_name)
        self._print_result_config(result_config)
        return result_config

    # write generated config.ini
    def _write_config(self, generated_config, session_folder_name):
        path_to_config = os.path.join(self._path_to_session_folder(session_folder_name), 'config.ini')
        with open(path_to_config, "wb") as config:
            generated_config.write(config)
        logger.finfo(messages.CONFIG_CREATOR_CONFIG_FILE_SAVED, file=path_to_config)

    def _check_existing_config_file(self, session_folder_name):
        if os.path.exists(os.path.join(self._path_to_session_folder(session_folder_name), 'config.ini')):
            user_input = raw_input(messages.CONFIG_CREATOR_ASK_TO_REWRITE_CONFIG)
            if user_input.lower() == "y":
                logger.fdebug(messages.CONFIG_CREATOR_ALLOWED_OVERWRITE_CONFIG_FILE)
                return True
            else:
                return False
        else:
            self._create_session_folder(self._path_to_session_folder(session_folder_name))
            return True

    @staticmethod
    def _create_session_folder(session_folder_path):
        if os.path.isfile(session_folder_path):
            raise Exception(messages.CONFIG_CREATOR_CONFIG_FOLDER_IS_FILE)
        elif not os.path.exists(session_folder_path):
            mkdir_p(session_folder_path)
            logger.fdebug(messages.CONFIG_CREATOR_CREATE_CONF_FOLDER, folder=session_folder_path)
