"""
alloggiati/serializers.py
=========================
DRF serializers for the Alloggiati Web sync API.
"""

from __future__ import annotations

from rest_framework import serializers

from alloggiati.models import AlloggiatiCredential, AlloggiatiSyncLog


# ---------------------------------------------------------------------------
# Nested serializers for structured validation errors
# ---------------------------------------------------------------------------

class AlloggiatiFieldErrorSerializer(serializers.Serializer):
    """A single field-level validation error."""
    field   = serializers.CharField(help_text="Guest field that failed validation.")
    message = serializers.CharField(help_text="Human-readable description of the failure.")


class AlloggiatiGuestValidationErrorSerializer(serializers.Serializer):
    """All validation errors for a single guest."""
    guest_id   = serializers.IntegerField(allow_null=True, help_text="Guest primary key.")
    booking_id = serializers.IntegerField(allow_null=True, help_text="Booking primary key.")
    guest_name = serializers.CharField(help_text="Guest display name (no document numbers).")
    errors     = AlloggiatiFieldErrorSerializer(
        many=True,
        help_text="List of field-level validation failures for this guest.",
    )


# ---------------------------------------------------------------------------
# Sync request / response
# ---------------------------------------------------------------------------

class AlloggiatiSyncRequestSerializer(serializers.Serializer):
    """Validates the POST /api/alloggiati/sync request body."""

    structureId = serializers.IntegerField(
        help_text="Primary key of the Structure to sync."
    )
    dateFrom = serializers.DateField(
        input_formats=["%Y-%m-%d"],
        help_text="Start of sync window (YYYY-MM-DD, inclusive).",
    )
    dateTo = serializers.DateField(
        input_formats=["%Y-%m-%d"],
        help_text="End of sync window (YYYY-MM-DD, inclusive).",
    )

    def validate(self, data):
        if data["dateFrom"] > data["dateTo"]:
            raise serializers.ValidationError(
                "dateFrom cannot be later than dateTo."
            )
        return data


class AlloggiatiSyncResponseSerializer(serializers.Serializer):
    """Documents the POST /api/alloggiati/sync response body."""

    status = serializers.ChoiceField(
        choices=["CONNECTED", "PARTIAL", "ERROR"],
        help_text="Outcome of the sync operation.",
    )
    sent     = serializers.IntegerField(help_text="Number of guests accepted by Alloggiati Web.")
    rejected = serializers.IntegerField(help_text="Total guests rejected (validation + server).")
    message  = serializers.CharField(help_text="Human-readable summary.")
    sync_log_id = serializers.UUIDField(
        allow_null=True,
        help_text="UUID of the persisted AlloggiatiSyncLog record.",
    )
    validation_errors = AlloggiatiGuestValidationErrorSerializer(
        many=True,
        help_text=(
            "Per-guest structured validation errors. "
            "Empty list when all guests passed validation."
        ),
    )


# ---------------------------------------------------------------------------
# Sync log history
# ---------------------------------------------------------------------------

class AlloggiatiSyncLogSerializer(serializers.ModelSerializer):
    """Read-only serializer for AlloggiatiSyncLog list/detail views."""

    class Meta:
        model = AlloggiatiSyncLog
        fields = [
            "id",
            "structure",
            "date_from",
            "date_to",
            "status",
            "message",
            "guests_sent",
            "guests_rejected",
            "validation_errors",
            "started_at",
            "completed_at",
        ]
        read_only_fields = fields


# ---------------------------------------------------------------------------
# Credential management
# ---------------------------------------------------------------------------

class AlloggiatiCredentialWriteSerializer(serializers.ModelSerializer):
    """
    Write serializer for creating / updating AlloggiatiCredential.

    Accepts plain-text credential values; encryption is handled by the
    model property setters.
    """

    username = serializers.CharField(
        required=False,
        allow_blank=True,
        write_only=True,
        help_text="Alloggiati Web username (CODES mode).",
    )
    password = serializers.CharField(
        required=False,
        allow_blank=True,
        write_only=True,
        help_text="Alloggiati Web password / web service key (CODES mode).",
    )
    certificate = serializers.CharField(
        required=False,
        allow_blank=True,
        write_only=True,
        help_text="PEM-encoded client certificate (DIGITAL_CERTIFICATE mode).",
    )
    private_key = serializers.CharField(
        required=False,
        allow_blank=True,
        write_only=True,
        help_text="PEM-encoded private key (DIGITAL_CERTIFICATE mode).",
    )

    class Meta:
        model = AlloggiatiCredential
        fields = ["mode", "username", "password", "certificate", "private_key"]

    def validate(self, data):
        mode = data.get(
            "mode",
            getattr(self.instance, "mode", AlloggiatiCredential.MODE_CODES),
        )

        is_update = self.instance is not None

        if mode == AlloggiatiCredential.MODE_CODES:
            username = data.get("username")

            # Username is always required
            if username is None or username.strip() == "":
                raise serializers.ValidationError(
                    "username is required for CODES mode."
                )

            # Password required only during create
            if not is_update:
                password = data.get("password")
                if password is None or password.strip() == "":
                    raise serializers.ValidationError(
                        "password is required for CODES mode."
                    )

        elif mode == AlloggiatiCredential.MODE_DIGITAL_CERTIFICATE:
            certificate = data.get("certificate")
            private_key = data.get("private_key")

            # Certificate + key required only during create
            if not is_update:
                if not certificate or not private_key:
                    raise serializers.ValidationError(
                        "certificate and private_key are required for DIGITAL_CERTIFICATE mode."
                    )

        return data

    def create(self, validated_data):
        structure = self.context["structure"]
        credential = AlloggiatiCredential(structure=structure)
        return self._apply_fields(credential, validated_data)

    def update(self, instance, validated_data):
        return self._apply_fields(instance, validated_data)

    def _apply_fields(
        self, credential: AlloggiatiCredential, data: dict
    ) -> AlloggiatiCredential:
        credential.mode = data.get("mode", credential.mode)
        if "username" in data:
            credential.username = data["username"]
        if "password" in data:
            credential.password = data["password"]
        if "certificate" in data:
            credential.certificate = data["certificate"]
        if "private_key" in data:
            credential.private_key = data["private_key"]
        credential.save()
        return credential


class AlloggiatiCredentialStatusSerializer(serializers.Serializer):
    """Read-only status payload for credential connection state."""

    structure_id = serializers.IntegerField()
    connected    = serializers.BooleanField()
    mode         = serializers.CharField(allow_null=True)
    created_at   = serializers.DateTimeField(allow_null=True)
    updated_at   = serializers.DateTimeField(allow_null=True)
