from collections import defaultdict

from parallels.core.converter.business_objects.common import EntityConverter
from parallels.core.logging import log_context
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common import format_list
from parallels.plesk.source.expand import messages


class ExpandToPleskSubscriptionConverter(object):
    def convert(self, sources_info, subscription_mapping, model, plain_report):
        """
        :type subscription_mapping: dict[str | unicode, parallels.core.migration_list.entities.subscription_info.SubscriptionMappingInfo]
        :type model: parallels.core.target_data_model.Model
        :type plain_report: parallels.core.reports.subscription_report_provider.SubscriptionReportProvider
        """
        converted_subscriptions, failed_subscriptions = self._convert_subscriptions_plain(sources_info, plain_report)

        clients_by_login = {}

        for client in model.clients.values():
            clients_by_login[client.login] = client
        for reseller in model.resellers.values():
            for client in reseller.clients:
                clients_by_login[client.login] = client

        for subscription_name, mapping in subscription_mapping.items():
            if subscription_name in failed_subscriptions:
                continue

            converted_subscription = converted_subscriptions.get(subscription_name)

            if converted_subscription is None:
                plain_report.get_root_report().add_issue(
                    "subscription_does_not_exist", Issue.SEVERITY_ERROR,
                    messages.SUBSCRIPTION_DOES_NOT_EXIST_ON_SOURCE % subscription_name,
                    messages.SOLUTION_CORRECT_SUBSCRIPTION_NAME
                )
                continue

            subscription_report = plain_report.get_subscription_report(subscription_name)

            if mapping.owner not in clients_by_login:
                subscription_report.add_issue(
                    "owner_does_not_exist", Issue.SEVERITY_ERROR,
                    messages.CUSTOMER_OR_RESELLER_DOES_NOT_EXIST % mapping.owner,
                    messages.CREATE_CUSTOMER_OR_RESELLER_MANUALLY)
                continue

            client = clients_by_login[mapping.owner]
            client.subscriptions.append(converted_subscription)

    @staticmethod
    def _convert_subscriptions_plain(sources_info, plain_report):
        """Perform direct conversion: backup subscription to target model subscription
        :type sources_info: list[parallels.core.global_context.SourceInfo]
        :type plain_report: parallels.core.reports.subscription_report_provider.SubscriptionReportProvider
        """
        converted_subscriptions = defaultdict(list)
        failed_subscriptions = set()

        for source_info in sources_info:
            with log_context(source_info.id):
                backup = source_info.load_raw_dump()
                for subscription in backup.iter_all_subscriptions():
                    model_subscription = EntityConverter(None).create_subscription_from_plesk_backup_subscription(
                        subscription, None, source_info.id, source_info.is_windows
                    )

                    converted_subscriptions[subscription.name].append(model_subscription)

        merged_subscriptions = {}

        for name, subscriptions in converted_subscriptions.items():
            if len(subscriptions) == 1:
                subscription = subscriptions[0]
                merged_subscriptions[subscription.name] = subscription
            else:
                plain_report.get_root_report().add_issue(
                    "subscription_on_multiple_sources", Issue.SEVERITY_ERROR,
                    messages.SUBSCRIPTION_EXISTS_ON_MULTIPLE_SOURCE_PANELS % (
                        name,
                        format_list([s.source for s in subscriptions]),
                    ),
                    messages.SOLUTION_SPECIFY_THE_ONLY_SERVER_TO_MIGRATE_SUBSCRIPTION)
                failed_subscriptions.add(name)

        return merged_subscriptions, failed_subscriptions
