from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import ComPortReading, User, Report
from vehicles.models import Vehicle, VehicleExtra
from nomenclatures.models import Nomenclature, NomenclatureField, NomenclatureEntry


class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name',
                  'role', 'is_admin', 'is_active', 'date_joined', 'password',
                  'can_measure', 'can_manually_measure', 'can_manage_entities', 'can_edit_documents']
        read_only_fields = ['id', 'date_joined']
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        user = User(**validated_data)
        if password:
            user.set_password(password)
        user.save()
        return user

    def update(self, instance, validated_data):
        password = validated_data.pop('password', None)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        if password:
            instance.set_password(password)
        instance.save()
        return instance


class UserDetailSerializer(serializers.ModelSerializer):
    """Serializer for current user details (excludes password)"""
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name',
                  'role', 'is_admin', 'is_active', 'date_joined',
                  'can_measure', 'can_manually_measure', 'can_manage_entities', 'can_edit_documents']
        read_only_fields = ['id', 'date_joined']


class ChangePasswordSerializer(serializers.Serializer):
    """Serializer for password change endpoint"""
    old_password = serializers.CharField(required=True, write_only=True)
    new_password = serializers.CharField(required=True, write_only=True)

    def validate_old_password(self, value):
        user = self.context['request'].user
        if not user.check_password(value):
            raise serializers.ValidationError("Old password is incorrect")
        return value

    def validate_new_password(self, value):
        # Use Django's password validators
        validate_password(value, self.context['request'].user)
        return value

    def save(self, **kwargs):
        user = self.context['request'].user
        user.set_password(self.validated_data['new_password'])
        user.save()
        return user


class ComPortReadingSerializer(serializers.ModelSerializer):
    class Meta:
        model = ComPortReading
        fields = ['id', 'port', 'data', 'timestamp', 'source_ip']
        read_only_fields = ['id', 'timestamp']


class VehicleExtraSerializer(serializers.ModelSerializer):
    class Meta:
        model = VehicleExtra
        fields = ['id', 'data']
        read_only_fields = ['id']

    def validate_data(self, value):
        if not value:
            return value

        nomenclatures = {
            n.code: n for n in
            Nomenclature.objects.filter(applies_to='vehicle').prefetch_related('fields', 'entries')
        }

        errors = {}
        for key, val in value.items():
            if key not in nomenclatures:
                errors[key] = f"Unknown nomenclature code '{key}'."
                continue

            nom = nomenclatures[key]

            if nom.kind == Nomenclature.LOOKUP:
                if not isinstance(val, int):
                    errors[key] = "Must be a nomenclature entry ID (integer)."
                elif not nom.entries.filter(id=val, is_active=True).exists():
                    errors[key] = f"Entry ID {val} not found in '{nom.code}'."

            elif nom.kind == Nomenclature.FIELD:
                field_def = nom.fields.first()
                if field_def:
                    if field_def.field_type == NomenclatureField.TEXT and not isinstance(val, str):
                        errors[key] = "Must be a string."
                    elif field_def.field_type == NomenclatureField.NUMBER and not isinstance(val, (int, float)):
                        errors[key] = "Must be a number."
                    elif field_def.field_type == NomenclatureField.BOOL and not isinstance(val, bool):
                        errors[key] = "Must be a boolean."
                    elif field_def.field_type == NomenclatureField.CHOICE and val not in (field_def.choices or []):
                        errors[key] = f"Must be one of {field_def.choices}."

        if errors:
            raise serializers.ValidationError(errors)

        return value


class VehicleSerializer(serializers.ModelSerializer):
    extra = VehicleExtraSerializer(required=False, allow_null=True)
    tare_user_name = serializers.CharField(source='tare_user.username', read_only=True, default=None)
    gross_user_name = serializers.CharField(source='gross_user.username', read_only=True, default=None)
    net_user_name = serializers.CharField(source='net_user.username', read_only=True, default=None)

    class Meta:
        model = Vehicle
        fields = ['id', 'vehicle_number', 'trailer1_number', 'trailer2_number', 'driver_pid',
                  'tare', 'tare_date', 'tare_user', 'tare_user_name',
                  'gross', 'gross_date', 'gross_user', 'gross_user_name',
                  'net', 'net_date', 'net_user', 'net_user_name',
                  'doc_number', 'extra']
        read_only_fields = ['id', 'tare_date', 'tare_user', 'tare_user_name',
                            'gross_date', 'gross_user', 'gross_user_name',
                            'net', 'net_date', 'net_user', 'net_user_name',
                            'doc_number']

    def create(self, validated_data):
        extra_data = validated_data.pop('extra', None)
        vehicle = Vehicle.objects.create(**validated_data)

        if extra_data:
            VehicleExtra.objects.create(vehicle=vehicle, **extra_data)

        return vehicle

    def update(self, instance, validated_data):
        extra_data = validated_data.pop('extra', None)

        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        # Handle VehicleExtra update/creation
        if extra_data is not None:
            if hasattr(instance, 'extra'):
                for attr, value in extra_data.items():
                    setattr(instance.extra, attr, value)
                instance.extra.save()
            else:
                VehicleExtra.objects.create(vehicle=instance, **extra_data)

        return instance

    def validate_vehicle_number(self, value):
        if not value or not value.strip():
            raise serializers.ValidationError("Vehicle number cannot be empty")
        return value.strip()


