import os
import sys
import yaml
import logging
import logging.config

from parallels.core.cli.command import CommandOperation
from parallels.core.logging.binary import BinaryLogger, BinaryLogIndexFile, BinaryLogMessagesFile
from parallels.core.registry import Registry
from parallels.core.utils.common import mkdir_p, open_no_inherit
from parallels.core.utils.config_utils import get_option, get_boolean_option
from parallels.core.logging.context import InfoLogFormatter, ConsoleLogFormatter
from parallels.core.logging.context import DebugLogFormatter
from parallels.core.logging.context import IndentFilter


class LoggingConfiguration(object):
    @staticmethod
    def configure(options, use_separate_log_by_default=False):
        logging.captureWarnings(True)

        parallels_logger = logging.getLogger('parallels')
        paramiko_logger = logging.getLogger('paramiko')

        # clean up handlers list to prevent log entries duplicating
        parallels_logger.handlers = []
        paramiko_logger.handlers = []

        # load logging configuration from config, if present
        if options.logging_config is not None and os.path.exists(options.logging_config):
            with open_no_inherit(options.logging_config) as f:
                config = yaml.load(f)
                logging.config.dictConfig(config)

        registry = Registry.get_instance()

        if get_option('use-separate-log', use_separate_log_by_default):
            # write log into current session directory
            context = registry.get_context()

            session_files = context.session_files
            if context.options.command_operation == CommandOperation.TASK_RUNNER:
                mkdir_p(session_files.get_path_to_task_manager_logs_dir())
                info_log_path = session_files.get_path_to_task_manager_runner_info_log()
                debug_log_path = session_files.get_path_to_task_manager_runner_debug_log()
            elif context.options.command_operation == CommandOperation.TASK_MANAGER:
                mkdir_p(session_files.get_path_to_task_manager_logs_dir())
                info_log_path = session_files.get_path_to_task_manager_info_log()
                debug_log_path = session_files.get_path_to_task_manager_debug_log()
            else:
                info_log_path = session_files.get_path_to_info_log()
                debug_log_path = session_files.get_path_to_debug_log()

                if get_boolean_option('use-binary-log', True):
                    try:
                        if not os.path.exists(session_files.get_path_to_binary_log_index()):
                            with open(session_files.get_path_to_binary_log_index(), 'w'):
                                pass
                        BinaryLogger.get_instance().initialize(
                            BinaryLogIndexFile(open(session_files.get_path_to_binary_log_index(), 'r+b')),
                            BinaryLogMessagesFile(open(session_files.get_path_to_binary_log_messages(), 'ab'))
                        )
                    except Exception:
                        # Ignore all errors related to binary log - as binary log is not essential for migration.
                        # Here we don't have any logging configured to put stack trace to it.
                        # We can't put it to stderr/stdout directly because migrator could get broken on Windows.
                        # So the only option is to ignore the error, which makes debugging of binary log
                        # problems a bit more difficult.
                        pass
        else:
            # write log into common place
            var_dir = registry.get_var_dir()
            logs_dir = os.path.join(var_dir, 'logs')
            if not os.path.exists(os.path.join(logs_dir)):
                os.mkdir(logs_dir)
            info_log_path = os.path.join(logs_dir, 'info.log')
            debug_log_path = os.path.join(logs_dir, 'debug.log')

        parallels_logger.setLevel(logging.DEBUG)
        paramiko_logger.setLevel(logging.WARNING)

        info_file_handler = logging.FileHandler(info_log_path)
        info_file_handler.setLevel(logging.INFO)
        info_file_handler.setFormatter(InfoLogFormatter())
        info_file_handler.addFilter(IndentFilter())
        parallels_logger.addHandler(info_file_handler)

        debug_file_handler = logging.FileHandler(debug_log_path)
        debug_file_handler.setLevel(logging.NOTSET)
        debug_file_handler.setFormatter(DebugLogFormatter())
        debug_file_handler.addFilter(IndentFilter())
        parallels_logger.addHandler(debug_file_handler)
        paramiko_logger.addHandler(debug_file_handler)

        if not options.quiet:
            # display log entries in console if is not quiet mode
            console_handler = logging.StreamHandler(stream=sys.stdout)
            console_handler.setLevel(logging.INFO)
            console_handler.setFormatter(ConsoleLogFormatter())
            console_handler.addFilter(IndentFilter())
            parallels_logger.addHandler(console_handler)