from collections import defaultdict
from decimal import Decimal

from django.db.models import Prefetch

from accounts.branch_utils import scope
from sales.models import Sale, SaleItem, SaleReturn, SaleReturnItem


ZERO = Decimal("0")


def sale_line_financials(company, start_date, end_date, branch=None):
    """
    Return per-sale-line financial records.

    Invoice discounts are allocated proportionally to line gross value so:
        revenue = net invoice amount
        cogs = historical buy_price * quantity
        gross profit = revenue - cogs
    """
    sales = (
        scope(Sale.objects.filter(
            company=company, date__range=[start_date, end_date]
        ), branch)
        .prefetch_related(
            Prefetch(
                "items",
                queryset=SaleItem.objects.select_related(
                    "product", "product__brand", "product__category"
                ),
            )
        )
        .order_by("date", "created_at")
    )

    rows = []
    for sale in sales:
        sale_total = Decimal(sale.total_amount or ZERO)
        net_amount = Decimal(sale.net_amount or ZERO)
        discount_ratio = (
            net_amount / sale_total if sale_total > ZERO else ZERO
        )

        for item in sale.items.all():
            qty = Decimal(item.quantity or ZERO)
            gross_line = Decimal(item.total or ZERO)
            buy_price = Decimal(item.buy_price or ZERO)
            sell_price = Decimal(item.sell_price or ZERO)

            net_line = gross_line * discount_ratio
            cost = qty * buy_price
            profit = net_line - cost

            rows.append({
                "sale": sale,
                "item": item,
                "date": sale.date,
                "product": item.product,
                "brand": item.product.brand,
                "category": item.product.category,
                "quantity": qty,
                "buy_price": buy_price,
                "sell_price": sell_price,
                "gross_revenue": gross_line,
                "net_revenue": net_line,
                "cost": cost,
                "profit": profit,
            })
    return rows


def return_financials(company, start_date, end_date, branch=None):
    """
    Return financial impact of sales returns.

    A return reduces revenue and restores the historical cost of the returned
    item. SaleReturnItem.buy_price is captured from the original SaleItem.
    """
    returns = (
        scope(SaleReturn.objects.filter(
            company=company, date__range=[start_date, end_date]
        ), branch, field="sale__branch")
        .prefetch_related(
            Prefetch(
                "items",
                queryset=SaleReturnItem.objects.select_related(
                    "product", "product__brand", "product__category"
                ),
            )
        )
    )

    rows = []
    for ret in returns:
        for item in ret.items.all():
            qty = Decimal(item.quantity or ZERO)
            refund = Decimal(item.total or ZERO)
            buy_price = Decimal(item.buy_price or ZERO)
            restored_cost = qty * buy_price
            rows.append({
                "return": ret,
                "item": item,
                "date": ret.date,
                "product": item.product,
                "brand": item.product.brand,
                "category": item.product.category,
                "quantity": qty,
                "buy_price": buy_price,
                "sell_price": Decimal(item.sell_price or ZERO),
                "revenue": -refund,
                "cost": -restored_cost,
                "profit": -(refund - restored_cost),
            })
    return rows


def financial_summary(company, start_date, end_date, branch=None):
    """Revenue / COGS / gross profit for a period. ``branch=None`` = all branches."""
    sales_rows = sale_line_financials(company, start_date, end_date, branch)
    return_rows = return_financials(company, start_date, end_date, branch)

    revenue = sum((r["net_revenue"] for r in sales_rows), ZERO)
    cogs = sum((r["cost"] for r in sales_rows), ZERO)
    gross_profit = sum((r["profit"] for r in sales_rows), ZERO)

    return_revenue = sum((r["revenue"] for r in return_rows), ZERO)
    return_cogs = sum((r["cost"] for r in return_rows), ZERO)
    return_profit = sum((r["profit"] for r in return_rows), ZERO)

    revenue += return_revenue
    cogs += return_cogs
    gross_profit += return_profit

    return {
        "revenue": revenue,
        "cogs": cogs,
        "gross_profit": gross_profit,
        "sales_count": scope(Sale.objects.filter(
            company=company, date__range=[start_date, end_date]
        ), branch).count(),
        "return_revenue": return_revenue,
        "return_cogs": return_cogs,
        "return_profit": return_profit,
    }


def group_profit(rows, key_func):
    grouped = defaultdict(lambda: {
        "quantity": ZERO,
        "revenue": ZERO,
        "cost": ZERO,
        "profit": ZERO,
        "buy_value": ZERO,
        "sell_value": ZERO,
    })

    for row in rows:
        key = key_func(row)
        g = grouped[key]
        g["quantity"] += row["quantity"]
        g["revenue"] += row.get("net_revenue", row.get("revenue", ZERO))
        g["cost"] += row["cost"]
        g["profit"] += row["profit"]
        g["buy_value"] += row["buy_price"] * row["quantity"]
        g["sell_value"] += row["sell_price"] * row["quantity"]

    result = []
    for key, g in grouped.items():
        qty = g["quantity"]
        g["key"] = key
        g["avg_buy_price"] = g["buy_value"] / qty if qty else ZERO
        g["avg_sell_price"] = g["sell_value"] / qty if qty else ZERO
        result.append(g)

    result.sort(key=lambda x: x["revenue"], reverse=True)
    return result
