from datetime import date, timedelta
from django.utils import timezone
from django.db.models import Sum, Q

from .models import Invoice
from tenants.models import TenantProfile


# ================= GENERATE INVOICES =================

def generate_monthly_invoices():
    """
    PRODUCTION BILLING LOGIC

    ✔ First invoice = RENT + DEPOSIT
    ✔ Subsequent = RENT only
    ✔ Due date = 2nd of month
    ✔ Visible before due (handled in UI)
    ✔ No duplicates per month
    """

    today = timezone.now().date()
    month_start = today.replace(day=1)

    # ✅ RENT DUE DATE (2nd of month)
    due_date = month_start.replace(day=2)

    created_count = 0
    skipped_count = 0

    tenants = TenantProfile.objects.filter(
        unit__isnull=False
    ).select_related("user", "unit")

    for profile in tenants:
        user = profile.user
        unit = profile.unit

        if not user or not unit:
            skipped_count += 1
            continue

        # ✅ CHECK IF TENANT HAS EVER BEEN BILLED
        has_any_invoice = Invoice.objects.filter(
            tenant=user,
            unit=unit
        ).exists()

        # ✅ PREVENT DUPLICATE MONTHLY BILLING
        already_billed_this_month = Invoice.objects.filter(
            tenant=user,
            unit=unit,
            created_at__year=today.year,
            created_at__month=today.month
        ).exists()

        if already_billed_this_month:
            skipped_count += 1
            continue

        # ✅ FIRST PAYMENT = RENT + DEPOSIT
        if not has_any_invoice:
            amount = unit.rent_amount + (unit.deposit or 0)
        else:
            amount = unit.rent_amount

        Invoice.objects.create(
            tenant=user,
            unit=unit,
            amount=amount,
            due_date=due_date,
            status='pending'
        )

        created_count += 1

    return f"Invoices created: {created_count}, Skipped: {skipped_count}"

# ================= CREDIT BALANCE =================
def get_tenant_credit(tenant):
    """
    Returns total available credit for a tenant.
    Credit is stored as NEGATIVE invoices.
    """
    credit = Invoice.objects.filter(
        tenant=tenant,
        amount__lt=0
    ).aggregate(total=Sum('amount'))['total'] or 0

    return abs(credit)  # convert to positive value


# ================= APPLY PAYMENT =================
def apply_payment(payment):
    """
    Apply payment with FULL accounting support:

    ✔ Pays oldest invoices first
    ✔ Supports partial payments
    ✔ Supports overpayments → stored as CREDIT
    ✔ Uses credit automatically before charging new invoices
    ✔ NEVER mutates invoice.amount
    """

    tenant = payment.tenant
    remaining = payment.amount

    # ================= STEP 1: APPLY EXISTING CREDIT =================
    credit_invoices = Invoice.objects.filter(
        tenant=tenant,
        amount__lt=0  # credit entries
    ).order_by('created_at')

    for credit in credit_invoices:
        if remaining <= 0:
            break

        credit_value = abs(credit.amount)

        if credit_value <= remaining:
            # fully consume credit
            remaining -= credit_value
            credit.delete()
        else:
            # partially consume credit
            credit.amount += remaining  # since it's negative
            credit.save()
            remaining = 0

    # ================= STEP 2: APPLY TO UNPAID INVOICES =================
    invoices = Invoice.objects.filter(
        tenant=tenant,
        status__in=['pending', 'partial', 'overdue']
    ).order_by('due_date')

    for invoice in invoices:
        if remaining <= 0:
            break

        balance = invoice.balance

        if balance <= 0:
            continue

        if remaining >= balance:
            # fully clear invoice
            remaining -= balance
        else:
            # partial payment
            remaining = 0

        invoice.update_status()

    # ================= STEP 3: STORE OVERPAYMENT AS CREDIT =================
    if remaining > 0:
        Invoice.objects.create(
            tenant=tenant,
            unit=None,
            amount=-remaining,  # NEGATIVE = CREDIT
            due_date=timezone.now().date(),
            status='paid'
        )