class NomenclatureFieldSerializer(serializers.ModelSerializer):
    class Meta:
        model = NomenclatureField
        fields = ['id', 'key', 'label', 'field_type', 'required', 'choices', 'sort_order']
        read_only_fields = ['id']

    def validate_key(self, value):
        if not value or not value.strip():
            raise serializers.ValidationError("Field key cannot be empty")
        return value.strip()

    def validate_field_type(self, value):
        valid_types = [choice[0] for choice in NomenclatureField.FIELD_TYPES]
        if value not in valid_types:
            raise serializers.ValidationError(
                f"Invalid field type. Must be one of: {', '.join(valid_types)}"
            )
        return value

    def validate(self, data):
        if data.get('field_type') == NomenclatureField.CHOICE:
            choices = data.get('choices')
            if not choices or not isinstance(choices, list) or len(choices) == 0:
                raise serializers.ValidationError({
                    'choices': 'Choices must be a non-empty list for choice fields.'
                })
        elif data.get('choices'):
            raise serializers.ValidationError({
                'choices': 'Only choice-type fields can have choices.'
            })
        return data


class NomenclatureEntrySerializer(serializers.ModelSerializer):
    display_value = serializers.SerializerMethodField()

    class Meta:
        model = NomenclatureEntry
        fields = ['id', 'data', 'is_active', 'display_value']
        read_only_fields = ['id']

    def get_display_value(self, obj):
        display_key = obj.nomenclature.display_field or "name"
        return obj.data.get(display_key, f"Entry #{obj.pk}")

    def validate_data(self, value):
        nomenclature = self.context.get('nomenclature')
        if not nomenclature:
            return value

        field_defs = {f.key: f for f in nomenclature.fields.all()}

        for key in value.keys():
            if key not in field_defs:
                raise serializers.ValidationError(
                    f"Unknown field '{key}'. Valid fields: {list(field_defs.keys())}"
                )

        for key, field_def in field_defs.items():
            if field_def.required and key not in value:
                raise serializers.ValidationError(
                    f"Required field '{key}' is missing."
                )

        for key, val in value.items():
            if key not in field_defs:
                continue
            field_def = field_defs[key]

            if field_def.field_type == NomenclatureField.TEXT and not isinstance(val, str):
                raise serializers.ValidationError(f"Field '{key}' must be a string.")
            elif field_def.field_type == NomenclatureField.NUMBER and not isinstance(val, (int, float)):
                raise serializers.ValidationError(f"Field '{key}' must be a number.")
            elif field_def.field_type == NomenclatureField.BOOL and not isinstance(val, bool):
                raise serializers.ValidationError(f"Field '{key}' must be a boolean.")
            elif field_def.field_type == NomenclatureField.CHOICE and val not in (field_def.choices or []):
                raise serializers.ValidationError(f"Field '{key}' must be one of {field_def.choices}.")

        return value


class NomenclatureSerializer(serializers.ModelSerializer):
    fields = NomenclatureFieldSerializer(many=True, required=False)
    entry_count = serializers.IntegerField(source='entries.count', read_only=True)

    class Meta:
        model = Nomenclature
        fields = ['id', 'code', 'name', 'applies_to', 'kind', 'display_field',
                  'sort_order', 'fields', 'entry_count']
        read_only_fields = ['id']

    def create(self, validated_data):
        fields_data = validated_data.pop('fields', [])
        nomenclature = Nomenclature.objects.create(**validated_data)

        for field_data in fields_data:
            NomenclatureField.objects.create(nomenclature=nomenclature, **field_data)

        return nomenclature

    def update(self, instance, validated_data):
        fields_data = validated_data.pop('fields', None)

        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        if fields_data is not None:
            instance.fields.all().delete()
            for field_data in fields_data:
                NomenclatureField.objects.create(nomenclature=instance, **field_data)

        return instance

    def validate_code(self, value):
        if not value or not value.strip():
            raise serializers.ValidationError("Code cannot be empty")
        return value.strip()

    def validate_applies_to(self, value):
        valid_choices = ['vehicle', 'container']
        if value not in valid_choices:
            raise serializers.ValidationError(
                f"Invalid applies_to value. Must be one of: {', '.join(valid_choices)}"
            )
        return value

    def validate(self, data):
        kind = data.get('kind', getattr(self.instance, 'kind', Nomenclature.LOOKUP))
        fields_data = data.get('fields', [])

        if kind == Nomenclature.FIELD and fields_data and len(fields_data) != 1:
            raise serializers.ValidationError(
                "A 'field' nomenclature must have exactly one field definition."
            )

        if kind == Nomenclature.LOOKUP:
            display_field = data.get('display_field', getattr(self.instance, 'display_field', 'name'))
            field_keys = [f['key'] for f in fields_data] if fields_data else []
            if field_keys and display_field not in field_keys:
                raise serializers.ValidationError(
                    f"display_field '{display_field}' must match one of the defined field keys."
                )

        return data


class ReportSerializer(serializers.ModelSerializer):
    created_by_name = serializers.CharField(source='created_by.username', read_only=True, default=None)

    class Meta:
        model = Report
        fields = ['id', 'name', 'page_width', 'page_height', 'api_endpoint',
                  'elements', 'created_by', 'created_by_name', 'created_at', 'updated_at']
        read_only_fields = ['id', 'created_by', 'created_by_name', 'created_at', 'updated_at']

    def validate_name(self, value):
        if not value or not value.strip():
            raise serializers.ValidationError("Report name cannot be empty")
        return value.strip()

    def validate_elements(self, value):
        if not isinstance(value, list):
            raise serializers.ValidationError("Elements must be a list")
        return value
