import json
from collections import defaultdict
import itertools

from parallels.core.migration_list.entities.list_data import MigrationListData
from parallels.core.migration_list.entities.plans import MigrationListPlans
from parallels.core.migration_list.entities.subscription_info import SubscriptionMappingInfo
from parallels.core.migration_list.reader.base_reader import MigrationListReaderBase


class MigrationListReaderJSON(MigrationListReaderBase):
    def read(self, fileobj, subscriptions_only=False, read_resellers=True):
        """Read migration list from file in JSON format

        Returns tuple, where:
        1) The first element is object which describes migration list data:
        parallels.core.migration_list.entities.list_data.MigrationListData
        2) The second element is list of error messages occurred when reading migration list.

        :param fileobj: file-like object to read migration list from
        :param bool subscriptions_only: whether to include subscriptions only, or also read plans, resellers, etc
        :param bool read_resellers: whether to read information about resellers; if false all subscriptions and
            customers owned by resellers will be owned by admin
        :rtype: tuple(parallels.core.migration_list.entities.list_data.MigrationListData, list[str | unicode))
        """
        data = json.load(fileobj)

        resellers = {}
        if not subscriptions_only and read_resellers:
            for reseller_login, reseller_info in data.get("resellers", {}).items():
                resellers[reseller_login] = reseller_info.get("plan")

        customers = {}
        if not subscriptions_only:
            for customer_login, customer_info in data.get("customers", {}).items():
                if read_resellers:
                    customers[customer_login] = customer_info.get("owner")
                else:
                    customers[customer_login] = None  # reassign all customer to admin

        plans = defaultdict(set)
        if not subscriptions_only:
            for plan in itertools.chain(data.get("plans", []), data.get("addon_plans", [])):
                if read_resellers or plan.get("owner") is None:
                    plans[plan.get("owner")].add(plan["name"])

        subscriptions = {}

        for subscription_name, subscription in data.get("subscriptions", {}).items():
            if not subscriptions_only:
                if subscription.get('owner') is None or subscription.get('owner') in customers or read_resellers:
                    owner = subscription.get('owner')
                    reseller_owner = None
                    if subscription.get('owner') in customers:
                        reseller_owner = data.get("customers", {}).get(subscription.get('owner'), {}).get('owner')

                    if read_resellers or not reseller_owner:
                        plan = subscription.get('plan')
                        addon_plans = subscription.get('addon_plans', [])
                    else:
                        plan = None
                        addon_plans = []
                else:
                    owner = None
                    plan = None
                    addon_plans = []
            else:
                owner = None
                plan = None
                addon_plans = []

            subscriptions[subscription_name] = SubscriptionMappingInfo(
                plan=plan,
                addon_plans=addon_plans,
                owner=owner,
                ipv4=subscription.get('ipv4'),
                ipv6=subscription.get('ipv6'),
                node_ip=subscription.get('node_ip')
            )

        migration_list_data = MigrationListData(subscriptions, resellers, customers, plans)
        return migration_list_data, []

    def read_resellers(self, fileobj):
        """Read resellers from migration list file in JSON format

        Returns tuple, where:
        1) The first element is a dictionary, describing resellers:
        {reseller login: reseller plan}.
        If plan is not set and reseller subscription is custom, then reseller plan is None.
        2) The second element is list of error messages occurred when reading migration list.

        :param fileobj: file-like object to read migration list from
        :rtype: tuple(dict[str | unicode, str | unicode], list[str | unicode])
        """
        data = json.load(fileobj)
        resellers = {}
        for reseller_login, reseller_info in data.get("resellers", {}).items():
            resellers[reseller_login] = reseller_info.get("plan")

        return resellers, []

    @classmethod
    def read_plans(cls, fileobj, read_resellers=True):
        """Read plans from migration list file in JSON format

        Returns tuple, where:
        1) The first element is object which describes migration list plans:
        parallels.core.migration_list.entities.plans.MigrationListPlans
        2) The second element is list of error messages occurred when reading migration list.

        :param fileobj: file-like object to read migration list from
        :param bool read_resellers: whether to read information about resellers;
            if false all plans under resellers are ignored
        :rtype: tuple(parallels.core.migration_list.entities.plans.MigrationListPlans, list[str | unicode])
        """
        data = json.load(fileobj)

        plans = defaultdict(set)
        for plan in data.get("plans", []):
            if not read_resellers and plan.get("owner") is not None:
                continue

            plans[plan.get("owner")].add(plan["name"])

        addon_plans = defaultdict(set)
        for addon_plan in data.get("addon_plans", []):
            if not read_resellers and addon_plan.get("owner") is not None:
                continue

            addon_plans[addon_plan.get("owner")].add(addon_plan["name"])

        plans = MigrationListPlans(plans, addon_plans)

        return plans, []
