"""
Promo codes for colleges, universities, and partner institutes.
"""
from decimal import Decimal, ROUND_HALF_UP

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone


DISCOUNT_PERCENT = 'percent'
DISCOUNT_FIXED = 'fixed'
DISCOUNT_TYPE_CHOICES = [
    (DISCOUNT_PERCENT, 'Percentage (%)'),
    (DISCOUNT_FIXED, 'Fixed amount'),
]

APPLIES_COURSES = 'courses'
APPLIES_CERTS = 'certifications'
APPLIES_BOTH = 'both'
APPLIES_TO_CHOICES = [
    (APPLIES_COURSES, 'Courses only'),
    (APPLIES_CERTS, 'Certifications only'),
    (APPLIES_BOTH, 'Courses & certifications'),
]


class PromoCode(models.Model):
    """
    Special discount code for partner colleges / universities / institutes.
    Students enter the code at checkout to reduce course or certification fees.
    """
    code = models.CharField(
        max_length=40,
        unique=True,
        db_index=True,
        help_text='Students enter this code (e.g. NUST2026). Stored uppercase.',
    )
    title = models.CharField(max_length=200, help_text='Internal label, e.g. NUST Spring Offer')
    institute_name = models.CharField(
        max_length=200,
        help_text='College, university, or institute this promo is for.',
    )
    description = models.TextField(blank=True)
    discount_type = models.CharField(
        max_length=20,
        choices=DISCOUNT_TYPE_CHOICES,
        default=DISCOUNT_PERCENT,
    )
    discount_value = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        help_text='Percent (e.g. 20) or fixed currency amount.',
    )
    applies_to = models.CharField(
        max_length=20,
        choices=APPLIES_TO_CHOICES,
        default=APPLIES_BOTH,
    )
    max_uses = models.PositiveIntegerField(
        default=0,
        help_text='Total redemptions allowed. 0 = unlimited.',
    )
    max_uses_per_user = models.PositiveIntegerField(
        default=1,
        help_text='How many times one student can use this code. 0 = unlimited.',
    )
    used_count = models.PositiveIntegerField(default=0)
    min_amount = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        default=0,
        help_text='Minimum order amount before discount applies.',
    )
    valid_from = models.DateTimeField(null=True, blank=True)
    valid_until = models.DateTimeField(null=True, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_promo_codes',
    )

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return f'{self.code} — {self.institute_name}'

    def save(self, *args, **kwargs):
        self.code = (self.code or '').strip().upper().replace(' ', '')
        if self.discount_value is not None and self.discount_value < 0:
            self.discount_value = Decimal('0')
        super().save(*args, **kwargs)

    def clean(self):
        if self.discount_type == DISCOUNT_PERCENT and self.discount_value > 100:
            raise ValidationError({'discount_value': 'Percentage cannot exceed 100.'})

    @property
    def is_currently_valid(self):
        if not self.is_active:
            return False
        now = timezone.now()
        if self.valid_from and now < self.valid_from:
            return False
        if self.valid_until and now > self.valid_until:
            return False
        if self.max_uses and self.used_count >= self.max_uses:
            return False
        return True

    def applies_to_scope(self, scope: str) -> bool:
        if self.applies_to == APPLIES_BOTH:
            return True
        return self.applies_to == scope

    def calculate_discount(self, amount) -> tuple[Decimal, Decimal]:
        """Return (discount_amount, final_amount) for a base amount."""
        base = Decimal(amount or 0)
        if base <= 0:
            return Decimal('0.00'), Decimal('0.00')
        if base < (self.min_amount or 0):
            return Decimal('0.00'), base.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

        if self.discount_type == DISCOUNT_PERCENT:
            discount = (base * Decimal(self.discount_value) / Decimal('100')).quantize(
                Decimal('0.01'), rounding=ROUND_HALF_UP
            )
        else:
            discount = Decimal(self.discount_value).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

        discount = min(discount, base)
        final = (base - discount).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        return discount, final

    def discount_label(self):
        if self.discount_type == DISCOUNT_PERCENT:
            return f'{self.discount_value}% off'
        return f'{self.discount_value} off'


class PromoRedemption(models.Model):
    """Audit log of promo usage."""
    promo = models.ForeignKey(PromoCode, on_delete=models.CASCADE, related_name='redemptions')
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='promo_redemptions',
    )
    scope = models.CharField(max_length=20)  # courses | certifications
    enrollment = models.ForeignKey(
        'enrollments.Enrollment',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='promo_redemptions',
    )
    certification_application = models.ForeignKey(
        'certifications.CertificationApplication',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='promo_redemptions',
    )
    original_amount = models.DecimalField(max_digits=8, decimal_places=2)
    discount_amount = models.DecimalField(max_digits=8, decimal_places=2)
    final_amount = models.DecimalField(max_digits=8, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return f'{self.promo.code} by {self.user_id} (−{self.discount_amount})'
