from datetime import datetime

import jdatetime
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db import transaction
from django.db.models import Sum, QuerySet
from simple_history.models import HistoricalRecords

from apps.authentication.models import OrganizationType, Organization
from apps.authentication.services.service import get_all_org_child
from apps.authorization.models import UserRelations
from apps.core.models import BaseModel
from apps.herd.models import Rancher
from apps.livestock.models import LiveStockType
from apps.product.services.distribution_child import get_all_distribution_child


class LivestockGroup(models.TextChoices):
    ROOSTAEI = "rural", "روستایی"  # noqa
    SANATI = "industrial", "صنعتی"  # noqa
    ASHAYERI = "nomadic", "عشایری"  # noqa


class LivestockType(models.TextChoices):
    LIGHT = "light", "سبک"
    HEAVY = "heavy", "سنگین"  # noqa


class LivestockSubtype(models.TextChoices):
    MILKING = "milking", "شیری"  # noqa
    FATTENING = "fattening", "پرواری"  # noqa


# Create your models here.

class ProductCategory(BaseModel):
    """ Category for products """

    name = models.CharField(max_length=250, default='empty')  # noqa
    type_choices = (
        ('free', 'Free'),  # free product
        ('gov', 'Governmental')  # government product
    )
    type = models.CharField(max_length=5, choices=type_choices, default='empty')
    img = models.CharField(max_length=100, default='empty')
    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        related_name='parents',
        null=True
    )

    def __str__(self):
        return f'name: {self.name} - type: {self.type}'

    def save(self, *args, **kwargs):
        super(ProductCategory, self).save(*args, **kwargs)


class Product(BaseModel):
    """ Child of reference product - like: brown rice """

    name = models.CharField(max_length=250, default='empty')  # noqa
    product_id = models.BigIntegerField(default=0)
    type_choices = (
        ('free', 'FREE'),  # free product
        ('gov', 'GOVERNMENTAL')  # government product
    )
    type = models.CharField(max_length=5, choices=type_choices)
    img = models.CharField(max_length=100, default='empty')
    category = models.ForeignKey(
        ProductCategory,
        on_delete=models.CASCADE,
        related_name='products',
        null=True
    )

    def generate_product_id(self):  # noqa
        """ generate id for product from 10 """

        last = Product.objects.filter(product_id__gte=10, product_id__lte=1999).order_by('-product_id').first()
        if last:
            next_code = last.product_id + 1
        else:
            next_code = 10
        return next_code

    def quota_information(self):
        """
        quotas information of product
        """

        # number of quotas
        quotas_count = self.quotas.filter(is_closed=False).count()

        # total weight of product that assigned in quota
        total_quotas_weight = self.quotas.filter(is_closed=False).aggregate(
            total=models.Sum('quota_weight')
        )['total'] or 0

        # total remaining weight of product quotas
        total_remaining_quotas_weight = self.quotas.filter(is_closed=False).aggregate(
            total=models.Sum('remaining_weight')
        )['total'] or 0

        total_distributed_weight = QuotaDistribution.objects.filter(
            quota__product_id=self.id,
            quota__is_closed=False
        ).aggregate(total_weight=models.Sum('weight'))['total_weight'] or 0

        total_sold = QuotaDistribution.objects.filter(
            quota__product_id=self.id,
            quota__is_closed=False
        ).aggregate(total_sold=models.Sum('been_sold'))['total_sold'] or 0

        total_warehouse_entry = QuotaDistribution.objects.filter(
            quota__product_id=self.id,
            quota__is_closed=False
        ).aggregate(total_entry=models.Sum('warehouse_entry'))['total_entry'] or 0

        data = {
            'product_id': self.id,
            'product_name': self.name,
            'quotas_count': quotas_count,
            'total_quotas_weight': total_quotas_weight,
            'total_remaining_quotas_weight': total_remaining_quotas_weight,
            'total_distributed_weight': total_distributed_weight,
            'total_sold': total_sold,
            'total_warehouse_entry': total_warehouse_entry,
        }

        return data

    def __str__(self):
        return f'name: {self.name} - type: {self.type}'

    def save(self, *args, **kwargs):
        if not self.product_id:
            self.product_id = self.generate_product_id()  # set product id
        super(Product, self).save(*args, **kwargs)


