import secrets
from django.db import models
from django.core.validators import MinValueValidator
from django.contrib.auth.hashers import make_password, check_password
from django.utils import timezone


class ShopSettings(models.Model):
    """
    Singleton — always exactly one row (pk=1).
    Stores the shop's hashed login PIN and a session token.
    """
    pin_hash      = models.CharField(max_length=256)          # hashed PIN
    pin_is_default = models.BooleanField(default=True)   # True until user changes from 1234
    token         = models.CharField(max_length=64, blank=True)  # active session token
    token_created = models.DateTimeField(null=True, blank=True)

    class Meta:
        verbose_name = 'Shop Settings'

    def __str__(self):
        return 'Shop Settings'

    # ── Convenience helpers ───────────────────────────────────────────────────

    @classmethod
    def get(cls):
        """Return the singleton, creating it with PIN=1234 if it doesn't exist."""
        obj, created = cls.objects.get_or_create(
            pk=1,
            defaults={'pin_hash': make_password('1234')},
        )
        return obj

    def verify_pin(self, raw_pin: str) -> bool:
        return check_password(raw_pin, self.pin_hash)

    def set_pin(self, raw_pin: str):
        self.pin_hash = make_password(raw_pin)
        self.pin_is_default = False
        self.save(update_fields=['pin_hash', 'pin_is_default'])

    def rotate_token(self) -> str:
        """Generate a new session token, save and return it."""
        self.token = secrets.token_hex(32)
        self.token_created = timezone.now()
        self.save(update_fields=['token', 'token_created'])
        return self.token

    def invalidate_token(self):
        self.token = ''
        self.save(update_fields=['token'])


class Product(models.Model):
    """A gas cylinder product — one record per brand + weight combination."""

    BRAND_CHOICES = [
        ('K-Gas', 'K-Gas'),
        ('Total', 'Total'),
        ('Hashi Energy', 'Hashi Energy'),
        ('Oryx', 'Oryx'),
        ('Meru', 'Meru'),
        ('Pro-Gas', 'Pro-Gas'),
        ('Afrigas', 'Afrigas'),
        ('Other', 'Other'),
    ]

    WEIGHT_CHOICES = [
        ('3kg', '3kg'),
        ('6kg', '6kg'),
        ('13kg', '13kg'),
        ('22kg', '22kg'),
        ('35kg', '35kg'),
        ('45kg', '45kg'),
        ('50kg', '50kg'),
    ]

    brand       = models.CharField(max_length=50, choices=BRAND_CHOICES)
    weight      = models.CharField(max_length=10, choices=WEIGHT_CHOICES)
    price       = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0)])
    full_qty    = models.PositiveIntegerField(default=0)
    empty_qty   = models.PositiveIntegerField(default=0)
    created_at  = models.DateTimeField(auto_now_add=True)
    updated_at  = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ('brand', 'weight')
        ordering = ['brand', 'weight']

    def __str__(self):
        return f"{self.brand} {self.weight}"

    @property
    def total_qty(self):
        return self.full_qty + self.empty_qty


class Customer(models.Model):
    """Registered customer."""

    name        = models.CharField(max_length=150)
    location    = models.CharField(max_length=200, blank=True)
    contact     = models.CharField(max_length=20, blank=True)
    created_at  = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    @property
    def outstanding_credit(self):
        return self.sales.filter(credit__gt=0).aggregate(
            total=models.Sum('credit')
        )['total'] or 0


class Sale(models.Model):
    """A sale transaction."""

    PAYMENT_CHOICES = [
        ('cash', 'Cash'),
        ('mpesa', 'M-Pesa'),
    ]

    customer            = models.ForeignKey(
                            Customer, null=True, blank=True,
                            on_delete=models.SET_NULL, related_name='sales'
                          )
    product             = models.ForeignKey(Product, on_delete=models.PROTECT, related_name='sales')
    qty                 = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)])
    amount              = models.DecimalField(max_digits=10, decimal_places=2)   # billed
    paid                = models.DecimalField(max_digits=10, decimal_places=2)   # collected so far
    credit              = models.DecimalField(max_digits=10, decimal_places=2, default=0)  # still owed
    payment_method      = models.CharField(max_length=10, choices=PAYMENT_CHOICES, default='cash')
    transaction_number  = models.CharField(max_length=50, blank=True)   # M-Pesa ref
    date                = models.DateTimeField(default=timezone.now)
    created_at          = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date']

    def __str__(self):
        customer_name = self.customer.name if self.customer else 'Walk-in'
        return f"{customer_name} — {self.product} × {self.qty} on {self.date:%Y-%m-%d}"

    def save(self, *args, **kwargs):
        # Auto-compute credit on every save
        self.credit = max(0, self.amount - self.paid)
        super().save(*args, **kwargs)


class CreditPayment(models.Model):
    """Partial or full repayment against an outstanding credit sale."""

    sale        = models.ForeignKey(Sale, on_delete=models.CASCADE, related_name='credit_payments')
    amount      = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.01)])
    date        = models.DateTimeField(default=timezone.now)
    note        = models.CharField(max_length=200, blank=True)

    class Meta:
        ordering = ['-date']

    def __str__(self):
        return f"Payment of {self.amount} on Sale #{self.sale_id}"


class Expense(models.Model):
    """A business expense."""

    CATEGORY_CHOICES = [
        ('Rent', 'Rent'),
        ('Stock / Refill', 'Stock / Refill'),
        ('Transport', 'Transport'),
        ('Salaries', 'Salaries'),
        ('Utilities', 'Utilities'),
        ('Equipment', 'Equipment'),
        ('Maintenance', 'Maintenance'),
        ('Licensing', 'Licensing'),
        ('Other', 'Other'),
    ]

    PAYMENT_CHOICES = [
        ('cash', 'Cash'),
        ('mpesa', 'M-Pesa'),
    ]

    category            = models.CharField(max_length=30, choices=CATEGORY_CHOICES)
    description         = models.CharField(max_length=300)
    amount              = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.01)])
    payment_method      = models.CharField(max_length=10, choices=PAYMENT_CHOICES, default='cash')
    transaction_number  = models.CharField(max_length=50, blank=True)
    date                = models.DateField(default=timezone.now)
    created_at          = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date', '-created_at']

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