"""Validation for the Voice Agents module.

Mirrors the field rules in the Hunar Voice Agents API Docs (Agents section) so a
bad request is rejected here with per-field errors, instead of costing a round
trip and coming back as an opaque 422. Hunar still validates authoritatively —
this only front-runs it.

Nothing is persisted: Hunar owns agent state. These are plain Serializers, not
ModelSerializers.
"""

from rest_framework import serializers

#: Agent languages (PDF p1).
LANGUAGES = [
    "ENGLISH", "HINDI", "TAMIL", "TELUGU", "KANNADA", "MARATHI", "MALAYALAM",
    "GUJARATI", "BENGALI", "TURKISH", "ARABIC", "SPANISH",
]

#: Voice personas (PDF p1).
VOICE_PERSONAS = ["NEHA", "ROY", "ZOE", "SAM", "MIRA", "EESHA"]

#: Agent statuses Hunar reports, used for the list filter (PDF p3, p7).
AGENT_STATUSES = ["DRAFT", "ACTIVE", "INACTIVE"]

#: When `voice_persona` or `language` changes, Hunar requires ALL of these in the
#: same request (PDF p6, p10).
PERSONA_CHANGE_REQUIRED_FIELDS = [
    "name", "objective", "language", "voice_persona", "persona_name",
    "agent_prompt", "introduction", "result_prompt", "result_schema",
]


def _validate_result_pair(attrs, existing=None):
    """`result_prompt` and `result_schema` must be provided together (PDF p10).

    On update, a field already present on the agent counts as provided — sending
    just one of the two is only an error when the other is absent everywhere.
    """
    existing = existing or {}
    has_prompt = "result_prompt" in attrs or bool(existing.get("result_prompt"))
    has_schema = "result_schema" in attrs or existing.get("result_schema") is not None
    if ("result_prompt" in attrs or "result_schema" in attrs) and not (has_prompt and has_schema):
        missing = "result_schema" if has_prompt else "result_prompt"
        raise serializers.ValidationError({
            missing: "result_prompt and result_schema must be provided together.",
        })


class VoiceAgentCreateSerializer(serializers.Serializer):
    """POST /ai-calls/agents/ — every field required except `persona_name` (PDF p1-2)."""

    name = serializers.CharField(min_length=3, max_length=64)
    language = serializers.ChoiceField(choices=LANGUAGES)
    voice_persona = serializers.ChoiceField(choices=VOICE_PERSONAS)
    persona_name = serializers.CharField(required=False, allow_blank=True, max_length=64)
    agent_prompt = serializers.CharField()
    objective = serializers.CharField()
    introduction = serializers.CharField()
    result_prompt = serializers.CharField()
    result_schema = serializers.JSONField()
    status = serializers.ChoiceField(choices=AGENT_STATUSES, required=False, default="DRAFT")

    def validate_result_schema(self, value):
        if not isinstance(value, dict):
            raise serializers.ValidationError("result_schema must be a JSON object.")
        return value


class VoiceAgentUpdateSerializer(serializers.Serializer):
    """PUT /ai-calls/agents/<uuid>/ — every field optional (PDF p2, p5).

    Instantiate with `context={"existing": <current agent dict>}` so the
    persona/language rule can tell a real change from a no-op.
    """

    name = serializers.CharField(min_length=3, max_length=64, required=False)
    language = serializers.ChoiceField(choices=LANGUAGES, required=False)
    voice_persona = serializers.ChoiceField(choices=VOICE_PERSONAS, required=False)
    persona_name = serializers.CharField(required=False, allow_blank=True, max_length=64)
    agent_prompt = serializers.CharField(required=False)
    objective = serializers.CharField(required=False)
    introduction = serializers.CharField(required=False)
    result_prompt = serializers.CharField(required=False)
    result_schema = serializers.JSONField(required=False)
    status = serializers.ChoiceField(choices=AGENT_STATUSES, required=False)

    def validate_result_schema(self, value):
        if not isinstance(value, dict):
            raise serializers.ValidationError("result_schema must be a JSON object.")
        return value

    def validate(self, attrs):
        if not attrs:
            raise serializers.ValidationError(
                "Send at least one field to update."
            )
        existing = self.context.get("existing") or {}
        _validate_result_pair(attrs, existing)

        # Changing voice_persona or language requires the full set (PDF p6/p10).
        # Only an actual change triggers it — resubmitting the same value does not.
        changing = [
            field for field in ("voice_persona", "language")
            if field in attrs and str(attrs[field]) != str(existing.get(field) or "")
        ]
        if changing:
            missing = [f for f in PERSONA_CHANGE_REQUIRED_FIELDS if f not in attrs]
            if missing:
                raise serializers.ValidationError({
                    field: (
                        f"When updating {' or '.join(changing)}, all of these must be sent "
                        f"together: {', '.join(PERSONA_CHANGE_REQUIRED_FIELDS)}. "
                        f"Missing: {', '.join(missing)}."
                    )
                    for field in missing
                })
        return attrs
