from decimal import Decimal

from django.conf import settings
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone


class UserRole(models.Model):
    class Role(models.TextChoices):
        ADMIN = "admin", "Admin"
        WAITER = "waiter", "Mesero"
        KITCHEN = "kitchen", "Cocina"

    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="role_profile")
    role = models.CharField(max_length=20, choices=Role.choices, default=Role.WAITER)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["user__username"]
        verbose_name = "Rol de usuario"
        verbose_name_plural = "Roles de usuario"

    def __str__(self):
        return f"{self.user.username} · {self.get_role_display()}"

    @classmethod
    def resolve_for(cls, user):
        if not user or not user.is_authenticated:
            return None
        if user.is_superuser:
            return cls.Role.ADMIN
        profile, _ = cls.objects.get_or_create(user=user, defaults={"role": cls.Role.WAITER})
        return profile.role

    @classmethod
    def label_for(cls, user):
        role = cls.resolve_for(user)
        return dict(cls.Role.choices).get(role, "Sin rol") if role else "Invitado"


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def ensure_user_role(sender, instance, created, **kwargs):
    if created and not instance.is_superuser:
        UserRole.objects.get_or_create(user=instance, defaults={"role": UserRole.Role.WAITER})


class Table(models.Model):
    class Status(models.TextChoices):
        FREE = "free", "Libre"
        OCCUPIED = "occupied", "Ocupada"

    name = models.CharField(max_length=40, unique=True)
    seats = models.PositiveSmallIntegerField(default=4)
    status = models.CharField(max_length=12, choices=Status.choices, default=Status.FREE)
    area = models.CharField(max_length=50, blank=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]

    def __str__(self):
        return self.name

    @property
    def current_order(self):
        cached_orders = getattr(self, "open_orders_cache", None)
        if cached_orders is not None:
            return cached_orders[0] if cached_orders else None
        return self.orders.exclude(status=Order.Status.PAID).order_by("-created_at").first()


class Product(models.Model):
    name = models.CharField(max_length=120)
    category = models.CharField(max_length=80)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    is_available = models.BooleanField(default=True)
    station = models.CharField(max_length=80, default="Cocina")

    class Meta:
        ordering = ["category", "name"]

    def __str__(self):
        return self.name


class Order(models.Model):
    class Status(models.TextChoices):
        OPEN = "open", "Abierta"
        PREPARING = "preparing", "En preparación"
        READY = "ready", "Lista"
        PAID = "paid", "Pagada"

    table = models.ForeignKey(Table, on_delete=models.PROTECT, related_name="orders")
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.OPEN)
    guest_name = models.CharField(max_length=120, blank=True)
    server_note = models.CharField(max_length=200, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    sent_to_kitchen_at = models.DateTimeField(null=True, blank=True)
    paid_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"Orden #{self.pk} · {self.table.name}"

    @property
    def subtotal(self):
        if hasattr(self, "subtotal_value") and self.subtotal_value is not None:
            return self.subtotal_value
        total = sum((item.line_total for item in self.items.select_related("product").all()), Decimal("0.00"))
        return total.quantize(Decimal("0.01"))

    @property
    def total_items(self):
        if hasattr(self, "items_count") and self.items_count is not None:
            return self.items_count
        return sum(item.quantity for item in self.items.all())

    def allowed_transitions(self):
        transitions = {
            self.Status.OPEN: [self.Status.PREPARING],
            self.Status.PREPARING: [self.Status.READY],
            self.Status.READY: [self.Status.PAID],
            self.Status.PAID: [],
        }
        return transitions.get(self.status, [])

    def advance_to(self, new_status):
        if new_status not in self.allowed_transitions():
            raise ValueError("Invalid status transition")

        now = timezone.now()
        self.status = new_status
        if new_status == self.Status.PREPARING and not self.sent_to_kitchen_at:
            self.sent_to_kitchen_at = now
        if new_status == self.Status.PAID:
            self.paid_at = now
            self.table.status = Table.Status.FREE
            self.table.save(update_fields=["status", "updated_at"])
        self.save(update_fields=["status", "sent_to_kitchen_at", "paid_at", "updated_at"])


class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items")
    product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="order_items")
    quantity = models.PositiveIntegerField(default=1)
    note = models.CharField(max_length=200, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["created_at", "id"]

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

    @property
    def line_total(self):
        return (self.product.price * self.quantity).quantize(Decimal("0.01"))
