from django.db import models
from django.conf import settings
from django.utils import timezone
import secrets
import time


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}"


_ROMAN_MONTHS = {
    1: "I", 2: "II", 3: "III", 4: "IV", 5: "V", 6: "VI",
    7: "VII", 8: "VIII", 9: "IX", 10: "X", 11: "XI", 12: "XII",
}


class Asset(models.Model):
    """Asset inventory."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=255)
    asset_tag = models.CharField(max_length=100, unique=True)
    category = models.ForeignKey("AssetCategory", on_delete=models.SET_NULL, null=True, related_name="assets")
    serial_number = models.CharField(max_length=100, blank=True)
    status = models.CharField(max_length=20, choices=[
        ("available", "Available"),
        ("assigned", "Assigned"),
        ("maintenance", "Maintenance"),
        ("retired", "Retired"),
        ("lost", "Lost"),
    ], default="available")
    location = models.ForeignKey("AssetLocation", on_delete=models.SET_NULL, null=True, blank=True, related_name="assets")
    vendor = models.ForeignKey("Vendor", on_delete=models.SET_NULL, null=True, blank=True, related_name="assets")
    purchase_order = models.ForeignKey(
        "procurements.PurchaseOrder",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assets",
    )
    goods_receipt = models.ForeignKey(
        "procurements.GoodsReceipt",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assets",
    )
    purchase_date = models.DateField(null=True, blank=True)
    purchase_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    warranty_expiry = models.DateField(null=True, blank=True)
    description = models.TextField(blank=True)
    image = models.ImageField(upload_to="assets/", blank=True, null=True)
    specifications = models.JSONField(default=dict)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "assets"
        ordering = ["asset_tag"]

    def __str__(self):
        return f"{self.asset_tag} - {self.name}"


class AssetCategory(models.Model):
    """Asset categories."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    slug = models.SlugField(max_length=100, unique=True, blank=True, null=True)
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField(blank=True)
    depreciation_rate = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)

    class Meta:
        db_table = "asset_categories"
        ordering = ["name"]
        verbose_name_plural = "Asset Categories"

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug and self.name:
            self.slug = self.name.lower().replace(" ", "-")
        super().save(*args, **kwargs)


class AssetLocation(models.Model):
    """Physical locations for assets."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
    name = models.CharField(max_length=255)
    address = models.TextField(blank=True)
    contact_person = models.CharField(max_length=100, blank=True)
    contact_phone = models.CharField(max_length=20, blank=True)

    class Meta:
        db_table = "asset_locations"
        ordering = ["name"]

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug and self.name:
            self.slug = self.name.lower().replace(" ", "-")
        super().save(*args, **kwargs)


class AssetAssignment(models.Model):
    """Asset assignment to employees."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    asset = models.ForeignKey(Asset, on_delete=models.CASCADE, related_name="assignments")
    employee = models.ForeignKey("hr.Employee", on_delete=models.CASCADE, related_name="asset_assignments")
    assigned_date = models.DateField()
    returned_date = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=20, choices=[
        ("assigned", "Assigned"),
        ("returned", "Returned"),
    ], default="assigned")
    condition = models.CharField(max_length=100, blank=True)
    notes = models.TextField(blank=True)

    class Meta:
        db_table = "asset_assignments"
        ordering = ["-assigned_date"]


