import json

from apps.core.custom_middlewares.request_middleware import get_current_request_body
from apps.product.models import QuotaDistribution, OrganizationQuotaStats, AttributeValue, Attribute, QuotaBrokerValue, \
    Broker
from apps.product.validators.quota_stats_validator import QuotaStatsValidator
from apps.warehouse.models import InventoryEntry


def import_price_features(data, assigned_stat, quota):
    """
    create price attributes data & broker pricing value for every distribution
    """
    assigned_stat.attribute_values.all().delete()
    assigned_stat.broker_values.all().delete()

    if 'price_attributes_data' and 'broker_data' in data.keys():
        attr_price_values = [AttributeValue(
            attribute=Attribute.objects.get(id=attr['attribute']),
            value=attr['value'],
            quota=quota,
            org_quota_stat=assigned_stat
        ) for attr in data['price_attributes_data']]
        AttributeValue.objects.bulk_create(attr_price_values)  # bulk create

        broker_price_values = [
            QuotaBrokerValue(
                quota=quota,
                broker=Broker.objects.get(id=broker['broker']),
                value=broker['value'],
                org_quota_stat=assigned_stat
            ) for broker in data['broker_data']
        ]
        QuotaBrokerValue.objects.bulk_create(broker_price_values)  # bulk create

        quota.edited_pricing_features = True
        quota.save(update_fields=['edited_pricing_features'])


class QuotaStatsService:
    @staticmethod
    def apply_distribution(distribution: QuotaDistribution):
        quota = distribution.quota
        # origin org
        assigner = distribution.assigner_organization
        if assigner.type.key == 'ADM':
            assigner = quota.registerer_organization

        # destination org
        assigned = distribution.assigned_organization

        QuotaStatsValidator.validate_assigner_has_enough(
            assigner_org=assigner,
            quota=quota,
            amount=distribution.weight
        )

        # ================ origin ================
        assigner_stat, created = OrganizationQuotaStats.objects.get_or_create(
            organization=assigner,
            quota=quota,
        )
        if created:
            assigner_stat.stat_type = 'distribution'
            assigner_stat.save()
        if assigner_stat.stat_type == 'distribution':
            assigner_stat.remaining_amount -= distribution.weight
            assigner_stat.total_distributed += distribution.weight
            assigner_stat.save()

        # ============== destination ================
        assigned_stat, _ = OrganizationQuotaStats.objects.get_or_create(
            organization=assigned,
            quota=quota,
            stat_type='distribution'
        )
        assigned_stat.total_amount += distribution.weight
        assigned_stat.remaining_amount += distribution.weight
        assigned_stat.distributions.add(distribution)
        assigned_stat.save()

        # create pricing attributes and broker pricing data
        data = json.loads(get_current_request_body().decode())
        import_price_features(data, assigned_stat, quota)

    @staticmethod
    def update_distribution(distribution: QuotaDistribution, old_weight: int):
        diff = distribution.weight - old_weight

        quota = distribution.quota

        assigner = distribution.assigner_organization
        assigned = distribution.assigned_organization

        QuotaStatsValidator.validate_distribution_update(
            assigner_org=assigner,
            assigned_org=assigned,
            quota=quota,
            old_amount=old_weight,
            new_amount=distribution.weight
        )

        assigner_stat = OrganizationQuotaStats.objects.get(
            organization=assigner,
            quota=quota
        )
        assigned_stat = OrganizationQuotaStats.objects.get(
            organization=assigned,
            quota=quota
        )

        # create pricing attributes and broker pricing data
        data = json.loads(get_current_request_body().decode())
        import_price_features(data, assigned_stat, quota)

        if diff == 0:
            return

        if assigner_stat.stat_type == 'distribution':
            # if diff > 0 it is added  to destination
            assigner_stat.remaining_amount -= diff
            assigner_stat.total_distributed += diff
            assigner_stat.save()
        assigned_stat.total_amount += diff
        assigned_stat.remaining_amount += diff
        assigned_stat.save()

    @staticmethod
    def delete_distribution(distribution: QuotaDistribution):
        quota = distribution.quota

        # origin
        assigner_stat = OrganizationQuotaStats.objects.get(
            organization=distribution.assigner_organization,
            quota=quota
        )
        if assigner_stat.stat_type == 'distribution':
            assigner_stat.remaining_amount += distribution.weight
            assigner_stat.total_distributed -= distribution.weight
            assigner_stat.save()

        # destination
        assigned_stat = OrganizationQuotaStats.objects.get(
            organization=distribution.assigned_organization,
            quota=quota
        )
        assigned_stat.total_amount -= distribution.weight
        assigned_stat.remaining_amount -= distribution.weight
        assigned_stat.distributions.remove(distribution)
        assigned_stat.save()

    @staticmethod
    def apply_inventory_entry(entry: InventoryEntry, created):
        quota = entry.quota
        weight = entry.weight

        if not created:
            old_weight = entry._old_weight  # noqa
            diff = weight - old_weight
        else:
            diff = weight
        print("0000", diff)
        QuotaStatsService._propagate_inventory(
            organization=entry.organization,
            quota=quota,
            diff=diff
        )

    @staticmethod
    def remove_inventory_entry(entry: InventoryEntry):
        quota = entry.quota
        weight = entry.weight

        QuotaStatsService._propagate_inventory(
            organization=entry.organization,
            quota=quota,
            diff=-weight
        )

    @staticmethod
    def _propagate_inventory(organization, quota, diff):
        org = organization
        stat_queryset = OrganizationQuotaStats.objects.select_related('quota', 'organization')
        update_my_remaining_stat = True

        parent_orgs = []

        while org:
            parent_orgs.append(org)
            org = org.parent_organization

        parent_orgs_ids = [org.id for org in parent_orgs]

        target_org_ids = set(parent_orgs_ids)

        if quota.registerer_organization.id not in target_org_ids:
            target_org_ids.add(quota.registerer_organization.id)

        stats = (stat_queryset.filter(
            quota=quota,
            organization__in=target_org_ids
        ))

        for stat in stats:
            stat.inventory_received = (stat.inventory_received or 0) + diff

            # just update my own stat remaining quota weight
            if update_my_remaining_stat:
                stat.remaining_amount -= diff
                update_my_remaining_stat = False

            if stat.inventory_received < 0:
                stat.inventory_received = 0

        OrganizationQuotaStats.objects.bulk_update(stats, ['inventory_received', 'remaining_amount'])
