from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from django.db.models import Q

from utils.audit import AuditModel


class Zafra(AuditModel):
    nombre = models.CharField(max_length=120, unique=True)
    fecha_inicio = models.DateField()
    fecha_fin = models.DateField()
    activa = models.BooleanField(default=False)
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="zafras",
        null=True,
        blank=True,
    )
    precio_pactado_por_tonelada = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        null=True,
        blank=True,
    )
    codigo_sap = models.CharField(max_length=60, null=True, blank=True)

    class Meta:
        permissions = [
            ("create", "Puede crear catálogos"),
            ("edit", "Puede editar catálogos"),
            ("delete", "Puede eliminar catálogos"),
        ]

    def clean(self):
        if self.fecha_inicio and self.fecha_fin and self.fecha_inicio > self.fecha_fin:
            raise ValidationError({"fecha_fin": "La fecha_fin debe ser mayor o igual a fecha_inicio."})

        if self.fecha_inicio and self.fecha_fin:
            qs = Zafra.objects.all()
            if self.pk:
                qs = qs.exclude(pk=self.pk)
            if self.oficina_id is None:
                qs = qs.filter(oficina__isnull=True)
            else:
                qs = qs.filter(oficina_id=self.oficina_id)
            overlap = qs.filter(
                Q(fecha_inicio__lte=self.fecha_fin) & Q(fecha_fin__gte=self.fecha_inicio)
            ).exists()
            if overlap:
                raise ValidationError(
                    "Existe una zafra con fechas que se solapan con el rango especificado."
                )

    def __str__(self):
        return self.nombre


class Variedad(AuditModel):
    nombre = models.CharField(max_length=120, unique=True)
    descripcion = models.TextField(blank=True)
    activa = models.BooleanField(default=True)
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="variedades",
        null=True,
        blank=True,
    )

    def __str__(self):
        return self.nombre


_rfc_validator = RegexValidator(
    regex=r"^([A-ZÑ&]{3,4})\d{6}([A-Z0-9]{3})$",
    message="RFC inválido.",
)


class Productor(AuditModel):
    nombre = models.CharField(max_length=180)
    rfc = models.CharField(max_length=13, unique=True, validators=[_rfc_validator])
    codigo_sica = models.CharField(max_length=40, unique=True, null=True, blank=True)
    email = models.EmailField(blank=True)
    telefono = models.CharField(max_length=50, blank=True)
    direccion = models.TextField(blank=True)
    activo = models.BooleanField(default=True)
    oficinas = models.ManyToManyField(
        "Oficina",
        through="ProductorOficina",
        related_name="productores",
        blank=True,
    )

    def clean(self):
        if self.codigo_sica == "":
            self.codigo_sica = None

    def __str__(self):
        return f"{self.nombre} ({self.rfc})"


class Contrato(AuditModel):
    numero_contrato = models.CharField(max_length=60)
    productor = models.ForeignKey(Productor, on_delete=models.PROTECT, related_name="contratos")
    zafra = models.ForeignKey(Zafra, on_delete=models.PROTECT, related_name="contratos")
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="contratos",
        null=True,
        blank=True,
    )
    activo = models.BooleanField(default=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["zafra", "numero_contrato"],
                name="uq_contrato_numero_por_zafra",
            )
        ]

    def __str__(self):
        return self.numero_contrato

    def clean(self):
        if self.zafra_id and self.oficina_id and self.zafra.oficina_id and self.zafra.oficina_id != self.oficina_id:
            raise ValidationError({"oficina": "La zafra no pertenece a la oficina indicada."})


class ContratoVariedad(AuditModel):
    contrato = models.ForeignKey(
        Contrato,
        on_delete=models.CASCADE,
        related_name="variedades",
    )
    variedad = models.ForeignKey(
        Variedad,
        on_delete=models.PROTECT,
        related_name="contratos_variedad",
    )
    peso_teorico = models.DecimalField(max_digits=12, decimal_places=3, null=True, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["contrato", "variedad"],
                name="uq_contrato_variedad",
            )
        ]

    def __str__(self):
        return f"{self.contrato} - {self.variedad}"

    def clean(self):
        contrato_oficina_id = getattr(self.contrato, "oficina_id", None)
        variedad_oficina_id = getattr(self.variedad, "oficina_id", None)
        if (
            contrato_oficina_id is not None
            and variedad_oficina_id is not None
            and contrato_oficina_id != variedad_oficina_id
        ):
            raise ValidationError({"variedad": "La variedad no pertenece a la oficina del contrato."})


class Campo(AuditModel):
    nombre = models.CharField(max_length=120)
    importe_apoyo = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name="Importe de apoyo ($)",
        help_text="Importe de apoyo que otorga el molino para flete por tonelada.",
    )
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="campos",
        null=True,
        blank=True,
    )

    def __str__(self):
        return self.nombre