class ProductStats(BaseModel):
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='stats',
        null=True
    )
    organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='product_stats',
        null=True
    )
    product_org_stat_type = models.CharField(
        max_length=25,
        null=True,
        default='registerer',
        help_text='registerer or distributioned'  # noqa
    )
    quotas_number = models.PositiveBigIntegerField(default=0)
    sale_unit = models.CharField(max_length=25, null=True)
    active_quotas_weight = models.PositiveBigIntegerField(default=0)
    closed_quotas_weight = models.PositiveBigIntegerField(default=0)
    total_quota_weight = models.PositiveBigIntegerField(default=0)
    total_quota_remaining = models.PositiveBigIntegerField(default=0)
    total_remaining_distribution_weight = models.PositiveBigIntegerField(default=0)
    received_distribution_weight = models.PositiveBigIntegerField(default=0)
    given_distribution_weight = models.PositiveBigIntegerField(default=0)
    received_distribution_number = models.PositiveBigIntegerField(default=0)
    given_distribution_number = models.PositiveBigIntegerField(default=0)
    total_warehouse_entry = models.PositiveBigIntegerField(default=0)
    total_sold = models.PositiveBigIntegerField(default=0)
    total_transactions = models.PositiveBigIntegerField(default=0)

    def __str__(self):
        return f'Product: {self.product.name}-{self.product.id} stats'

    def save(self, *args, **kwargs):
        return super(ProductStats, self).save(*args, **kwargs)


class Attribute(BaseModel):
    """
    every reference product have multiple attributes
    """
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='attributes',
        null=True
    )
    name = models.CharField(max_length=100, default='empty')
    type = models.ForeignKey(
        'SaleUnit',
        on_delete=models.CASCADE,
        related_name="attributes",
        null=True
    )
    required = models.BooleanField(default=False)
    is_global = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.product.name} - {self.name}'

    def save(self, *args, **kwargs):
        return super(Attribute, self).save(*args, **kwargs)


class AttributeValue(BaseModel):
    """
    every child product should have attribute value for
    reference product attribute
    """

    quota = models.ForeignKey(
        'Quota',
        on_delete=models.CASCADE,
        related_name='attribute_values',
        null=True
    )
    org_quota_stat = models.ForeignKey(
        'OrganizationQuotaStats',
        on_delete=models.CASCADE,
        related_name='attribute_values',
        null=True
    )
    attribute = models.ForeignKey(
        Attribute,
        on_delete=models.CASCADE,
        related_name='values',
        null=True
    )
    value = models.IntegerField(default=0)

    def __str__(self):
        return f'Quota({self.quota.id}) - {self.attribute.name} - {self.value}'

    def save(self, *args, **kwargs):
        return super(AttributeValue, self).save(*args, **kwargs)


class Broker(BaseModel):
    """ Broker for product """

    BROKER_TYPES = (
        ('public', 'PUBLIC'),
        ('exclusive', 'EXCLUSIVE')
    )
    name = models.CharField(max_length=255, null=True)
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='product_broker',
        null=True
    )
    organization_type = models.ForeignKey(
        OrganizationType,
        on_delete=models.CASCADE,
        related_name='product_organization',
        null=True
    )
    calculation_strategy = models.ForeignKey(
        'SaleUnit',
        on_delete=models.CASCADE,
        related_name='brokers',
        null=True
    )
    broker_type = models.CharField(choices=BROKER_TYPES, max_length=20, null=True)
    fix_broker_price = models.PositiveBigIntegerField(default=0)
    fix_broker_price_state = models.BooleanField(default=False)
    suggested_broker_price = models.PositiveBigIntegerField(default=0)
    required = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.organization_type.name} - {self.product.name}'

    def save(self, *args, **kwargs):
        return super(Broker, self).save(*args, **kwargs)


class SaleUnit(BaseModel):
    """ Units of product for sale """

    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='sale_unit',
        null=True
    )
    unit = models.CharField(max_length=250, null=True, unique=True)
    required = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.product.name} - {self.unit}'

    def save(self, *args, **kwargs):
        return super(SaleUnit, self).save(*args, **kwargs)


