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


# ── Shop auth ─────────────────────────────────────────────────────────────────

class ShopSettings(models.Model):
    pin_hash       = models.CharField(max_length=256)
    pin_is_default = models.BooleanField(default=True)
    token          = models.CharField(max_length=64, blank=True)
    token_created  = models.DateTimeField(null=True, blank=True)

    class Meta:
        verbose_name = 'Shop Settings'

    def __str__(self):
        return 'Shop Settings'

    @classmethod
    def get(cls):
        obj, _ = cls.objects.get_or_create(
            pk=1,
            defaults={'pin_hash': make_password('1234'), 'pin_is_default': True},
        )
        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:
        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'])


# ── Brands ────────────────────────────────────────────────────────────────────

PREDEFINED_BRANDS = ['K-Gas', 'Total', 'Hashi Energy', 'Oryx', 'Meru', 'Pro-Gas', 'Afrigas']


class Brand(models.Model):
    name          = models.CharField(max_length=100, unique=True)
    is_predefined = models.BooleanField(default=False)
    created_at    = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    @classmethod
    def ensure_predefined(cls):
        """Create the built-in brands if they don't exist yet."""
        for name in PREDEFINED_BRANDS:
            cls.objects.get_or_create(name=name, defaults={'is_predefined': True})


# ── Products ──────────────────────────────────────────────────────────────────

WEIGHT_CHOICES = [
    ('3kg', '3 kg'), ('6kg', '6 kg'), ('13kg', '13 kg'),
    ('22kg', '22 kg'), ('35kg', '35 kg'), ('45kg', '45 kg'), ('50kg', '50 kg'),
]


class Product(models.Model):
    brand      = models.ForeignKey(Brand, on_delete=models.PROTECT, related_name='products')
    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)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

    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


# ── Customers ─────────────────────────────────────────────────────────────────

class Customer(models.Model):
    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


# ── Sales ─────────────────────────────────────────────────────────────────────

class Sale(models.Model):
    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)
    paid               = models.DecimalField(max_digits=10, decimal_places=2)
    credit             = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    payment_method     = models.CharField(max_length=10, choices=PAYMENT_CHOICES, default='cash')
    transaction_number = models.CharField(max_length=50, blank=True)
    date               = models.DateTimeField(default=timezone.now)
    created_at         = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date']

    def __str__(self):
        name = self.customer.name if self.customer else 'Walk-in'
        return f"{name} — {self.product} × {self.qty} on {self.date:%Y-%m-%d}"

    def save(self, *args, **kwargs):
        self.credit = max(0, self.amount - self.paid)
        super().save(*args, **kwargs)


class CreditPayment(models.Model):
    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']


# ── Expenses ──────────────────────────────────────────────────────────────────

class Expense(models.Model):
    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')]
    STATE_CHOICES   = [('full', 'Full'), ('empty', 'Empty')]
    REFILL_TYPE_CHOICES = [('new', 'New'), ('refill', 'Refill')]
    AVAILABLE_BRANDS = Brand.objects.values_list('name', flat=True)

    # Common fields
    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)

    # Stock / Refill — only populated when category == 'Stock / Refill'
    refill_brand    = models.ForeignKey(Brand, null=True, blank=True, on_delete=models.SET_NULL, related_name='expenses')
    refill_weight   = models.CharField(max_length=10, choices=WEIGHT_CHOICES, blank=True)
    refill_quantity = models.PositiveIntegerField(null=True, blank=True)
    refill_state    = models.CharField(max_length=5, choices=STATE_CHOICES, blank=True)
    refill_type = models.CharField(
        max_length=10, choices=REFILL_TYPE_CHOICES,
        blank=True, default='new',
        help_text="'new' = fresh cylinders; 'refill' = empties returned full"
    )

    class Meta:
        ordering = ['-date', '-created_at']

    def __str__(self):
        return f"{self.category} — {self.description} ({self.amount})"

    @property
    def is_refill(self):
        return self.category == 'Stock / Refill'