from collections import defaultdict
from datetime import date, timedelta
from decimal import Decimal

from django.db.models import Sum
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from account.permissions import HasTabPermission, get_user_shop_filter
from shop_management.models.data.purchase import Purchase
from shop_management.models.data.purchase_return import PurchaseReturn
from shop_management.models.data.supplier_payment import SupplierPayment
from shop_management.models.data.shop import Shop


def money(value):
    return str(Decimal(value or 0).quantize(Decimal("0.01")))


def purchase_total(purchase):
    subtotal = sum(
        (Decimal(item.quantity or 0) * Decimal(item.buy_price or 0) for item in purchase.purchase_items.all()),
        Decimal("0"),
    )
    return max(subtotal - Decimal(purchase.amount or 0), Decimal("0"))


class PurchaseAnalysisReportView(APIView):
    """Posted purchase facts only; legacy drafts and editable balance fields are excluded."""

    permission_classes = [IsAuthenticated, HasTabPermission]
    required_tab = "reports"

    def get(self, request):
        try:
            end = date.fromisoformat(request.query_params.get("end_date") or str(date.today()))
            start = date.fromisoformat(request.query_params.get("start_date") or str(end - timedelta(days=29)))
        except ValueError:
            raise ValidationError({"date": "Use YYYY-MM-DD format."})
        if start > end:
            raise ValidationError({"end_date": "End date cannot be before start date."})
        try:
            shop = Shop.objects.get(get_user_shop_filter(request.user), pk=request.query_params.get("shop"))
        except (Shop.DoesNotExist, ValueError, TypeError):
            raise ValidationError({"shop": "Branch was not found or is not accessible."})

        purchases = list(Purchase.objects.filter(
            shop=shop, uses_inventory_engine=True,
            document_status=Purchase.DocumentStatus.POSTED,
            purchase_date__range=(start, end), is_deleted=False,
        ).select_related("supplier", "currency", "warehouse").prefetch_related("purchase_items__product"))
        purchase_ids = [row.id for row in purchases]
        period_returns = list(PurchaseReturn.objects.filter(
            shop=shop, uses_inventory_engine=True,
            document_status=PurchaseReturn.DocumentStatus.POSTED,
            return_date__date__range=(start, end),
        ).select_related("purchase", "purchase__currency", "purchase__supplier").prefetch_related("return_items__purchase_item__product"))

        returns_by_purchase = defaultdict(Decimal)
        all_returns = PurchaseReturn.objects.filter(
            purchase_id__in=purchase_ids,
            document_status=PurchaseReturn.DocumentStatus.POSTED,
            return_date__date__lte=end,
        ).prefetch_related("return_items")
        for returned in all_returns:
            returns_by_purchase[returned.purchase_id] += Decimal(returned.total_refund_amount or 0)

        by_supplier = defaultdict(lambda: {"name": "", "count": 0, "gross": Decimal("0"), "returns": Decimal("0")})
        by_product = defaultdict(lambda: {"name": "", "quantity": Decimal("0"), "cost": Decimal("0")})
        daily = defaultdict(Decimal)
        invoices = []
        gross_base = Decimal("0")
        outstanding_base = Decimal("0")
        paid_base = Decimal("0")

        for purchase in purchases:
            rate = Decimal(purchase.exchange_rate_used or 1)
            total = purchase_total(purchase)
            returned = returns_by_purchase[purchase.id]
            allocated = purchase.supplier_payment_allocations.filter(
                payment__status=SupplierPayment.Status.POSTED,
                payment__payment_date__lte=end,
            ).aggregate(total=Sum("allocated_amount"))["total"] or Decimal("0")
            outstanding = max(total - returned - allocated, Decimal("0"))
            gross_base += total * rate
            paid_base += allocated * rate
            outstanding_base += outstanding * rate
            daily[purchase.purchase_date] += total * rate
            supplier_key = purchase.supplier_id or 0
            supplier = by_supplier[supplier_key]
            supplier["name"] = purchase.supplier.name if purchase.supplier else "No supplier"
            supplier["count"] += 1; supplier["gross"] += total * rate; supplier["returns"] += returned * rate
            ratio = total / sum((Decimal(item.quantity or 0) * Decimal(item.buy_price or 0) for item in purchase.purchase_items.all()), Decimal("0")) if purchase.purchase_items.all() else Decimal("0")
            for item in purchase.purchase_items.all():
                if not item.product: continue
                product = by_product[item.product_id]; product["name"] = item.product.name
                product["quantity"] += Decimal(item.quantity or 0)
                product["cost"] += Decimal(item.quantity or 0) * Decimal(item.buy_price or 0) * ratio * rate
            invoices.append({
                "date": purchase.purchase_date, "number": purchase.purchase_number,
                "supplier": supplier["name"], "warehouse": purchase.warehouse.name if purchase.warehouse else "",
                "currency": purchase.currency.title if purchase.currency else "", "total": money(total),
                "returns": money(returned), "paid": money(allocated), "outstanding": money(outstanding),
            })

        return_base = sum(
            (Decimal(row.total_refund_amount or 0) * Decimal(row.purchase.exchange_rate_used or 1) for row in period_returns),
            Decimal("0"),
        )
        return Response({
            "shop": shop.name, "start_date": start, "end_date": end,
            "summary": {"gross_purchases": money(gross_base), "returns": money(return_base),
                        "net_purchases": money(gross_base - return_base), "paid": money(paid_base),
                        "outstanding": money(outstanding_base), "invoice_count": len(purchases)},
            "daily": [{"date": day, "purchases": money(value)} for day, value in sorted(daily.items())],
            "suppliers": [{"supplier_id": key, "supplier": row["name"], "invoice_count": row["count"],
                           "gross_purchases": money(row["gross"]), "returns": money(row["returns"]),
                           "net_purchases": money(row["gross"] - row["returns"])}
                          for key, row in sorted(by_supplier.items(), key=lambda item: item[1]["gross"], reverse=True)],
            "products": [{"product_id": key, "product": row["name"], "quantity_purchased": money(row["quantity"]),
                          "purchase_cost": money(row["cost"])}
                         for key, row in sorted(by_product.items(), key=lambda item: item[1]["cost"], reverse=True)],
            "invoices": invoices,
        })