class IncentivePlan(BaseModel):
    """ incentive plan for every quota """

    PLAN_TYPE_CHOICES = (
        ('ILQ', 'increasing livestock quotas'),
        ('SM', 'statistical/monitoring')
    )
    GROUP_CHOICES = (
        ('industrial', 'Industrial'),
        ('rural', 'Rural'),
        ('nomadic', 'Nomadic')
    )
    name = models.CharField(max_length=255)
    description = models.TextField(blank=True, null=True)
    registering_organization = models.ForeignKey(
        UserRelations,
        on_delete=models.CASCADE,
        related_name='incentive_plans',
        null=True
    )
    plan_type = models.CharField(choices=PLAN_TYPE_CHOICES, max_length=5, null=True)
    group = models.CharField(choices=GROUP_CHOICES, max_length=15, null=True)
    is_time_unlimited = models.BooleanField(default=False)
    start_date_limit = models.DateField(null=True, blank=True)
    end_date_limit = models.DateField(null=True, blank=True)

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        return super(IncentivePlan, self).save(*args, **kwargs)


class IncentivePlanRancher(BaseModel):
    plan = models.ForeignKey(
        IncentivePlan,
        on_delete=models.CASCADE,
        related_name='rancher_plans',
        null=True
    )
    rancher = models.ForeignKey(
        Rancher,
        on_delete=models.CASCADE,
        related_name='plans',
        null=True
    )
    livestock_type = models.ForeignKey(
        LiveStockType,
        on_delete=models.CASCADE,
        related_name='rancher_plans',
        null=True
    )
    allowed_quantity = models.PositiveBigIntegerField(default=0)
    used_quantity = models.PositiveBigIntegerField(default=0)

    def __str__(self):
        return f'{self.plan.name}-{self.rancher.first_name}-{self.livestock_type.name}'

    def save(self, *args, **kwargs):
        return super(IncentivePlanRancher, self).save(*args, **kwargs)


