import secrets
import time

from django.db import models


def uuid7() -> str:
    """Generate a UUID v7 (time-ordered UUID)."""
    nanoseconds = time.time_ns()
    uuid_int = (nanoseconds << 16) | secrets.randbits(48)
    return f"{uuid_int:032x}"


def to_letters(n: int) -> str:
    """1->A, 2->B, ... 26->Z, 27->AA (bijective base-26)."""
    out = ""
    while n > 0:
        n, rem = divmod(n - 1, 26)
        out = chr(65 + rem) + out
    return out


class Currency(models.Model):
    """A currency selectable across finance forms (funds, budgets)."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    code = models.CharField(max_length=10, unique=True, help_text="ISO-ish code, e.g. USD, IDR.")
    abbreviation = models.CharField(max_length=10, help_text="Display abbreviation, e.g. $USD, Rp.")
    name = models.CharField(max_length=100, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "currencies"
        ordering = ["code"]
        verbose_name_plural = "currencies"

    def __str__(self):
        return self.code


class BankAccount(models.Model):
    """An org bank account for receiving money. Multiple accounts, typically one
    (or more) per currency. Shown on invoices and used for incoming funding."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    label = models.CharField(max_length=100, blank=True, help_text="Optional nickname, e.g. 'IDR Operations'.")
    bank_name = models.CharField(max_length=150, help_text="e.g. Bank Mandiri.")
    account_number = models.CharField(max_length=50)
    account_holder = models.CharField(max_length=150, blank=True)
    branch = models.CharField(max_length=150, blank=True)
    swift_code = models.CharField(max_length=20, blank=True, help_text="For international transfers.")
    currency = models.CharField(max_length=10, default="IDR", db_index=True, help_text="Currency this account receives.")
    is_default = models.BooleanField(default=False, help_text="Default for its currency.")
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "bank_accounts"
        ordering = ["currency", "-is_default", "bank_name"]
        indexes = [models.Index(fields=["currency", "is_default"])]

    def __str__(self):
        return f"{self.bank_name} {self.account_number} ({self.currency})"


class TaxRate(models.Model):
    """A named tax rate (percentage) selectable on invoices, e.g. PPN 11%."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=100, help_text="Display name, e.g. PPN, VAT, PPh 23.")
    rate = models.DecimalField(max_digits=6, decimal_places=3, help_text="Percentage, e.g. 11 for 11%.")
    is_default = models.BooleanField(default=False, help_text="Pre-selected on new invoices.")
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "tax_rates"
        ordering = ["-is_default", "name"]

    def __str__(self):
        return f"{self.name} ({self.rate}%)"


class BudgetCategory(models.Model):
    """Org-wide budget category grouping budget items."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    code = models.CharField(max_length=20, unique=True, blank=True)
    name = models.CharField(max_length=255)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "budget_categories"
        ordering = ["name"]
        verbose_name_plural = "budget categories"

    def __str__(self):
        return self.name


class Invoice(models.Model):
    """Receivable invoice we issue to a donor (they pay us).

    Distinct from procurements.Invoice (vendor invoices we receive). Optionally
    tied to a project and/or event for context. The donor is the billed party.
    `total` is recomputed from line items (sum of amounts) plus `tax` on save.
    """
    STATUS_CHOICES = [
        ("draft", "Draft"),
        ("issued", "Issued"),
        ("sent", "Sent"),
        ("paid", "Paid"),
        ("overdue", "Overdue"),
        ("cancelled", "Cancelled"),
    ]

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    invoice_number = models.CharField(max_length=50, unique=True, blank=True)
    donor = models.ForeignKey(
        "companies.Company", on_delete=models.PROTECT, related_name="receivable_invoices",
        help_text="The billed party (donor / sponsor).",
    )
    project = models.ForeignKey(
        "projects.Project", on_delete=models.SET_NULL, null=True, blank=True,
        related_name="receivable_invoices",
    )
    event = models.ForeignKey(
        "events.Event", on_delete=models.SET_NULL, null=True, blank=True,
        related_name="receivable_invoices",
    )
    currency = models.CharField(max_length=10, default="IDR")
    subtotal = models.DecimalField(max_digits=14, decimal_places=2, default=0, help_text="Sum of line amounts. Computed.")
    tax = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    total = models.DecimalField(max_digits=14, decimal_places=2, default=0, help_text="subtotal + tax. Computed.")
    issue_date = models.DateField(null=True, blank=True)
    due_date = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="draft")
    paid_at = models.DateTimeField(null=True, blank=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "finance_invoices"
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["status"]),
            models.Index(fields=["donor"]),
            models.Index(fields=["project"]),
        ]

    def recompute_totals(self):
        from decimal import Decimal
        self.subtotal = sum((li.amount for li in self.lines.all()), Decimal("0"))
        self.total = (self.subtotal or Decimal("0")) + (self.tax or Decimal("0"))

    def __str__(self):
        return self.invoice_number or f"INV-{self.id[:8]}"


class InvoiceLine(models.Model):
    """A billed line on a receivable Invoice. amount = quantity × unit_price."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name="lines")
    description = models.CharField(max_length=255)
    quantity = models.DecimalField(max_digits=12, decimal_places=2, default=1)
    unit_price = models.DecimalField(max_digits=14, decimal_places=2, default=0)
    amount = models.DecimalField(max_digits=14, decimal_places=2, default=0, help_text="quantity × unit_price. Computed.")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "finance_invoice_lines"
        ordering = ["created_at"]

    def save(self, *args, **kwargs):
        self.amount = (self.quantity or 0) * (self.unit_price or 0)
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.description} — {self.amount}"


class BudgetItem(models.Model):
    """A line item under a budget category."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    code = models.CharField(max_length=30, blank=True)
    category = models.ForeignKey(BudgetCategory, on_delete=models.CASCADE, related_name="items")
    parent = models.ForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children",
        help_text="Optional parent item — nests this line under another (code becomes <parent>.<n>).",
    )
    name = models.CharField(max_length=255)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "budget_items"
        ordering = ["-created_at"]
        indexes = [models.Index(fields=["category"]), models.Index(fields=["parent"])]

    def __str__(self):
        return self.name
