import datetime
import sys

from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.registry import Registry
from parallels.core.thirdparties.colorama import Style
from parallels.core.utils.colors.report_colors import FORE_COLORS, BACK_COLORS, STYLES
from parallels.core.utils.common import utf8_tree_symbols, ascii_tree_symbols, draw_tree, open_no_inherit
from parallels.core.utils.json_utils import write_json
from parallels.core.utils.migrator_utils import get_optional_option_value

logger = get_logger(__name__)


def print_report(
    report, report_filename=None, log_message=u'',
    show_no_issue_branches=True, no_issues_branches_to_skip=None
):
    """Print report to console and log file, optionally write to a separate file

    :type report: parallels.core.reports.model.report.Report
    :type report_filename: str | unicode | None
    :type log_message: str | unicode
    :type show_no_issue_branches: bool
    :type no_issues_branches_to_skip: list[str | unicode] | None
    """
    report_str = _format_report(
        report, no_issues_branches_to_skip, None, show_no_issue_branches
    )
    color_report_str = _format_report(
        report, no_issues_branches_to_skip, None, show_no_issue_branches, colors=True
    )

    # We use special extra variable "colored", that is handled by console handler
    logger.info(
        log_message + "\n" + report_str,
        extra={'colored': log_message + "\n" + color_report_str}
    )

    if report_filename is not None:
        migrator_server = Registry().get_instance().get_context().migrator_server
        full_json_path = migrator_server.get_session_file_path('%s.json' % report_filename)
        write_json(full_json_path, report.as_dictionary(recursive=True))
        task_id = get_optional_option_value(Registry().get_instance().get_context().options, 'progress_task_id', None)
        if task_id is not None:
            task_json_path = migrator_server.get_session_file_path('%s.%s.json' % (report_filename, task_id))
            write_json(task_json_path, report.as_dictionary(recursive=True))
        full_plain_path = migrator_server.get_session_file_path(report_filename)
        path = _save_report(report_str, full_plain_path)
        logger.info(messages.REPORT_WAS_SAVED_INTO_FILE % path)


def _save_report(tree_report, report_file_path):
    task_id = get_optional_option_value(Registry().get_instance().get_context().options, 'progress_task_id', None)
    if task_id is not None:
        suffix = '.%s' % task_id
    else:
        suffix = datetime.datetime.now().strftime(".%Y.%m.%d.%H.%M.%S")

    report_file_path = u'%s%s' % (report_file_path, suffix)
    with open_no_inherit(report_file_path, 'w') as f:
        f.write(tree_report.encode('utf-8'))
    return report_file_path


def _format_report(report, minor_issues=None, report_file_path=None, show_no_issue_branches=True, colors=False):
    """Print issues report to stdout as tree with the use of pseudographics.
    show_no_issue_branches - whether to show the branches without issues
    minor_issues - do not show the no-issue nodes of the specified types, even if show_no_issue_branches is True
    """
    if minor_issues is None:
        minor_issues = set([])

    def format_issue(issue):
        """
        :type issue: parallels.core.reports.model.issue.Issue
        :rtype: str | unicode
        """
        severity = issue.severity
        if colors:
            severity = _colorize(severity, 'severity', severity)

        description = issue.get_issue_text()
        if colors:
            description = _colorize(description, 'message', issue.severity)

        formatted_issue = u"%s: %s" % (severity, description)

        return formatted_issue

    def format_report_title(report):
        if colors:
            if report.name is not None:
                return (
                    _colorize("%s '" % report.type, 'report-type') +
                    _colorize(report.name, 'report-name') +
                    _colorize("'", 'report-type')
                )
            else:
                return _colorize(report.type, 'report-type')
        else:
            return u"%s '%s'" % (report.type, report.name) if report.name is not None else report.type

    def report_to_tree(report):
        def keep_report(report):
            if not show_no_issue_branches and not report.has_issues():
                return False
            if report.type in minor_issues and len(report.children) == 0 and len(report.issues) == 0:
                return False
            return True

        return (
            format_report_title(report),
            [
                item for item in
                [(format_issue(i), []) for i in report.issues] +
                [report_to_tree(c) for c in report.children if keep_report(c)]
            ]
        )
    tree = report_to_tree(report)

    if sys.stdout.encoding == 'UTF-8':
        tree_symbols = utf8_tree_symbols
    else:
        tree_symbols = ascii_tree_symbols

    tree_report = draw_tree(tree, spacing=1, symbols=tree_symbols)
    return tree_report


def _colorize(text, *keys):
    fore_color = _get_key(FORE_COLORS, keys)
    back_color = _get_key(BACK_COLORS, keys)
    style = _get_key(STYLES, keys)
    result = ''
    if style is not None:
        result += style
    if fore_color is not None:
        result += fore_color
    if back_color is not None:
        result += back_color
    result += text
    if fore_color is not None or back_color is not None or style is not None:
        result += Style.RESET_ALL
    return result


def _get_key(d, keys):
    for key in keys:
        if key not in d:
            return None
        d = d[key]

    return d