class Quota(BaseModel):
    """ quota for product with some conditions """

    registerer_organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='quotas',
        null=True
    )
    assigned_organizations = models.ManyToManyField(
        Organization,
        related_name='assigned_quotas',
        blank=True
    )
    quota_id = models.PositiveBigIntegerField(null=True, blank=True)
    quota_code = models.CharField(max_length=15, null=True)
    quota_weight = models.PositiveIntegerField(default=0)
    remaining_weight = models.PositiveBigIntegerField(default=0)
    quota_distributed = models.PositiveIntegerField(default=0)
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='quotas',
        null=True
    )
    sale_type = models.CharField(max_length=50, choices=[("free", "آزاد"), ("gov", "دولتی")])  # noqa
    sale_unit = models.ForeignKey(
        SaleUnit,
        on_delete=models.CASCADE,
        related_name='quotas',
        null=True
    )
    month_choices = ArrayField(base_field=models.IntegerField(), null=True)
    sale_license = ArrayField(base_field=models.IntegerField(), null=True)
    group = ArrayField(base_field=models.CharField(
        max_length=50,
        choices=[("rural", "روستایی"), ("industrial", "صنعتی"), ("nomadic", "عشایری")],  # noqa
    ), null=True)
    has_distribution_limit = models.BooleanField(default=False)
    distribution_mode = ArrayField(base_field=models.IntegerField(), blank=True, null=True)
    has_organization_limit = models.BooleanField(default=False)
    limit_by_organizations = models.ManyToManyField(
        Organization,
        related_name='quota_limits',
        blank=True
    )
    base_price_factory = models.PositiveBigIntegerField(default=0)
    base_price_cooperative = models.PositiveBigIntegerField(default=0)
    final_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    limit_by_herd_size = models.BooleanField(default=True)
    is_closed = models.BooleanField(default=False)
    closed_at = models.DateTimeField(null=True, blank=True)
    pos_sale_type_choices = (
        ('weight', 'WEIGHT'),
        ('count', 'COUNT'),
        ('all', 'ALL'),
    )
    pos_sale_type = models.CharField(choices=pos_sale_type_choices, max_length=25, default='all')
    pre_sale = models.BooleanField(default=False)
    pre_sale_balance = models.IntegerField(default=0)
    free_sale = models.BooleanField(default=False)
    free_sale_balance = models.IntegerField(default=0)
    edited_pricing_features = models.BooleanField(
        default=False,
        help_text='True when quota broker values & attribute values changed in distribution of quota'
    )

    def __str__(self):
        return f"Quota ({self.id}) for {self.product.name}"

    def generate_quota_id(self):  # noqa
        """ generate id for quota from 1001  """

        with transaction.atomic():
            last = Quota.objects.filter(
                quota_id__gte=10001,
                quota_id__lte=19999
            ).select_for_update().order_by('-quota_id').first()
            if last:
                next_code = last.quota_id + 1
            else:
                next_code = 10001
            return next_code

    def calculate_final_price(self):
        """ calculate final price of quota """

        factor_total = sum([
            f.value for f in self.attribute_values.all()
        ])
        broker_total = sum([
            b.value for b in self.broker_values.all()
        ])
        coop = self.base_price_cooperative or 0
        factory = self.base_price_factory or 0

        return factor_total + broker_total + coop + factory

    @property
    def remaining_quota_weight(self):
        """ calculate remaining quota weight after distribution """
        distributed_weight = self.distributions_assigned.aggregate(total=models.Sum("weight"))["total"] or 0
        return self.quota_weight - distributed_weight

    def is_in_valid_time(self):
        """ check if quota allowed time for distribute, sale and... is expired """
        now = datetime.now()
        persian_date = jdatetime.datetime.fromgregorian(datetime=now)
        if self.has_distribution_limit:
            return persian_date.month in self.distribution_mode
        return True

    def is_in_sale_licence_time(self):
        """ check if quota allowed time for sale and... is expired """
        now = datetime.now()
        persian_date = jdatetime.datetime.fromgregorian(datetime=now)

        return persian_date.month in self.sale_license

    def quota_amount_by_org(self, org: Organization):
        """
        get stats of quota from org quota stat model
        """

        stat = OrganizationQuotaStats.objects.filter(
            quota=self,
            organization=org
        )

        if not stat.exists() and org.type.key == 'ADM':
            # if org is admin just see the quota was created by registerer org
            org = self.registerer_organization
            stat = OrganizationQuotaStats.objects.filter(
                quota=self,
                organization=org
            )

        elif not stat.exists():
            # get childs of organization
            org_childs = get_all_org_child(org)  # noqa

            # calculate total stats of child orgs
            stat = OrganizationQuotaStats.objects.filter(
                quota=self,
                organization_id__in=[child.id for child in org_childs]
            ).aggregate(
                total_quota_weight=models.Sum('total_amount'),
                total_remaining_weight=models.Sum('remaining_amount'),
                total_quota_distributed=models.Sum('total_distributed'),
                total_been_sold=models.Sum('sold_amount'),
                total_inventory_received=models.Sum('inventory_received'),
                total_inventory_entry_balance=models.Sum('inventory_entry_balance')
            )
            return {
                "id": 0,
                "quota_weight": stat['total_quota_weight'],
                "remaining_weight": stat['total_remaining_weight'],
                "quota_distributed": stat['total_quota_distributed'],
                "been_sold": stat['total_been_sold'],
                "inventory_received": stat['total_inventory_received'],
                "inventory_entry_balance": stat['total_inventory_entry_balance'],
            }

        return {
            "id": stat.first().id if stat.exists() else 0,
            "quota_weight": stat.first().total_amount if stat.exists() else 0,
            "remaining_weight": stat.first().remaining_amount if stat.exists() else 0,
            "quota_distributed": stat.first().total_distributed if stat.exists() else 0,
            "been_sold": stat.first().sold_amount if stat.exists() else 0,
            "inventory_received": stat.first().inventory_received if stat.exists() else 0,
            "inventory_entry_balance": stat.first().inventory_entry_balance if stat.exists() else 0,
        }

    def get_quota_stat(self, org: Organization):
        """
        get stats of quota from org quota stat model
        """

        stat = OrganizationQuotaStats.objects.filter(
            quota=self,
            organization=org
        )

        if not stat.exists() and org.type.key == 'ADM':
            # if org is admin just see the quota was created by registerer org
            org = self.registerer_organization
            stat = OrganizationQuotaStats.objects.filter(
                quota=self,
                organization=org
            )
        elif not stat.exists() and org.type.key != 'ADM':
            stat, created = OrganizationQuotaStats.objects.get_or_create(
                quota=self,
                organization=org,
                stat_type='distribution'
            )

        stat = stat.first() if isinstance(stat, QuerySet) else stat

        # first quota created is none stat on attribute & broker value table
        if stat and stat.stat_type == 'quota':
            return None
        return stat if stat else None

    def soft_delete(self):
        self.trash = True
        self.save(update_fields=['trash'])

        for dist in self.distributions_assigned.all():
            dist.soft_delete()

    def save(self, calculate_final_price=None, *args, **kwargs):
        if not self.quota_id:
            self.quota_id = self.generate_quota_id()
        if calculate_final_price:
            if not self.final_price:
                self.final_price = self.calculate_final_price()
        return super(Quota, self).save(*args, **kwargs)


