from rest_framework import serializers

from .models import Device, LocationPoint


class LocationIngestSerializer(serializers.Serializer):
    device_id = serializers.CharField(max_length=64)
    device_name = serializers.CharField(max_length=128, required=False, allow_blank=True)
    api_key = serializers.CharField(max_length=64, required=False, allow_blank=True)
    latitude = serializers.DecimalField(
        max_digits=10, decimal_places=7, required=False, allow_null=True
    )
    longitude = serializers.DecimalField(
        max_digits=10, decimal_places=7, required=False, allow_null=True
    )
    altitude = serializers.FloatField(required=False, allow_null=True)
    speed = serializers.FloatField(required=False, allow_null=True)
    satellites = serializers.IntegerField(required=False, allow_null=True, min_value=0)
    fix_valid = serializers.BooleanField(default=False)
    chars_processed = serializers.IntegerField(required=False, allow_null=True, min_value=0)
    gps_date = serializers.DateField(required=False, allow_null=True)
    gps_time = serializers.TimeField(required=False, allow_null=True)

    def to_internal_value(self, data):
        if isinstance(data, dict):
            data = data.copy()
            raw_date = data.get("gps_date")
            if raw_date and isinstance(raw_date, str) and not self._is_usable_gps_date(raw_date):
                data["gps_date"] = None
        return super().to_internal_value(data)

    @staticmethod
    def _is_usable_gps_date(value):
        try:
            year, month, day = (int(part) for part in value.split("-"))
        except (TypeError, ValueError):
            return False
        return year >= 2020 and 1 <= month <= 12 and 1 <= day <= 31

    def validate(self, attrs):
        if attrs.get("fix_valid") and (
            attrs.get("latitude") is None or attrs.get("longitude") is None
        ):
            raise serializers.ValidationError(
                "latitude and longitude are required when fix_valid is true."
            )
        return attrs

    def create(self, validated_data):
        device_id = validated_data.pop("device_id")
        device_name = validated_data.pop("device_name", "")
        api_key = validated_data.pop("api_key", "")

        device, created = Device.objects.get_or_create(
            device_id=device_id,
            defaults={"name": device_name or device_id, "api_key": api_key},
        )

        if not created:
            if device_name and device.name != device_name:
                device.name = device_name
            if api_key and not device.api_key:
                device.api_key = api_key
            device.save(update_fields=["name", "api_key"])

        if device.api_key and api_key and device.api_key != api_key:
            raise serializers.ValidationError({"api_key": "Invalid API key for this device."})

        location = LocationPoint.objects.create(device=device, **validated_data)
        device.last_seen = location.recorded_at
        device.save(update_fields=["last_seen"])
        return location


class LocationPointSerializer(serializers.ModelSerializer):
    class Meta:
        model = LocationPoint
        fields = [
            "id",
            "latitude",
            "longitude",
            "altitude",
            "speed",
            "satellites",
            "fix_valid",
            "chars_processed",
            "gps_date",
            "gps_time",
            "recorded_at",
        ]


class DeviceSerializer(serializers.ModelSerializer):
    latest_location = LocationPointSerializer(read_only=True)
    location_count = serializers.SerializerMethodField()

    class Meta:
        model = Device
        fields = [
            "id",
            "device_id",
            "name",
            "is_active",
            "last_seen",
            "created_at",
            "latest_location",
            "location_count",
        ]

    def get_location_count(self, obj):
        return obj.locations.count()
