from __future__ import annotations

from django.db import transaction
from django.db.models import Max
from rest_framework.exceptions import PermissionDenied, ValidationError

from bookings.models import Booking
from invoicing.models import Invoice, InvoiceLineItem, Receipt
from structures.models import Structure


def get_invoice_number_components(structure: Structure) -> tuple[int, str]:
    Structure.objects.select_for_update().get(pk=structure.pk)
    next_sequence = (
        Invoice.objects.filter(structure=structure).aggregate(max_sequence=Max("sequence"))[
            "max_sequence"
        ]
        or 0
    ) + 1
    prefix = (structure.invoice_prefix or "INV").strip() or "INV"
    return next_sequence, f"{prefix}-{next_sequence:05d}"


@transaction.atomic
def create_invoice_for_reservation(*, reservation_id: int, user, extra_details: dict | None = None) -> Invoice:
    booking = (
        Booking.objects.select_for_update()
        .select_related("structure")
        .filter(pk=reservation_id)
        .first()
    )
    if booking is None:
        raise ValidationError({"reservation_id": "Reservation not found."})

    if booking.structure.user_id != user.id:
        raise PermissionDenied("You do not have access to this reservation.")

    if not booking.structure.invoicing_enabled:
        raise ValidationError("Invoicing is disabled for this structure.")

    receipt = (
        Receipt.objects.select_for_update()
        .select_related("structure", "reservation")
        .prefetch_related("line_items")
        .filter(reservation=booking)
        .first()
    )
    if receipt is None:
        raise ValidationError("Receipt does not exist for this reservation.")

    if Invoice.objects.filter(reservation=booking).exists():
        raise ValidationError("An invoice already exists for this reservation.")

    sequence, number = get_invoice_number_components(booking.structure)
    invoice = Invoice.objects.create(
        structure=booking.structure,
        reservation=booking,
        receipt=receipt,
        number=number,
        sequence=sequence,
        total_gross=receipt.total_gross,
        total_net=receipt.total_net,
        total_vat=receipt.total_vat,
        city_tax_amount=receipt.city_tax_amount,
        extra_details=extra_details or {},
    )

    InvoiceLineItem.objects.bulk_create(
        [
            InvoiceLineItem(
                invoice=invoice,
                line_type=line_item.line_type,
                description=line_item.description,
                vat_rate=line_item.vat_rate,
                gross_amount=line_item.gross_amount,
                net_amount=line_item.net_amount,
                vat_amount=line_item.vat_amount,
            )
            for line_item in receipt.line_items.all()
        ]
    )
    receipt.is_finalized = True
    receipt.save(update_fields=["is_finalized", "updated_at"])
    return invoice