class Linea(AuditModel):
    codigo = models.CharField(max_length=40, unique=True)
    nombre = models.CharField(max_length=120)
    correo = models.EmailField(blank=True)
    id_sap = models.CharField(max_length=60, blank=True, verbose_name="ID SAP")
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="lineas",
        null=True,
        blank=True,
    )

    class Meta:
        verbose_name = "Línea"
        verbose_name_plural = "Líneas"

    def __str__(self):
        return f"{self.codigo} - {self.nombre}"


class Bodega(AuditModel):
    codigo = models.CharField(max_length=40, unique=True)
    nombre = models.CharField(max_length=120)
    ubicacion = models.CharField(max_length=255, blank=True)
    activa = models.BooleanField(default=True)
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="bodegas",
        null=True,
        blank=True,
    )
    codigo_sap = models.CharField(max_length=60, blank=True)
    es_rechazo = models.BooleanField(default=False, verbose_name="Es bodega de rechazo")
    es_externo = models.BooleanField(default=False, verbose_name="Es bodega externa")

    class Meta:
        permissions = [
            ("manage_bodegas", "Puede administrar bodegas"),
        ]

    def __str__(self):
        return f"{self.codigo} - {self.nombre}"


class Bascula(AuditModel):
    codigo = models.CharField(max_length=60, unique=True)
    modelo = models.CharField(max_length=120, blank=True)
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="basculas",
        null=True,
        blank=True,
    )

    def __str__(self):
        return self.codigo


class Parametro(AuditModel):
    nombre = models.CharField(max_length=120, unique=True)
    unidad = models.CharField(max_length=40, blank=True)
    umbral_min = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True)
    umbral_max = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True)
    activo = models.BooleanField(default=True)

    class Meta:
        permissions = [
            ("manage_calidad_catalogos", "Puede administrar catálogos de calidad (parámetros y castigos)"),
        ]

    def clean(self):
        if (
            self.umbral_min is not None
            and self.umbral_max is not None
            and self.umbral_min > self.umbral_max
        ):
            raise ValidationError({"umbral_max": "El umbral_max debe ser mayor o igual a umbral_min."})

    def __str__(self):
        return self.nombre


class Castigo(AuditModel):
    parametro = models.ForeignKey(Parametro, on_delete=models.PROTECT, related_name="castigos")
    limite_superior = models.DecimalField(max_digits=10, decimal_places=3)
    descontar_kg_por_tonelada = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    bonificar_por_tonelada = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    es_rechazo = models.BooleanField(default=False)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["parametro", "limite_superior"],
                name="uq_castigo_limite_por_parametro",
            ),
            models.CheckConstraint(
                check=models.Q(limite_superior__gte=0),
                name="ck_castigo_limite_superior_gte_0",
            ),
            models.CheckConstraint(
                check=models.Q(descontar_kg_por_tonelada__gte=0),
                name="ck_castigo_descontar_kg_por_tonelada_gte_0",
            ),
            models.CheckConstraint(
                check=models.Q(bonificar_por_tonelada__gte=0),
                name="ck_castigo_bonificar_por_tonelada_gte_0",
            ),
        ]

    def clean(self):
        if self.limite_superior is not None and self.limite_superior < 0:
            raise ValidationError({"limite_superior": "El limite_superior debe ser mayor o igual a 0."})
        if self.descontar_kg_por_tonelada is not None and self.descontar_kg_por_tonelada < 0:
            raise ValidationError(
                {"descontar_kg_por_tonelada": "El descontar_kg_por_tonelada debe ser mayor o igual a 0."}
            )
        if self.bonificar_por_tonelada is not None and self.bonificar_por_tonelada < 0:
            raise ValidationError({"bonificar_por_tonelada": "El bonificar_por_tonelada debe ser mayor o igual a 0."})

    def __str__(self):
        return f"{self.parametro} (<= {self.limite_superior})"


class ProductorOficina(AuditModel):
    productor = models.ForeignKey(
        Productor,
        on_delete=models.PROTECT,
        related_name="productor_oficinas",
    )
    oficina = models.ForeignKey(
        "Oficina",
        on_delete=models.PROTECT,
        related_name="productor_oficinas",
    )
    codigo_sap = models.CharField(max_length=60, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["productor", "oficina"],
                name="uq_productor_oficina",
            )
        ]

    def __str__(self):
        return f"ProductorOficina(productor={self.productor_id}, oficina={self.oficina_id})"


class Oficina(AuditModel):
    codigo = models.CharField(max_length=40, unique=True)
    nombre = models.CharField(max_length=120)
    ubicacion = models.CharField(max_length=255, blank=True)
    activa = models.BooleanField(default=True)
    id_sap = models.CharField(max_length=60, blank=True, null=True)
    sap_url = models.URLField(blank=True, null=True)
    sap_companydb = models.CharField(max_length=120, blank=True, null=True)
    sap_usuario = models.CharField(max_length=120, blank=True, null=True)
    sap_contrasena = models.CharField(max_length=255, blank=True, null=True)

    def __str__(self):
        return f"{self.codigo} - {self.nombre}"
