from decimal import Decimal

from django.conf import settings
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import serializers

from apps.catalogos.models import Parametro
from apps.muestreo_laboratorio.models import (
    FotoMuestra,
    FotoResultado,
    ParametroResultado,
    ResultadoLaboratorio,
    TomaMuestra,
)
from apps.muestreo_laboratorio.utils import compute_fuera_de_rango, compute_tags_and_alertas
from apps.recepciones.models import Muestreo, Recepcion
from apps.recepciones.serializers import BoletaLiteSerializer, RecepcionSerializer


def _choices_help_text(choices):
    first = choices[0] if choices else None
    if isinstance(first, (list, tuple)) and len(first) == 2:
        return "Valores permitidos: " + ", ".join([f"{v} ({label})" for v, label in choices])
    return "Valores permitidos: " + ", ".join([str(v) for v in choices])


class FotoMuestraSerializer(serializers.ModelSerializer):
    imagen_url = serializers.SerializerMethodField()

    class Meta:
        model = FotoMuestra
        fields = ["id", "tipo", "imagen_url", "created_at"]
        extra_kwargs = {
            "tipo": {"help_text": _choices_help_text(FotoMuestra.TIPO_CHOICES)},
        }

    def get_imagen_url(self, obj):
        request = self.context.get("request")
        url = reverse("muestreo-laboratorio-foto-muestra-download", kwargs={"pk": obj.pk})
        if request:
            return request.build_absolute_uri(url)
        return url


class FotoMuestraCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = FotoMuestra
        fields = ["tipo", "imagen"]
        extra_kwargs = {
            "tipo": {"help_text": _choices_help_text(FotoMuestra.TIPO_CHOICES)},
        }

    def validate_imagen(self, f):
        max_size = getattr(settings, "MUESTREO_LAB_MAX_FOTO_MB", 5) * 1024 * 1024
        if f.size > max_size:
            raise serializers.ValidationError("La imagen excede el tamaño máximo permitido.")
        name = (getattr(f, "name", "") or "").lower()
        if not (name.endswith(".jpg") or name.endswith(".jpeg") or name.endswith(".png")):
            raise serializers.ValidationError("Extensión inválida. Usa jpg o png.")
        return f


class TomaMuestraSerializer(serializers.ModelSerializer):
    muestreo_id = serializers.IntegerField(read_only=True)
    fotos = FotoMuestraSerializer(many=True, read_only=True)

    class Meta:
        model = TomaMuestra
        fields = [
            "id",
            "muestreo_id",
            "fecha_toma",
            "latitud",
            "longitud",
            "submuestras",
            "observaciones",
            "fotos",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]
        read_only_fields = [
            "id",
            "muestreo_id",
            "fotos",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]


class TomaMuestraUpsertSerializer(serializers.ModelSerializer):
    class Meta:
        model = TomaMuestra
        fields = ["fecha_toma", "latitud", "longitud", "submuestras", "observaciones"]


class UserLiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ["id", "username", "first_name", "last_name"]


class FotoResultadoSerializer(serializers.ModelSerializer):
    imagen_url = serializers.SerializerMethodField()

    class Meta:
        model = FotoResultado
        fields = ["id", "tipo", "imagen_url", "created_at"]
        extra_kwargs = {
            "tipo": {"help_text": _choices_help_text(FotoResultado.TIPO_CHOICES)},
        }

    def get_imagen_url(self, obj):
        request = self.context.get("request")
        url = reverse("muestreo-laboratorio-foto-resultado-download", kwargs={"pk": obj.pk})
        if request:
            return request.build_absolute_uri(url)
        return url


class FotoResultadoCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = FotoResultado
        fields = ["tipo", "imagen"]
        extra_kwargs = {
            "tipo": {"help_text": _choices_help_text(FotoResultado.TIPO_CHOICES)},
        }

    def validate_imagen(self, f):
        max_size = getattr(settings, "MUESTREO_LAB_MAX_FOTO_MB", 5) * 1024 * 1024
        if f.size > max_size:
            raise serializers.ValidationError("La imagen excede el tamaño máximo permitido.")
        name = (getattr(f, "name", "") or "").lower()
        if not (name.endswith(".jpg") or name.endswith(".jpeg") or name.endswith(".png")):
            raise serializers.ValidationError("Extensión inválida. Usa jpg o png.")
        return f


class ParametroResultadoSerializer(serializers.ModelSerializer):
    parametro_nombre = serializers.CharField(source="parametro.nombre", read_only=True)

    class Meta:
        model = ParametroResultado
        fields = [
            "id",
            "parametro",
            "parametro_nombre",
            "valor",
            "unidad",
            "observacion",
            "fuera_de_rango",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]
        read_only_fields = [
            "id",
            "fuera_de_rango",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]


class ParametroResultadoWriteSerializer(serializers.ModelSerializer):
    class Meta:
        model = ParametroResultado
        fields = ["parametro", "valor", "observacion"]

    def validate_parametro(self, v: Parametro):
        if not v.activo:
            raise serializers.ValidationError("El parámetro no está activo.")
        return v

    def validate_valor(self, v):
        if v is None:
            return v
        return Decimal(v)

    def _apply_defaults(self, attrs, *, instance=None):
        parametro = attrs.get("parametro") or getattr(instance, "parametro", None)
        if parametro is not None:
            attrs["unidad"] = getattr(parametro, "unidad", "") or ""

        valor = attrs.get("valor")
        if parametro and valor is not None:
            attrs["fuera_de_rango"] = compute_fuera_de_rango(
                umbral_min=parametro.umbral_min,
                umbral_max=parametro.umbral_max,
                valor=valor,
            )
        return attrs

    def create(self, validated_data):
        validated_data = self._apply_defaults(validated_data)
        obj = super().create(validated_data)
        self._recompute_result(obj.resultado_laboratorio)
        return obj

    def update(self, instance, validated_data):
        validated_data = self._apply_defaults(validated_data, instance=instance)
        obj = super().update(instance, validated_data)
        self._recompute_result(obj.resultado_laboratorio)
        return obj

    def _recompute_result(self, resultado: ResultadoLaboratorio):
        tags, alertas = compute_tags_and_alertas(resultado.parametros.select_related("parametro").all())
        ResultadoLaboratorio.objects.filter(pk=resultado.pk).update(tags=tags, alertas=alertas)