class QuotaFinalPriceTypes(BaseModel):
    name = models.CharField(max_length=250)
    en_name = models.CharField(max_length=250, null=True)

    def __str__(self):
        return f'{self.name}'

    def save(self, *args, **kwargs):
        return super(QuotaFinalPriceTypes, self).save(*args, **kwargs)


class QuotaPriceCalculationItems(BaseModel):
    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name='pricing_items',
        null=True
    )
    pricing_type = models.ForeignKey(
        'QuotaFinalPriceTypes',
        on_delete=models.CASCADE,
        related_name='pricing_items',
        null=True
    )
    name = models.CharField(max_length=250)
    value = models.PositiveBigIntegerField(default=0)

    def __str__(self):
        return f'{self.quota.quota_id}-{self.pricing_type.name}-{self.name}'

    def save(self, *args, **kwargs):
        return super(QuotaPriceCalculationItems, self).save(*args, **kwargs)


class QuotaStats(BaseModel):
    quota = models.OneToOneField(
        Quota,
        on_delete=models.CASCADE,
        related_name='stats',
        null=True,
    )
    total_distributed = models.PositiveBigIntegerField(default=0)
    remaining = models.PositiveBigIntegerField(default=0)
    total_inventory = models.PositiveBigIntegerField(default=0)
    total_sale = models.PositiveBigIntegerField(default=0)

    def __str__(self):
        return f'Quota: {self.quota.quota_id} stats'

    def save(self, *args, **kwargs):
        return super(QuotaStats, self).save(*args, **kwargs)


class QuotaUsage(BaseModel):
    quota_stat = models.ForeignKey(
        'OrganizationQuotaStats',
        on_delete=models.CASCADE,
        related_name='usages',
        null=True
    )
    distribution = models.ForeignKey(
        "QuotaDistribution",
        on_delete=models.CASCADE,
        related_name='usages',
        null=True
    )
    rancher = models.ForeignKey(
        Rancher,
        on_delete=models.CASCADE,
        related_name='usages',
        null=True
    )
    livestock_type = models.ForeignKey(
        LiveStockType,
        on_delete=models.CASCADE,
        related_name='usages',
        null=True
    )
    incentive_plan = models.ForeignKey(
        IncentivePlan,
        null=True,
        blank=True,
        on_delete=models.SET_NULL
    )
    count = models.PositiveIntegerField()
    base_quota_used = models.IntegerField(default=0)
    incentive_quota_used = models.IntegerField(default=0)
    usage_type_choices = (
        ('base', 'BASE'),
        ('incentive', 'INCENTIVE'),
    )
    usage_type = models.CharField(max_length=150, choices=usage_type_choices, null=True)

    def __str__(self):
        return f'rancher: {self.rancher.ranching_farm} - plan: {self.incentive_plan.name}'

    def save(self, *args, **kwargs):
        return super(QuotaUsage, self).save(*args, **kwargs)


