from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.migration_list.writer.plain_writer import MigrationListWriterPlain
from parallels.core.reports.model.issue import Issue
from parallels.core.reports.plain_report import PlainReport
from parallels.core.reports.printer import print_report
from parallels.core.utils import steps_profiler
from parallels.core.utils.common import get_executable_file_name, ilen
from parallels.core.utils.yaml_utils import read_yaml

logger = get_logger(__name__)


class PrintFinalMigrationReport(CommonAction):
    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_PRINT_FINAL_REPORT_DESCRIPTION

    def get_failure_message(self, global_context):
        """Get message for situation when action failed

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return messages.ACTION_PRINT_FINAL_REPORT_FAILURE

    def get_logging_properties(self):
        """Get how action should be logged to migration tools end-user

        :rtype: parallels.core.actions.utils.logging_properties.LoggingProperties
        """
        return LoggingProperties(info_log=False, top_level=True)

    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        unfiltered_model = global_context.migrator.get_target_model(False)

        steps_profiler.get_default_steps_report().set_migrated_subscriptions_count(
            ilen(unfiltered_model.iter_all_subscriptions())
        )

        print_report(
            global_context.execution_migration_report, 'accounts_report_tree',
            no_issues_branches_to_skip=self._get_minor_issue_targets()
        )

        final_plain_report = PlainReport(
            global_context.execution_migration_report, global_context.migration_list_data
        )
        failed_subscriptions = {}
        for subscription in unfiltered_model.iter_all_subscriptions():
            report = final_plain_report.get_subscription_report(subscription.name)
            if report.has_errors():
                failed_subscriptions[subscription.name] = [
                    issue.get_issue_text()
                    for issue in report.issues if issue.severity == Issue.SEVERITY_ERROR
                ]

        logger.info(messages.SUMMARY)

        if global_context.post_migration_checks_enabled:
            run_post_migrations_msg = "\n" + messages.RUN_TEST_ALL_AFTER_MIGRATION
        else:
            run_post_migrations_msg = ""

        logger.info(
            messages.SUBSCRIPTIONS_MIGRATION_STATUS + run_post_migrations_msg,
            ilen(unfiltered_model.iter_all_subscriptions()) - len(failed_subscriptions),
            ilen(unfiltered_model.iter_all_subscriptions())
        )

        def quote(x):
            return u"'%s'" % (x,)

        failed_objects_types = [
            (messages.FAILED_OBJECTS_SUBSCRIPTIONS, global_context.safe.failed_objects.subscriptions, quote),
            (messages.FAILED_OBJECTS_RESELLERS, global_context.safe.failed_objects.resellers, quote),
            (messages.FAILED_OBJECTS_CLIENTS, global_context.safe.failed_objects.clients, quote),
            (messages.FAILED_OBJECTS_PLANS, global_context.safe.failed_objects.plans, lambda p: (
                messages.OWNED_BY_RESELLER % (p[1], p[0]) if p[0] is not None else messages.OWNED_BY_ADMIN % p[1]
            )),
            (messages.FAILED_OBJECTS_AUX_USERS, global_context.safe.failed_objects.auxiliary_users, lambda a: (
                messages.OWNED_BY % (a[1], a[0],)
            )),
            (
                messages.FAILED_OBJECTS_AUX_USER_ROLES, global_context.safe.failed_objects.auxiliary_user_roles,
                lambda a: messages.OWNED_BY % (a[1], a[0],)
            )
        ]

        for name, objects, format_func in failed_objects_types:
            error_description = []
            for obj_type, obj_info_list in objects.items():
                errors = self._get_report_obj_by_severity(
                    obj_info_list, Issue.SEVERITY_ERROR
                )
                if len(errors) > 0:
                    error_description.append(format_func(obj_type))
            if len(error_description) > 0:
                logger.error(
                    messages.FAILED_TO_PERFORM_OPERATION,
                    len(error_description), name, u', '.join(error_description))

        if len(failed_subscriptions) > 0:
            target_existing_objects = read_yaml(global_context.session_files.get_path_to_existing_objects_model())

            admin_id = global_context.hosting_repository.panel.get_administrator_id(None)

            target_service_templates = {
                None: [
                    st.name for st in target_existing_objects.plans
                    if st.owner_id == admin_id
                ]
            }
            for r in target_existing_objects.resellers.values():
                target_service_templates[r.username] = [
                    st.name for st in target_existing_objects.plans if st.owner_id == r.reseller_id
                    ]

            writer = MigrationListWriterPlain(global_context.migrator.get_migration_list_source_data())

            writer.write_selected_subscriptions(
                global_context.session_files.get_path_to_failed_subscriptions_list(),
                global_context.migrator.get_migration_list_source_data(),
                [
                    existing_subscription.name_canonical
                    for existing_subscription in target_existing_objects.subscriptions
                ],
                target_service_templates,
                global_context.migration_list_data.subscriptions_mapping,
                global_context.safe.failed_objects.subscriptions,
                failed_subscriptions
            )
            writer.write_selected_subscriptions(
                global_context.session_files.get_path_to_successful_subscriptions_list(),
                global_context.migrator.get_migration_list_source_data(),
                [
                    existing_subscription.name_canonical
                    for existing_subscription in target_existing_objects.subscriptions
                ],
                target_service_templates,
                global_context.migration_list_data.subscriptions_mapping,
                global_context.safe.failed_objects.subscriptions,
                {}  # no error messages for successful subscriptions
            )

            logger.error(
                messages.SUBSCRIPTIONS_FAILED_TO_MIGRATE.format(
                    failed_subscriptions=len(failed_subscriptions),
                    total_subscriptions=ilen(unfiltered_model.iter_all_subscriptions()),
                    failed_subscriptions_file=global_context.session_files.get_path_to_failed_subscriptions_list(),
                    executable_name=get_executable_file_name()
                )
            )

    @staticmethod
    def _get_report_obj_by_severity(obj_list, severity):
        return [info for info in obj_list if info.severity == severity]

    @classmethod
    def _get_minor_issue_targets(cls):
        """Get names of issue target which must be hidden if empty.
        """
        return [u'Auxiliary user', u'DNS zone']
