"""
gamification/models.py
=======================
Modelos del sistema de gamificación.

Componentes:
  - Temporada: agrupa sesiones de una unidad/módulo
  - Puntos: acumulado del alumno por sección/temporada
  - Insignia: catálogo de logros disponibles
  - InsigniaAlumno: logros desbloqueados por alumno
  - Ficha: moneda interna (500 pts = 1 ficha)
  - BeneficioFicha: beneficios definidos por el docente
  - Canje: historial de canjes de fichas

El motor de gamificación (gamification/engine.py) es el único que
debe llamar a los métodos de estos modelos. Las vistas no deben
modificar puntos o insignias directamente.
"""

from django.db import models
from django.conf import settings
from django.core.validators import MinValueValidator

from shared.mixins import TimestampedModel
from academic.models import Seccion


class Temporada(TimestampedModel):
    """
    Agrupa sesiones en una unidad temática con su propio ranking.
    Al cerrar la temporada, el podio se congela y se pueden asignar premios.
    """

    class Estado(models.TextChoices):
        ACTIVA  = 'activa',  'Activa'
        CERRADA = 'cerrada', 'Cerrada'

    seccion = models.ForeignKey(
        Seccion, on_delete=models.CASCADE, related_name='temporadas'
    )
    nombre  = models.CharField(max_length=100, verbose_name='Nombre')
    estado  = models.CharField(max_length=10, choices=Estado.choices, default=Estado.ACTIVA)
    inicio  = models.DateField()
    cierre  = models.DateField(null=True, blank=True)

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Temporada'
        db_table     = 'gamification_temporada'

    def __str__(self):
        return f'{self.nombre} ({self.seccion})'


class Puntos(TimestampedModel):
    """
    Puntos acumulados de un alumno en una sección/temporada.
    Un registro por (alumno, seccion, temporada).
    
    Los puntos se acumulan por sesión y se convierten en fichas
    automáticamente cuando cruzan el umbral (500 pts = 1 ficha).
    """

    alumno    = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='puntos'
    )
    seccion   = models.ForeignKey(Seccion, on_delete=models.CASCADE, related_name='puntos')
    temporada = models.ForeignKey(
        Temporada, null=True, blank=True, on_delete=models.SET_NULL, related_name='puntos'
    )

    total_puntos = models.PositiveIntegerField(default=0)
    total_fichas = models.PositiveSmallIntegerField(default=0)
    racha_sesiones = models.PositiveSmallIntegerField(
        default=0,
        verbose_name='Racha de sesiones consecutivas'
    )
    ultima_sesion_fecha = models.DateField(null=True, blank=True)

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Puntos'
        db_table     = 'gamification_puntos'
        unique_together = [['alumno', 'seccion', 'temporada']]

    def __str__(self):
        return f'{self.alumno_id}: {self.total_puntos} pts en {self.seccion}'

    def agregar_puntos(self, cantidad: int):
        """
        Agrega puntos y convierte a fichas si supera el umbral.
        Umbral: 500 pts = 1 ficha (configurable en settings).
        """
        UMBRAL = 500
        puntos_antes = self.total_puntos
        self.total_puntos += cantidad

        # Calcular fichas ganadas (puede ser más de 1 si sube mucho)
        fichas_antes  = puntos_antes  // UMBRAL
        fichas_ahora  = self.total_puntos // UMBRAL
        fichas_nuevas = fichas_ahora - fichas_antes

        if fichas_nuevas > 0:
            self.total_fichas += fichas_nuevas

        self.save(update_fields=['total_puntos', 'total_fichas'])
        return fichas_nuevas


class Insignia(TimestampedModel):
    """
    Catálogo de insignias/logros disponibles en la plataforma.
    Se carga desde fixtures (gamification/fixtures/insignias.json).
    """

    class Categoria(models.TextChoices):
        PARTICIPACION = 'participacion', 'Participación'
        VELOCIDAD     = 'velocidad',     'Velocidad'
        CONOCIMIENTO  = 'conocimiento',  'Conocimiento'
        SOCIAL        = 'social',        'Social'
        RACHA         = 'racha',         'Rachas'
        ESPECIAL      = 'especial',      'Especiales'

    class Nivel(models.IntegerChoices):
        COMUN  = 1, 'Común'
        RARO   = 2, 'Raro'
        EPICO  = 3, 'Épico'

    codigo      = models.CharField(max_length=50, unique=True, verbose_name='Código único')
    categoria   = models.CharField(max_length=15, choices=Categoria.choices)
    emoji       = models.CharField(max_length=10)
    nombre      = models.CharField(max_length=100)
    descripcion = models.TextField()
    criterio    = models.CharField(max_length=200, verbose_name='Criterio legible')
    nivel       = models.PositiveSmallIntegerField(choices=Nivel.choices, default=Nivel.COMUN)

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Insignia'
        db_table     = 'gamification_insignia'
        ordering     = ['categoria', 'nivel', 'nombre']

    def __str__(self):
        return f'{self.emoji} {self.nombre} [{self.codigo}]'


class InsigniaAlumno(TimestampedModel):
    """
    Registro de cada insignia desbloqueada por un alumno en una sección.
    """

    alumno   = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='insignias'
    )
    insignia = models.ForeignKey(Insignia, on_delete=models.CASCADE, related_name='alumnos')
    seccion  = models.ForeignKey(Seccion, on_delete=models.CASCADE, related_name='insignias')

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Insignia de alumno'
        db_table     = 'gamification_insigniaalumno'
        unique_together = [['alumno', 'insignia', 'seccion']]

    def __str__(self):
        return f'{self.alumno_id} desbloqueó {self.insignia.nombre}'


class BeneficioFicha(TimestampedModel):
    """
    Beneficio canjeable con fichas, definido por el docente.
    Cada docente configura los beneficios de sus secciones.
    """

    seccion     = models.ForeignKey(Seccion, on_delete=models.CASCADE, related_name='beneficios')
    nombre      = models.CharField(max_length=200)
    descripcion = models.TextField(blank=True)
    costo_fichas = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(1)],
        verbose_name='Costo en fichas'
    )
    disponible  = models.BooleanField(default=True)

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Beneficio de ficha'
        db_table     = 'gamification_beneficioficha'

    def __str__(self):
        return f'{self.nombre} ({self.costo_fichas} fichas)'


class Canje(TimestampedModel):
    """
    Historial de canjes de fichas por beneficios.
    El docente aprueba o rechaza cada solicitud.
    """

    class Estado(models.TextChoices):
        PENDIENTE = 'pendiente', 'Pendiente de aprobación'
        APROBADO  = 'aprobado',  'Aprobado'
        RECHAZADO = 'rechazado', 'Rechazado'

    alumno    = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='canjes'
    )
    beneficio = models.ForeignKey(BeneficioFicha, on_delete=models.CASCADE, related_name='canjes')
    seccion   = models.ForeignKey(Seccion, on_delete=models.CASCADE, related_name='canjes')
    estado    = models.CharField(max_length=10, choices=Estado.choices, default=Estado.PENDIENTE)
    fichas_usadas = models.PositiveSmallIntegerField()
    aprobado_por  = models.ForeignKey(
        settings.AUTH_USER_MODEL, null=True, blank=True,
        on_delete=models.SET_NULL, related_name='canjes_aprobados'
    )
    aprobado_en   = models.DateTimeField(null=True, blank=True)

    class Meta(TimestampedModel.Meta):
        verbose_name = 'Canje'
        db_table     = 'gamification_canje'

    def __str__(self):
        return f'Canje de {self.alumno_id}: {self.beneficio.nombre} [{self.estado}]'
