from __future__ import annotations

from decimal import Decimal
from typing import Optional

from django.utils import timezone

from ..models import (
    PickupOrder,
    PriceList,
    PriceListItem,
    WeighTicket,
    Invoice,
    InvoiceLine,
    Payout,
)


def _get_price(price_list: PriceList, material_id: int, unit: str, direction: str) -> Optional[Decimal]:
    item = (
        PriceListItem.objects.filter(
            price_list=price_list, material_id=material_id, unit=unit, direction=direction
        )
        .order_by("id")
        .first()
    )
    return item.unit_price if item else None


def generate_invoice_for_pickup(pickup: PickupOrder) -> Invoice:
    if getattr(pickup, "weigh_ticket", None) is None:
        raise ValueError("Pickup has no weigh ticket")

    # If invoice already exists and not void, return it (idempotent)
    existing = pickup.invoices.filter(status__in=[Invoice.STATUS_DRAFT, Invoice.STATUS_ISSUED, Invoice.STATUS_PAID]).first()
    if existing:
        return existing

    price_list = pickup.customer.price_list
    if not price_list:
        # Fallback to any org pricelist
        price_list = PriceList.objects.filter(organization=pickup.organization).order_by("id").first()
    if not price_list:
        raise ValueError("No price list found for customer or organization")

    inv = Invoice.objects.create(
        organization=pickup.organization,
        customer=pickup.customer,
        pickup=pickup,
        currency_code=price_list.currency_code,
        status=Invoice.STATUS_DRAFT,
    )

    total = Decimal("0.00")
    payout_total = Decimal("0.00")

    for line in pickup.weigh_ticket.lines.all():
        sell_price = _get_price(price_list, line.material_id, line.unit, PriceListItem.DIRECTION_SELL)
        buy_price = _get_price(price_list, line.material_id, line.unit, PriceListItem.DIRECTION_BUY)

        if sell_price is not None and sell_price > 0:
            line_total = (Decimal(line.quantity) * Decimal(sell_price)).quantize(Decimal("0.01"))
            InvoiceLine.objects.create(
                invoice=inv,
                description=f"{line.material.name} ({line.unit})",
                material=line.material,
                quantity=line.quantity,
                unit=line.unit,
                unit_price=sell_price,
                line_total=line_total,
            )
            total += line_total

        if buy_price is not None and buy_price > 0:
            payout_total += (Decimal(line.quantity) * Decimal(buy_price)).quantize(Decimal("0.01"))

    inv.total_amount = total
    inv.issued_at = timezone.now()
    inv.status = Invoice.STATUS_ISSUED if total > 0 else Invoice.STATUS_DRAFT
    inv.due_at = inv.issued_at + timezone.timedelta(days=14)
    inv.save()

    if payout_total > 0:
        Payout.objects.create(
            organization=pickup.organization,
            customer=pickup.customer,
            pickup=pickup,
            amount=payout_total,
            currency_code=price_list.currency_code,
            paid_at=timezone.now(),
            reference=f"PAYOUT-{pickup.id}",
        )

    # Update pickup status
    pickup.status = PickupOrder.STATUS_INVOICED
    pickup.save(update_fields=["status"]) 

    return inv

