import typing

from django.db.models import Count, Q, Value
from django.db.models import Sum
from django.db.models.functions import Coalesce

from apps.herd.models import Rancher
from apps.herd.services.rancher_service import RancherService
from apps.livestock.models import LiveStock, TemporaryLiveStock
from apps.product.models import Quota, QuotaDistribution, OrganizationQuotaStats
from apps.warehouse.models import (
    InventoryEntry,
    InventoryQuotaSaleItem
)

LIVESTOCK_GROPES = {
    'industrial': 'I',  # صنعتی
    'rural': 'V',  # روستایی
    'nomadic': 'N'  # عشایری
}


def get_rancher_statistics(rancher: Rancher = None) -> typing.Any:
    """ get statistics of a rancher """  # noqa

    # get rancher livestock
    livestocks = LiveStock.objects.filter(herd__rancher=rancher)  # noqa

    # get rancher temporary livestock , rancher will have no herd
    temporary_livestock = TemporaryLiveStock.objects.filter(rancher=rancher)

    if livestocks.exists():
        stats = livestocks.aggregate(
            herd_count=Count("herd", distinct=True),
            light_count=Count('id', filter=Q(weight_type='L')),
            heavy_count=Count('id', filter=Q(weight_type='H')),
            sheep=Count('id', filter=Q(type__name='گوسفند')),  # noqa
            goat=Count('id', filter=Q(type__name='بز')),
            cow=Count('id', filter=Q(type__name='گاو')),
            camel=Count('id', filter=Q(type__name='شتر')),
            horse=Count('id', filter=Q(type__name='اسب')),
        )
    else:
        stats = temporary_livestock.aggregate(
            light_count=Coalesce(Sum('count', filter=Q(livestock_type__weight_type='L')), Value(0)),
            heavy_count=Coalesce(Sum('count', filter=Q(livestock_type__weight_type='H')), Value(0)),
            sheep=Coalesce(Sum('count', filter=Q(livestock_type__name='گوسفند')), Value(0)),  # noqa
            goat=Coalesce(Sum('count', filter=Q(livestock_type__name='بز')), Value(0)),
            cow=Coalesce(Sum('count', filter=Q(livestock_type__name='گاو')), Value(0)),
            camel=Coalesce(Sum('count', filter=Q(livestock_type__name='شتر')), Value(0)),
            horse=Coalesce(Sum('count', filter=Q(livestock_type__name='اسب')), Value(0)),
        )

    return [{'name': key, 'value': value} for key, value in stats.items()], stats


def get_rancher_statistic_by_herd(rancher: Rancher = None) -> typing.Any:
    """ get statistics of a rancher by his herds """  # noqa

    herds = rancher.herd.all()
    if herds.exists():
        stats = herds.aggregate(
            herd_count=Count("id", distinct=True),
            light_count=Sum('light_livestock_number'),
            heavy_count=Sum('heavy_livestock_number'),
            sheep=Sum('light_livestock_number'),  # noqa
            cow=Sum('heavy_livestock_number'),
        )
    else:
        stats = {
            'herd_count': 0,
            'light_count': 0,
            'heavy_count': 0,
            'sheep': 0,
            'cow': 0,
        }

    return [{'name': key, 'value': value} for key, value in stats.items()], stats


def rancher_quota_weight(
        rancher: Rancher,
        inventory_entry: InventoryEntry = None,
        distribution: QuotaDistribution = None,
        quota: Quota = None,
        quota_stat: OrganizationQuotaStats = None
):
    """
    :param rancher: Rancher instance
    :param inventory_entry: InventoryEntry instance
    :param distribution: QuotaDistribution instance
    :param quota: Quota instance
    :param quota_stat: OrganizationQuotaStat instance
    :return: dict {total, by_type}
    """

    live_stock_meta = {
        "گوسفند": "sheep",  # noqa
        "بز": "goat",
        "گاو": "cow",
        "شتر": "camel",
        "اسب": "horse"
    }

    if inventory_entry:
        quota: Quota = inventory_entry.distribution.quota
    elif distribution:
        quota: Quota = distribution.quota
    else:
        quota: Quota = quota

    # list of quota live stock allocations
    allocations = list(quota.livestock_allocations.all().select_related('livestock_type'))
    # list of quota incentive plans
    incentive_plans = list(quota.incentive_assignments.all().select_related('livestock_type'))
    # list of rancher herds
    herds = rancher.herd.all()
    herd_livestock_gropes = [herd.activity if herd.activity else 'V' for herd in herds]
    livestock_counts_list, livestock_counts_dict = get_rancher_statistic_by_herd(rancher)

    total_weight = 0
    merged = {}

    # calculate quota base weight by livestock type & base total weight
    for item in allocations:  # noqa
        if item.livestock_type:
            if LIVESTOCK_GROPES[item.livestock_group] in herd_livestock_gropes:
                animal_type_fa = item.livestock_type.name
                animal_type_en = item.livestock_type.en_name
                per_head = item.quantity_kg
                count = livestock_counts_dict.get(live_stock_meta.get(animal_type_fa), 0)

                weight = per_head * count
                total_weight += weight

                if animal_type_en not in merged:
                    merged[animal_type_en] = {
                        "name_fa": animal_type_fa,
                        "weight": weight,
                        "type": item.livestock_type.weight_type
                    }
                else:
                    merged[animal_type_en]['weight'] += weight

    # calculate rancher incentive plans weight by livestock type & add it to total_weight
    for item in incentive_plans:
        rancher_plans = item.incentive_plan.rancher_plans.select_related(
            'rancher',
            'livestock_type'
        ).filter(rancher=rancher, livestock_type=item.livestock_type)
        if rancher_plans.exists():
            # multiple count of rancher livestock on incentive plan &
            # incentive plan quantity by this livestock type
            rancher_plan_weight = rancher_plans.first().allowed_quantity * item.quantity_kg
            total_weight += rancher_plan_weight
    # get rancher remaining usage of quota for purchase
    rancher_remaining_usage = RancherService.get_total_used_weight(
        rancher,
        InventoryQuotaSaleItem,
        quota_stat=quota_stat
    )

    if total_weight - rancher_remaining_usage < 0:
        remaining_weight = 0
        free_sale = rancher_remaining_usage - total_weight
        total_purchase = free_sale + total_weight
    else:
        remaining_weight = total_weight - rancher_remaining_usage
        free_sale = 0
        total_purchase = total_weight - remaining_weight

    return {
        "total_weight": total_weight,
        "remaining_weight": remaining_weight,
        'free_sale': free_sale,
        'total_purchase': total_purchase,
        "rancher_temporary_livestock": rancher.without_herd,
        "by_type": [{
            "name": key,
            "name_fa": value['name_fa'],
            "weight": value['weight'],
            "type": value['type']
        } for key, value in merged.items()]
    }