class QuotaIncentiveAssignment(BaseModel):
    """ assign incentive plan to quota """

    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name="incentive_assignments",
        null=True
    )
    incentive_plan = models.ForeignKey(
        IncentivePlan,
        on_delete=models.CASCADE,
        related_name='quota_assignment',
        null=True
    )
    heavy_value = models.PositiveBigIntegerField(default=0)
    light_value = models.PositiveBigIntegerField(default=0)
    livestock_type = models.ForeignKey(
        LiveStockType,
        on_delete=models.CASCADE,
        related_name='incentive_plans',
        null=True
    )
    quantity_kg = models.PositiveBigIntegerField(default=0)

    def calculate_heavy_value(self):
        """ calculate total livestock heavy value of incentive plans """
        heavy_weight = QuotaIncentiveAssignment.objects.filter(
            quota=self.quota, livestock_type__weight_type='H'
        ).aggregate(total=models.Sum('quantity_kg'))['total'] or 0

        self.heavy_value = heavy_weight
        self.save()

    def calculate_light_value(self):
        """ calculate total livestock light value of incentive plans """

        heavy_weight = QuotaIncentiveAssignment.objects.filter(
            quota=self.quota, livestock_type__weight_type='L'
        ).aggregate(total=models.Sum('quantity_kg'))['total'] or 0

        self.heavy_value = heavy_weight
        self.save()

    def __str__(self):
        return f"Quota ({self.quota.id}) for {self.incentive_plan.name}"

    def save(self, *args, **kwargs):
        return super(QuotaIncentiveAssignment, self).save(*args, **kwargs)


class QuotaBrokerValue(BaseModel):
    """ broker attributes value for quota """

    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name="broker_values",
        null=True
    )
    org_quota_stat = models.ForeignKey(
        'OrganizationQuotaStats',
        on_delete=models.CASCADE,
        related_name='broker_values',
        null=True
    )
    broker = models.ForeignKey(
        Broker,
        on_delete=models.CASCADE,
        related_name='values'
    )
    value = models.PositiveBigIntegerField(default=0)

    def __str__(self):
        return f"Quota ({self.quota.id}) for Broker({self.broker.organization_type.name})"

    def save(self, *args, **kwargs):
        return super(QuotaBrokerValue, self).save(*args, **kwargs)


class QuotaLivestockAllocation(BaseModel):
    """ livestock allocation to quota """

    quota = models.ForeignKey(
        "Quota",
        on_delete=models.CASCADE,
        related_name="livestock_allocations",
        null=True
    )
    livestock_group = models.CharField(max_length=20, choices=LivestockGroup.choices, null=True)
    livestock_type = models.ForeignKey(
        LiveStockType,
        on_delete=models.CASCADE,
        related_name='allocations',
        null=True
    )
    livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices, null=True)
    quantity_kg = models.PositiveBigIntegerField(default=0)

    """ 
    @using for set unique values between fields
    class Meta:
        unique_together = ('quota', 'livestock_group', 'livestock_type', 'livestock_subtype')
    """

    def __str__(self):
        return f"{self.livestock_group} - {self.livestock_type}/{self.livestock_subtype}: {self.quantity_kg}kg"

    def save(self, *args, **kwargs):
        return super(QuotaLivestockAllocation, self).save(*args, **kwargs)


class QuotaLiveStockAgeLimitation(BaseModel):
    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name='livestock_age_limitations',
        null=True
    )
    livestock_type = models.ForeignKey(
        LiveStockType,
        on_delete=models.CASCADE,
        related_name='quota_limitations',
        null=True
    )
    livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices, null=True)
    age_month = models.PositiveIntegerField(default=0)

    def __str__(self):
        return f"{self.livestock_type}/{self.livestock_subtype}: {self.age_month} month"

    def save(self, *args, **kwargs):
        return super(QuotaLiveStockAgeLimitation, self).save(*args, **kwargs)