class Maintenance(models.Model):
    """Asset maintenance records."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    asset = models.ForeignKey(Asset, on_delete=models.CASCADE, related_name="maintenance_records")
    title = models.CharField(max_length=255)
    description = models.TextField()
    status = models.CharField(max_length=20, choices=[
        ("scheduled", "Scheduled"),
        ("in_progress", "In Progress"),
        ("completed", "Completed"),
        ("cancelled", "Cancelled"),
    ], default="scheduled")
    scheduled_date = models.DateField()
    completed_date = models.DateField(null=True, blank=True)
    cost = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    vendor = models.CharField(max_length=255, blank=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "maintenance"
        ordering = ["-scheduled_date"]


class Procurement(models.Model):
    """Standalone asset-procurement request tracker.

    Distinct from the full procurements app flow (Requisition → Approval →
    PurchaseOrder → GoodsReceipt → Asset). This is the lightweight single-record
    "I need to buy this asset" tracker served at /api/procurements/ and used by
    the asset-management/procurements page. Do not merge or delete — it backs a
    live screen and has its own number sequence and status enum.
    """
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    number = models.CharField(max_length=40, unique=True, blank=True)
    title = models.CharField(max_length=255)
    description = models.TextField(blank=True)
    status = models.CharField(max_length=20, choices=[
        ("draft", "Draft"),
        ("pending_approval", "Pending Approval"),
        ("approved", "Approved"),
        ("ordered", "Ordered"),
        ("received", "Received"),
        ("cancelled", "Cancelled"),
    ], default="draft")
    requested_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="procurements")
    category = models.CharField(max_length=100, blank=True)
    quantity = models.IntegerField(default=1)
    estimated_cost = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    actual_cost = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    vendor = models.ForeignKey("Vendor", on_delete=models.SET_NULL, null=True, blank=True, related_name="procurements")
    items = models.JSONField(default=list, blank=True)
    delivery_date = models.DateField(null=True, blank=True)
    # Set when this lite tracker is promoted into the full procurement chain.
    # One-way link: a promoted Procurement points at the Requisition it spawned.
    requisition = models.ForeignKey(
        "procurements.Requisition",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="source_procurements",
    )
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "procurements"
        ordering = ["-created_at"]

    def save(self, *args, **kwargs):
        if not self.number:
            now = timezone.now()
            month = _ROMAN_MONTHS[now.month]
            year = now.year
            # Derive from the highest existing sequence, not count() — gaps from
            # deleted rows would otherwise reuse a taken number and collide.
            suffix = f"/{month}/{year}"
            existing = Procurement.objects.filter(number__endswith=suffix).values_list("number", flat=True)
            max_seq = 0
            for num in existing:
                try:
                    max_seq = max(max_seq, int(num.split("-")[1].split("/")[0]))
                except (IndexError, ValueError):
                    continue
            self.number = f"P-{max_seq + 1:04d}{suffix}"
        super().save(*args, **kwargs)


class AssetTicket(models.Model):
    """Support tickets for assets."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    title = models.CharField(max_length=255)
    description = models.TextField(blank=True)
    priority = models.CharField(max_length=20, choices=[
        ("low", "Low"),
        ("medium", "Medium"),
        ("high", "High"),
        ("critical", "Critical"),
    ], default="medium")
    status = models.CharField(max_length=20, choices=[
        ("open", "Open"),
        ("in_progress", "In Progress"),
        ("resolved", "Resolved"),
        ("closed", "Closed"),
    ], default="open")
    asset = models.ForeignKey(Asset, on_delete=models.SET_NULL, null=True, blank=True, related_name="tickets")
    reported_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="asset_reported_tickets")
    assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="asset_assigned_tickets")
    resolved_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 = "asset_tickets"
        ordering = ["-created_at"]

    def __str__(self):
        return self.title


class Vendor(models.Model):
    """Vendor/supplier records."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=255)
    contact_person = models.CharField(max_length=100, blank=True)
    email = models.EmailField(blank=True)
    phone = models.CharField(max_length=20, blank=True)
    address = models.TextField(blank=True)
    tax_id = models.CharField(max_length=50, blank=True)
    bank_details = models.JSONField(default=dict)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "vendors"
        ordering = ["name"]

    def __str__(self):
        return self.name

class AssetDocument(models.Model):
    """Documents attached to an asset — manual, warranty/guarantee card, invoice copy, etc."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    asset = models.ForeignKey(Asset, on_delete=models.CASCADE, related_name="documents")
    title = models.CharField(max_length=255)
    doc_type = models.CharField(max_length=20, choices=[
        ("manual", "Manual"),
        ("warranty", "Guarantee / Warranty Card"),
        ("invoice", "Invoice"),
        ("receipt", "Receipt"),
        ("other", "Other"),
    ], default="other")
    file = models.FileField(upload_to="asset_documents/")
    notes = models.TextField(blank=True)
    uploaded_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True,
        related_name="uploaded_asset_documents",
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "asset_documents"
        ordering = ["-created_at"]

    def __str__(self):
        return self.title


# ──────────────────────────────────────────────────────────────────────────
# Non-asset procured items — things you buy and hold but that aren't durable
# inventory assets: licenses/subscriptions, consumable stock, and rentals.
# Each can be sourced from a procurement PurchaseOrder / GoodsReceipt, mirroring
# the Asset FKs so a goods receipt can be split into the right record type.
# ──────────────────────────────────────────────────────────────────────────