class ResultadoLaboratorioSerializer(serializers.ModelSerializer):
    muestreo_id = serializers.IntegerField(read_only=True)
    parametros = ParametroResultadoSerializer(many=True, read_only=True)
    fotos = FotoResultadoSerializer(many=True, read_only=True)
    informe_url = serializers.SerializerMethodField()

    class Meta:
        model = ResultadoLaboratorio
        fields = [
            "id",
            "muestreo_id",
            "estado",
            "informe_url",
            "tags",
            "alertas",
            "observaciones",
            "fecha_publicacion",
            "publicado_por",
            "parametros",
            "fotos",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]
        read_only_fields = [
            "id",
            "muestreo_id",
            "informe_url",
            "tags",
            "alertas",
            "fecha_publicacion",
            "publicado_por",
            "parametros",
            "fotos",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]
        extra_kwargs = {
            "estado": {"help_text": _choices_help_text(ResultadoLaboratorio.ESTADO_CHOICES)},
        }

    def get_informe_url(self, obj):
        if not obj.informe:
            return None
        request = self.context.get("request")
        url = reverse("muestreo-laboratorio-informe-download", kwargs={"pk": obj.pk})
        if request:
            return request.build_absolute_uri(url)
        return url


class ResultadoLaboratorioCreateSerializer(serializers.ModelSerializer):
    muestreo_id = serializers.IntegerField(write_only=True)

    class Meta:
        model = ResultadoLaboratorio
        fields = ["muestreo_id", "observaciones"]

    def create(self, validated_data):
        muestreo_id = validated_data.pop("muestreo_id")
        return ResultadoLaboratorio.objects.create(muestreo_id=muestreo_id, **validated_data)


class ResultadoLaboratorioUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = ResultadoLaboratorio
        fields = ["observaciones", "estado"]
        extra_kwargs = {
            "estado": {"help_text": _choices_help_text(ResultadoLaboratorio.ESTADO_CHOICES)},
        }


class RecepcionLiteForOrdenMuestreoSerializer(serializers.ModelSerializer):
    boleta = BoletaLiteSerializer(read_only=True)
    bodega_destino_nombre = serializers.CharField(source="bodega_destino.nombre", read_only=True)

    class Meta:
        model = Recepcion
        fields = [
            "id",
            "boleta",
            "bodega_destino",
            "bodega_destino_nombre",
            "estado",
            "created_at",
            "updated_at",
        ]
        read_only_fields = fields


class OrdenMuestreoListSerializer(serializers.ModelSerializer):
    tecnico_id = serializers.IntegerField(source="tecnico_asignado_id", read_only=True)
    tecnico_nombre = serializers.SerializerMethodField()
    recepcion = RecepcionLiteForOrdenMuestreoSerializer(read_only=True)

    def get_tecnico_nombre(self, obj):
        tecnico = getattr(obj, "tecnico_asignado", None)
        if not tecnico:
            return None
        full_name = (tecnico.get_full_name() or "").strip() if hasattr(tecnico, "get_full_name") else ""
        return full_name or getattr(tecnico, "username", "") or str(tecnico)

    class Meta:
        model = Muestreo
        fields = [
            "id",
            "tipo_muestreo",
            "tecnico_id",
            "tecnico_nombre",
            "estado",
            "fecha_asignacion",
            "created_at",
            "updated_at",
            "recepcion",
        ]
        read_only_fields = fields


class OrdenMuestreoDetailSerializer(serializers.ModelSerializer):
    tecnico_id = serializers.IntegerField(source="tecnico_asignado_id", read_only=True)
    tecnico = UserLiteSerializer(source="tecnico_asignado", read_only=True)
    tecnico_nombre = serializers.SerializerMethodField()
    recepcion = RecepcionSerializer(read_only=True)
    toma_muestra = TomaMuestraSerializer(read_only=True)
    resultado_laboratorio = ResultadoLaboratorioSerializer(read_only=True)

    def get_tecnico_nombre(self, obj):
        tecnico = getattr(obj, "tecnico_asignado", None)
        if not tecnico:
            return None
        full_name = (tecnico.get_full_name() or "").strip() if hasattr(tecnico, "get_full_name") else ""
        return full_name or getattr(tecnico, "username", "") or str(tecnico)

    class Meta:
        model = Muestreo
        fields = [
            "id",
            "tipo_muestreo",
            "tecnico_id",
            "tecnico",
            "tecnico_nombre",
            "estado",
            "fecha_asignacion",
            "created_at",
            "updated_at",
            "recepcion",
            "toma_muestra",
            "resultado_laboratorio",
        ]
        read_only_fields = fields


class AdjuntarInformeSerializer(serializers.Serializer):
    informe = serializers.FileField()

    def validate_informe(self, f):
        max_size = getattr(settings, "MUESTREO_LAB_MAX_INFORME_MB", 10) * 1024 * 1024
        if f.size > max_size:
            raise serializers.ValidationError("El informe excede el tamaño máximo permitido.")
        name = (getattr(f, "name", "") or "").lower()
        if not name.endswith(".pdf"):
            raise serializers.ValidationError("Extensión inválida. Usa PDF.")
        return f
