import os

from parallels.core import messages
from parallels.core.migration_list.writer.base_writer import MigrationListWriterBase
from parallels.core.registry import Registry
from parallels.core.utils.common import default, group_by, sorted_dict, write_unicode_file


class MigrationListWriterPlain(MigrationListWriterBase):
    def write_initial(
        self, filename,
        target_subscriptions, target_service_plans, target_addon_service_plans,
        comment_existing_subscriptions=True
    ):
        initial_mapping_text = self.generate_initial_content(
            target_subscriptions, target_service_plans,
            target_addon_service_plans=target_addon_service_plans,
            comment_existing_subscriptions=comment_existing_subscriptions
        )
        write_unicode_file(filename, initial_mapping_text)

    def write_selected_subscriptions(
        self, filename, dump_iterator,
        target_subscriptions, target_service_plans, subscriptions_mapping,
        subscription_filter, pre_subscription_comments
    ):
        selected_mapping_text = self.generate_initial_content(
            target_subscriptions, target_service_plans,
            subscriptions_mapping, subscription_filter,
            pre_subscription_comments, comment_existing_subscriptions=False
        )
        write_unicode_file(filename, selected_mapping_text)

    def generate_initial_content(
        self, existing_subscription_canonical_names, target_service_plans,
        subscriptions_mapping=None, subscription_filter=None,
        pre_subscription_comments=None,
        target_addon_service_plans=None,
        comment_existing_subscriptions=True,
    ):
        if target_addon_service_plans is None:
            target_addon_service_plans = {}

        # list of SubscriptionSourceInfo
        subscriptions = self._source_data.extract_subscriptions(
            subscriptions_mapping, subscription_filter
        )
        # all plans, including ones without subscriptions on them, dictionary {reseller: [plan]}
        plans = self._source_data.get_plans(
            target_service_plans, addon_plans=False
        )
        # all addon plans, including ones without subscriptions on them, dictionary {reseller: [plan]}
        addon_plans = self._source_data.get_plans(
            target_addon_service_plans, addon_plans=True
        )
        # dictionary {login: contact}
        reseller_contacts = self._source_data.get_reseller_contacts()
        # dictionary {login: contact}
        customer_contacts = self._source_data.get_customer_contacts()
        reseller_plans = self._source_data.get_reseller_plans()

        lines = []

        def get_subscriptions_lines(login, subscriptions):
            subscription_lines = []
            for subscription in subscriptions:
                if subscription.customer != login:
                    continue
                if pre_subscription_comments is not None and subscription.name in pre_subscription_comments:
                    for comment in pre_subscription_comments[subscription.name]:
                        for line in comment.split("\n"):
                            subscription_lines.append(u'# %s' % line)
                subscription_comments = []
                if comment_existing_subscriptions:
                    if subscription.name in existing_subscription_canonical_names:
                        subscription_comments.append(messages.ALREADY_EXISTS_IN_TARGET_PANEL)
                comment = u"# " if subscription.name in existing_subscription_canonical_names else u""
                if len(subscription_comments) > 0:
                    subscription_lines.append(u"            %s%s # %s" % (
                        comment, subscription.name, u", ".join(subscription_comments))
                                              )
                else:
                    subscription_lines.append(u"            %s" % subscription.name)
            return subscription_lines

        def format_customer_section(login, reseller_login=None):
            contact = customer_contacts.get(login)
            if contact is not None and contact != '':
                return u"    Customer: %s # %s" % (login, contact)
            elif login not in customer_contacts:
                return u"    Customer: %s # %s" % (
                    login, messages.AUTO_GENERATED_CUSTOMER_COMMENT % default(reseller_login, 'admin')
                )
            else:
                return u"    Customer: %s" % login

        def get_client_block(client_name, reseller, subscriptions):
            if client_name is not None:
                client_block_lines = [format_customer_section(client_name, reseller)]
            else:
                client_block_lines = []

            subscriptions_by_plan = group_by(subscriptions, lambda s: (s.plan, tuple(s.addon_plans)))
            client_block_lines += get_plan_block(client_name, None, (), subscriptions_by_plan.get((None, ()), []))
            for (plan, addons), subscriptions in subscriptions_by_plan.items():
                if plan is not None:
                    client_block_lines += get_plan_block(client_name, plan, addons, subscriptions)
            return client_block_lines

        def get_plan_block(customer, plan_name, addon_plan_names, subscriptions):
            if plan_name is not None:
                plan_block_lines = [self._format_plan_section(plan_name)]
                for addon_plan_name in addon_plan_names:
                    plan_block_lines.append(self._format_addon_plan_section(addon_plan_name))
            elif plan_name is None and len(subscriptions) > 0:
                if not Registry.get_instance().get_context().target_panel_obj.has_custom_subscriptions_feature():
                    plan_block_lines = [u"        %s" % messages.CUSTOM_SUBSCRIPTIONS_MUST_ASSOCIATED_SERVICE_TEMPLATE]
                else:
                    # Target Plesk supports custom subscriptions -
                    # subscriptions not associated with any service plan
                    plan_block_lines = []
            else:
                plan_block_lines = []

            for s in subscriptions:
                if s.plan == plan_name and tuple(s.addon_plans) == addon_plan_names:
                    plan_block_lines += get_subscriptions_lines(customer, (s,))
            return plan_block_lines

        def get_reseller_block(login, reseller_subscriptions):
            reseller_block_lines = []

            if Registry.get_instance().get_context().target_panel_obj.has_admin_reseller_plan_feature():
                plan = reseller_plans.get(login)
                if plan is not None:
                    reseller_block_lines.append(self._format_reseller_plan_section(plan))

            if login is not None:
                reseller_block_lines.append(self._format_reseller_section(login, reseller_contacts))
            elif len(reseller_subscriptions) > 0:
                reseller_block_lines.append(messages.ADMIN_SUBSCRIPTIONS_AND_CUSTOMERS)

            subscriptions_by_client = sorted_dict(group_by(reseller_subscriptions, lambda s: s.customer))
            reseller_block_lines += get_client_block(None, login, subscriptions_by_client.get(None, []))
            used_plans = []
            for client, client_subscriptions in subscriptions_by_client.items():
                used_plans.extend([sub.plan for sub in client_subscriptions])
                if client is not None:
                    reseller_block_lines += get_client_block(client, login, client_subscriptions)

            subscriptions_by_plans = sorted_dict(
                group_by(reseller_subscriptions, lambda s: (s.plan, tuple(s.addon_plans)))
            )
            used_addon_plans = {
                addon_plan_name
                for (plan_name, addon_plan_names) in subscriptions_by_plans.keys()
                for addon_plan_name in addon_plan_names
            }
            if subscription_filter is None:
                unused_plans = []
                # iterate over plans with no subscriptions assigned, but still exist on target server
                for plan_name in sorted(set(target_service_plans.get(login, [])) - set(used_plans)):
                    unused_plans.append(self._format_plan_section(plan_name))
                # iterate over addon plans with no subscriptions assigned, but still exist on target server
                for plan_name in sorted(set(target_addon_service_plans.get(login, [])) - used_addon_plans):
                    unused_plans.append(self._format_addon_plan_section(plan_name))
                if unused_plans:
                    reseller_block_lines.append(u"    # %s" % messages.EXISTING_HOSTING_PLANS_NOT_USED_BY)
                    reseller_block_lines += unused_plans

            return reseller_block_lines

        lines.append(messages.MIGRATION_LIST_FILE_HEADER)
        lines.append(u"")

        subscriptions_by_reseller = group_by(subscriptions, lambda s: s.reseller)
        # Keep order of reseller blocks
        lines += get_reseller_block(None, subscriptions_by_reseller.get(None, []))
        for reseller, subscriptions in subscriptions_by_reseller.items():
            if reseller is not None and reseller_plans.get(reseller) is None:
                lines += get_reseller_block(reseller, subscriptions)
        for reseller, subscriptions in subscriptions_by_reseller.items():
            if reseller is not None and reseller_plans.get(reseller) is not None:
                lines += get_reseller_block(reseller, subscriptions)

        return os.linesep.join(lines) + os.linesep

    @staticmethod
    def _format_reseller_section(login, reseller_contacts):
        contact = reseller_contacts.get(login) or ''
        if contact != '':
            return u"Reseller: %s # %s" % (login, contact)
        else:
            return u"Reseller: %s" % (login,)

    @staticmethod
    def _format_plan_section(plan_name):
        return u"        Plan: %s" % (plan_name,)

    @staticmethod
    def _format_addon_plan_section(addon_plan_name):
        return u"        Addon Plan: %s" % (addon_plan_name,)

    @staticmethod
    def _format_reseller_plan_section(reseller_plan_name):
        return "Reseller Plan: %s" % reseller_plan_name