class QuotaDistribution(BaseModel):
    parent_distribution = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        related_name='children',
        null=True
    )
    assigner_organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='distributions_assigner',
        null=True
    )
    assigned_organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='distributions',
        null=True
    )
    description = models.TextField(max_length=1000, null=True, blank=True)
    distribution_id = models.CharField(max_length=20, null=True)
    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name='distributions_assigned',
        null=True
    )
    weight = models.PositiveBigIntegerField(default=0)
    remaining_weight = models.PositiveBigIntegerField(default=0)
    distributed = models.PositiveBigIntegerField(default=0)
    warehouse_entry = models.PositiveBigIntegerField(default=0)
    warehouse_balance = models.PositiveBigIntegerField(default=0)
    been_sold = models.PositiveBigIntegerField(default=0)
    history = HistoricalRecords()
    # pre_sale = models.BooleanField(default=False)
    pre_sale_balance = models.PositiveBigIntegerField(default=0)
    # free_sale = models.BooleanField(default=False)
    free_sale_balance = models.PositiveBigIntegerField(default=0)

    def generate_distribution_id(self):
        """ generate special id for quota distribution """

        year = jdatetime.datetime.now().year
        month = jdatetime.datetime.now().month
        day = jdatetime.datetime.now().day
        product_id = self.quota.product.product_id
        quota_id = self.quota.quota_id

        base_code = f"{str(year)[3]}{month}{day}{product_id}{quota_id}"

        similar_codes = QuotaDistribution.objects.filter(distribution_id__startswith=base_code).count()
        counter = str(similar_codes + 1).zfill(4)

        return f"{base_code}{counter}"

    def __str__(self):
        return f"{self.distribution_id}-"

    def soft_delete(self):
        self.trash = True
        self.save(update_fields=['trash'])

        childs = get_all_distribution_child(self)  # noqa
        for child in childs:
            child.soft_delete()

        for entry in self.inventory_entry.all():
            entry.soft_delete()

    @property
    def free_sale(self):
        return self.quota.free_sale

    @property
    def pre_sale(self):
        return self.quota.pre_sale

    def save(self, additional_data=None, *args, **kwargs):
        if not self.distribution_id:
            self.distribution_id = self.generate_distribution_id()

        if additional_data:
            self.additional_data = additional_data
            print(self.additional_data)
        return super(QuotaDistribution, self).save(*args, **kwargs)


class OrganizationQuotaStats(BaseModel):
    organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='org_quota_stats',
        null=True
    )
    quota = models.ForeignKey(
        Quota,
        on_delete=models.CASCADE,
        related_name='org_quota_stats',
        null=True
    )

    distributions = models.ManyToManyField(QuotaDistribution)
    total_amount = models.PositiveBigIntegerField(default=0)
    total_distributed = models.PositiveBigIntegerField(default=0)
    inventory_received = models.PositiveBigIntegerField(default=0)
    inventory_entry_balance = models.PositiveBigIntegerField(default=0)
    sold_amount = models.PositiveBigIntegerField(default=0)
    remaining_amount = models.PositiveBigIntegerField(default=0)  # total - sold
    stat_type = models.CharField(max_length=150, choices=(
        ('distribution', 'DISTRIBUTION'),
        ('quota', 'QUOTA'),
    ), default='distribution')
    free_sale_balance = models.PositiveBigIntegerField(default=0)
    pre_sale_balance = models.PositiveBigIntegerField(default=0)

    def update_amount(self, main_quota=None):
        """ calculate total/sold/remaining """
        from apps.warehouse.models import InventoryQuotaSaleItem

        if main_quota:
            # calculate total amount of distribution
            self.total_amount = self.distributions.filter().aggregate(
                total=Sum('weight')
            )['total'] or 0
            print(self.total_amount)
        self.total_distributed = self.distributions.filter().exclude(
            assigned_organization=self.organization
        ).aggregate(total=Sum('weight'))['total'] or 0

        self.sold_amount = InventoryQuotaSaleItem.objects.filter(
            quota_distribution__in=self.distributions.all(),
            transaction__transaction_status='success'
        ).aggregate(
            total=Sum('weight')
        )['total'] or 0

        self.remaining_amount = self.total_amount - self.total_distributed

        self.save()

    def __str__(self):
        return f"organization: {self.organization} - quota: {self.quota}"

    def save(self, *args, **kwargs):
        return super(OrganizationQuotaStats, self).save(*args, **kwargs)