class License(models.Model):
    """Software licenses & subscriptions (M365, support contracts, SaaS seats)."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=255)
    vendor = models.ForeignKey("Vendor", on_delete=models.SET_NULL, null=True, blank=True, related_name="licenses")
    license_key = models.CharField(max_length=255, blank=True)
    seats = models.PositiveIntegerField(default=1)
    cost_per_period = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    period = models.CharField(max_length=20, choices=[
        ("monthly", "Monthly"),
        ("quarterly", "Quarterly"),
        ("annual", "Annual"),
        ("perpetual", "Perpetual"),
    ], default="annual")
    start_date = models.DateField(null=True, blank=True)
    renewal_date = models.DateField(null=True, blank=True, db_index=True)
    auto_renew = models.BooleanField(default=False)
    status = models.CharField(max_length=20, choices=[
        ("active", "Active"),
        ("expiring", "Expiring"),
        ("expired", "Expired"),
        ("cancelled", "Cancelled"),
    ], default="active")
    purchase_order = models.ForeignKey(
        "procurements.PurchaseOrder", on_delete=models.SET_NULL, null=True, blank=True, related_name="licenses",
    )
    goods_receipt = models.ForeignKey(
        "procurements.GoodsReceipt", on_delete=models.SET_NULL, null=True, blank=True, related_name="licenses",
    )
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "licenses"
        ordering = ["renewal_date", "name"]
        indexes = [models.Index(fields=["status", "renewal_date"])]

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


class Rental(models.Model):
    """Rented equipment held for a fixed term — return tracked, not owned."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=255)
    vendor = models.ForeignKey("Vendor", on_delete=models.SET_NULL, null=True, blank=True, related_name="rentals")
    quantity = models.PositiveIntegerField(default=1)
    location = models.ForeignKey("AssetLocation", on_delete=models.SET_NULL, null=True, blank=True, related_name="rentals")
    rental_cost = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    deposit = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    start_date = models.DateField(null=True, blank=True)
    return_date = models.DateField(null=True, blank=True, db_index=True)
    returned_at = models.DateField(null=True, blank=True)
    returned_condition = models.CharField(max_length=20, choices=[
        ("good", "Good"),
        ("damaged", "Damaged"),
        ("lost", "Lost"),
    ], blank=True)
    status = models.CharField(max_length=20, choices=[
        ("active", "Active"),
        ("overdue", "Overdue"),
        ("returned", "Returned"),
    ], default="active")
    purchase_order = models.ForeignKey(
        "procurements.PurchaseOrder", on_delete=models.SET_NULL, null=True, blank=True, related_name="rentals",
    )
    goods_receipt = models.ForeignKey(
        "procurements.GoodsReceipt", on_delete=models.SET_NULL, null=True, blank=True, related_name="rentals",
    )
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "rentals"
        ordering = ["return_date", "name"]
        indexes = [models.Index(fields=["status", "return_date"])]

    def __str__(self):
        return f"{self.name} x{self.quantity}"


class StockItem(models.Model):
    """A consumable catalog item (paper, toner, pantry). Stock held per location."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=255)
    sku = models.CharField(max_length=100, unique=True, blank=True)
    category = models.ForeignKey("AssetCategory", on_delete=models.SET_NULL, null=True, blank=True, related_name="stock_items")
    unit = models.CharField(max_length=30, default="unit")
    reorder_point = models.PositiveIntegerField(default=0)
    unit_cost = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    vendor = models.ForeignKey("Vendor", on_delete=models.SET_NULL, null=True, blank=True, related_name="stock_items")
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "stock_items"
        ordering = ["name"]

    def __str__(self):
        return self.name

    @property
    def total_quantity(self):
        return self.levels.aggregate(t=models.Sum("quantity"))["t"] or 0


class StockLevel(models.Model):
    """On-hand quantity of a StockItem at one location. Derived from movements."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    item = models.ForeignKey(StockItem, on_delete=models.CASCADE, related_name="levels")
    location = models.ForeignKey("AssetLocation", on_delete=models.CASCADE, related_name="stock_levels")
    quantity = models.IntegerField(default=0)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "stock_levels"
        ordering = ["item__name"]
        constraints = [
            models.UniqueConstraint(fields=["item", "location"], name="uniq_stock_item_location"),
        ]

    def __str__(self):
        return f"{self.item.name} @ {self.location_id}: {self.quantity}"


class StockMovement(models.Model):
    """Immutable stock ledger entry. Each in/out/adjust/transfer mutates a StockLevel."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    item = models.ForeignKey(StockItem, on_delete=models.CASCADE, related_name="movements")
    location = models.ForeignKey("AssetLocation", on_delete=models.CASCADE, related_name="stock_movements")
    movement_type = models.CharField(max_length=20, choices=[
        ("in", "Stock In"),
        ("out", "Stock Out"),
        ("adjust", "Adjustment"),
        ("transfer", "Transfer"),
    ])
    quantity = models.IntegerField(help_text="Positive for in/transfer-in, negative for out.")
    reason = models.CharField(max_length=255, blank=True)
    goods_receipt = models.ForeignKey(
        "procurements.GoodsReceipt", on_delete=models.SET_NULL, null=True, blank=True, related_name="stock_movements",
    )
    performed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="stock_movements")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "stock_movements"
        ordering = ["-created_at"]
        indexes = [models.Index(fields=["item", "location", "-created_at"])]

    def __str__(self):
        return f"{self.movement_type} {self.quantity} {self.item.name}"

    def apply(self):
        """Upsert the matching StockLevel by this movement's signed quantity."""
        level, _ = StockLevel.objects.get_or_create(item=self.item, location=self.location)
        level.quantity = (level.quantity or 0) + self.quantity
        level.save(update_fields=["quantity", "updated_at"])
        return level
