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.customer_payment import CustomerPayment
from shop_management.models.data.sale import Sale
from shop_management.models.data.sale_return import SaleReturn, SaleReturnItem, SaleReturnItemBatchRestoration
from shop_management.models.data.shop import Shop


def money(value):
    return str(Decimal(value or 0).quantize(Decimal("0.01")))


class SalesPerformanceReportView(APIView):
    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."})
        shop_id = request.query_params.get("shop")
        if not shop_id:
            raise ValidationError({"shop": "Branch is required."})
        try:
            shop = Shop.objects.get(get_user_shop_filter(request.user), pk=shop_id)
        except Shop.DoesNotExist:
            raise ValidationError({"shop": "Branch was not found or is not accessible."})

        sales = list(Sale.objects.filter(
            shop=shop, uses_inventory_engine=True, document_status=Sale.DocumentStatus.POSTED,
            invoice_date__range=(start, end),
        ).select_related("customer", "currency").prefetch_related("sale_items__product"))
        sale_ids = [sale.id for sale in sales]
        returns = list(SaleReturn.objects.filter(
            sale_id__in=sale_ids, document_status=SaleReturn.DocumentStatus.POSTED,
            return_date__date__range=(start, end),
        ).select_related("sale", "sale__currency").prefetch_related("return_items__sale_item__product"))

        total_sales = sum((sale.calculated_total * sale.exchange_rate_used for sale in sales), Decimal("0"))
        total_cogs = sum((sum((item.cogs_total for item in sale.sale_items.all()), Decimal("0")) * sale.exchange_rate_used for sale in sales), Decimal("0"))
        total_returns = sum((row.total_refound_amount * row.sale.exchange_rate_used for row in returns), Decimal("0"))
        restorations = SaleReturnItemBatchRestoration.objects.filter(return_item__sale_return__in=returns).select_related("return_item__sale_return__sale", "return_item__sale_item__product")
        returned_cogs = sum((row.quantity * row.unit_cost * row.return_item.sale_return.sale.exchange_rate_used for row in restorations), Decimal("0"))
        payments = CustomerPayment.objects.filter(
            shop=shop, status=CustomerPayment.Status.POSTED, payment_date__range=(start, end),
            allocations__sale_id__in=sale_ids,
        ).prefetch_related("allocations").distinct()
        collected_base = sum((allocation.allocated_amount * payment.exchange_rate_used for payment in payments for allocation in payment.allocations.all() if allocation.sale_id in sale_ids), Decimal("0"))
        net_sales = total_sales - total_returns
        net_cogs = total_cogs - returned_cogs
        gross_profit = net_sales - net_cogs

        daily = defaultdict(lambda: {"sales": Decimal("0"), "returns": Decimal("0"), "cogs": Decimal("0")})
        products = defaultdict(lambda: {"quantity": Decimal("0"), "returned_quantity": Decimal("0"), "sales": Decimal("0"), "returns": Decimal("0"), "cogs": Decimal("0"), "name": ""})
        invoices = []
        returned_by_sale = defaultdict(Decimal)
        for ret in returns:
            value = ret.total_refound_amount * ret.sale.exchange_rate_used
            daily[ret.return_date.date()]["returns"] += value
            returned_by_sale[ret.sale_id] += value
            for item in ret.return_items.all():
                if item.sale_item and item.sale_item.product:
                    row = products[item.sale_item.product_id]; row["name"] = item.sale_item.product.name
                    row["returned_quantity"] += Decimal(item.return_quantity); row["returns"] += Decimal(item.refund_amount or 0) * ret.sale.exchange_rate_used
        for sale in sales:
            sale_rate = Decimal(sale.exchange_rate_used or 1)
            sale_value = sale.calculated_total * sale_rate
            sale_cogs_native = sum((item.cogs_total for item in sale.sale_items.all()), Decimal("0"))
            sale_cogs = sale_cogs_native * sale_rate
            daily[sale.invoice_date]["sales"] += sale_value; daily[sale.invoice_date]["cogs"] += sale_cogs
            ratio = sale.calculated_total / sale.calculated_subtotal if sale.calculated_subtotal else Decimal("0")
            allocated = sale.customer_payment_allocations.filter(payment__status=CustomerPayment.Status.POSTED, payment__payment_date__lte=end).aggregate(total=Sum("allocated_amount"))["total"] or Decimal("0")
            invoice_return = returned_by_sale[sale.id]
            invoice_return_native = invoice_return / sale_rate
            invoices.append({"date": sale.invoice_date, "number": sale.sale_number, "customer": sale.customer.name if sale.customer else sale.general_customer_name or "Walk-in customer", "currency": sale.currency.title, "sales_type": sale.sales_type, "total": money(sale.calculated_total), "returns": money(invoice_return_native), "cogs": money(sale_cogs_native), "collected": money(allocated), "outstanding": money(max(sale.calculated_total - allocated - invoice_return_native, Decimal("0"))), "gross_profit": money(sale.calculated_total - invoice_return_native - sale_cogs_native)})
            for item in sale.sale_items.all():
                if not item.product: continue
                row = products[item.product_id]; row["name"] = item.product.name; row["quantity"] += Decimal(item.quantity)
                row["sales"] += Decimal(item.quantity) * item.sale_price * ratio * sale.exchange_rate_used
                row["cogs"] += item.cogs_total * sale.exchange_rate_used

        return Response({
            "shop": shop.name, "start_date": start, "end_date": end,
            "summary": {"gross_sales": money(total_sales), "returns": money(total_returns), "net_sales": money(net_sales), "net_cogs": money(net_cogs), "gross_profit": money(gross_profit), "gross_margin_percent": money((gross_profit / net_sales * 100) if net_sales else 0), "collected": money(collected_base), "invoice_count": len(sales)},
            "daily": [{"date": day, "sales": money(row["sales"]), "returns": money(row["returns"]), "net_sales": money(row["sales"] - row["returns"]), "cogs": money(row["cogs"]), "gross_profit": money(row["sales"] - row["returns"] - row["cogs"])} for day, row in sorted(daily.items())],
            "products": [{"product_id": key, "product": row["name"], "quantity_sold": money(row["quantity"]), "quantity_returned": money(row["returned_quantity"]), "net_sales": money(row["sales"] - row["returns"]), "cogs": money(row["cogs"]), "gross_profit": money(row["sales"] - row["returns"] - row["cogs"])} for key, row in sorted(products.items(), key=lambda item: item[1]["sales"], reverse=True)],
            "invoices": invoices,
        })
