"""
Django model validation utilities using SST Pydantic schemas.

This module provides mixins and utilities for integrating SST validation
into Django models via clean() methods.

Usage in Django models:
    from lightwave.schema.validation.django import SSTValidatedModelMixin

    class Layout(SSTValidatedModelMixin, models.Model):
        name = models.CharField(max_length=50)
        template_path = models.CharField(max_length=200)

        def get_sst_validation_data(self):
            return {
                "template_path": self.template_path,
                "is_active": self.is_active,
            }

        def get_sst_key(self):
            return self.name
"""

from typing import Any

from django.core.exceptions import ValidationError
from pydantic import ValidationError as PydanticValidationError

from lightwave.schema.pydantic.core.validators import FieldOptions


class FieldOptionsValidatorMixin:
    """
    Mixin providing field validation against field_options.yaml.

    Add to Django models that need runtime validation of choice fields.
    """

    def validate_field_option(self, field_name: str, value: Any) -> str:
        """
        Validate a field value against field_options.yaml.

        Args:
            field_name: Name of the field in field_options.yaml
            value: Value to validate

        Returns:
            The validated value

        Raises:
            ValidationError: If value is not valid
        """
        try:
            return FieldOptions.validate(field_name, value)
        except ValueError as e:
            raise ValidationError({field_name: str(e)}) from e


class SSTLayoutValidator:
    """
    Validator for Layout model against layouts.yaml SST.

    Usage:
        class Layout(models.Model):
            def clean(self):
                SSTLayoutValidator.validate(self.name, {
                    "template_path": self.template_path,
                    "is_active": self.is_active,
                })
    """

    @classmethod
    def validate(cls, name: str, data: dict[str, Any]) -> list[str]:
        """
        Validate layout data against SST.

        Returns list of drift errors (empty if valid).
        """
        from lightwave.schema.pydantic.models.layouts import LayoutSchema

        return LayoutSchema.validate_against_sst(name, data)

    @classmethod
    def get_valid_names(cls) -> list[str]:
        """Get list of valid layout names from SST."""
        from lightwave.schema.pydantic.models.layouts import LayoutSchema

        return LayoutSchema.get_valid_layout_names()

    @classmethod
    def validate_name(cls, name: str) -> str:
        """
        Validate a layout name against SST.

        Raises ValidationError if invalid.
        """
        from lightwave.schema.pydantic.models.layouts import LayoutSchema

        try:
            return LayoutSchema.validate_layout_name(name)
        except ValueError as e:
            raise ValidationError({"name": str(e)}) from e


class SSTBlueprintValidator:
    """
    Validator for Blueprint/PageSchema model against blueprints.yaml SST.
    """

    @classmethod
    def validate(cls, name: str, data: dict[str, Any]) -> list[str]:
        """
        Validate blueprint data against SST.

        Returns list of drift errors (empty if valid).
        """
        from lightwave.schema.pydantic.sst.blueprints import PageSchemaDefinition

        return PageSchemaDefinition.validate_against_sst(name, data)

    @classmethod
    def get_valid_names(cls) -> list[str]:
        """Get list of valid blueprint names from SST."""
        from lightwave.schema.pydantic.sst.blueprints import PageSchemaDefinition

        all_blueprints = PageSchemaDefinition.get_all_from_sst()
        return [bp.name for bp in all_blueprints]

    @classmethod
    def validate_name(cls, name: str) -> str:
        """
        Validate a blueprint name against SST.

        Raises ValidationError if invalid.
        """
        from lightwave.schema.pydantic.sst.blueprints import PageSchemaDefinition

        try:
            PageSchemaDefinition.get_by_name(name)
            return name
        except KeyError as e:
            raise ValidationError({"name": str(e)}) from e


class SSTNavItemValidator:
    """
    Validator for NavItem model against navigation.yaml SST.
    """

    @classmethod
    def validate_menu_location(cls, location: str) -> str:
        """
        Validate a menu location against SST.

        Raises ValidationError if invalid.
        """
        from lightwave.schema.pydantic.models.navigation import NavItemSchema

        try:
            return NavItemSchema.validate_menu_location(location)
        except ValueError as e:
            raise ValidationError({"menu_location": str(e)}) from e

    @classmethod
    def get_valid_menu_locations(cls) -> list[str]:
        """Get list of valid menu locations from SST."""
        from lightwave.schema.pydantic.models.navigation import NavItemSchema

        return NavItemSchema.get_valid_menu_locations()


class JSONFieldValidator:
    """
    Validate JSONField data using Pydantic schemas.

    Usage:
        class Page(models.Model):
            data = models.JSONField()
            metadata = models.JSONField()

            def clean(self):
                JSONFieldValidator.validate_metadata(self.metadata)
    """

    @classmethod
    def validate_with_schema(cls, schema_class: type, data: dict[str, Any], field_name: str = "data") -> None:
        """
        Validate data against a Pydantic schema.

        Args:
            schema_class: The Pydantic schema class
            data: The data to validate
            field_name: Name for error messages

        Raises:
            ValidationError: If validation fails
        """
        try:
            schema_class(**data)
        except PydanticValidationError as e:
            errors = {}
            for error in e.errors():
                loc = ".".join(str(part) for part in error["loc"])
                key = f"{field_name}.{loc}" if loc else field_name
                errors[key] = error["msg"]
            raise ValidationError(errors) from e

    @classmethod
    def validate_site_settings(cls, data: dict[str, Any]) -> None:
        """Validate Site.settings JSONField."""
        from lightwave.schema.pydantic.models.cms import SiteSettingsSchema

        cls.validate_with_schema(SiteSettingsSchema, data, "settings")

    @classmethod
    def validate_page_metadata(cls, data: dict[str, Any]) -> None:
        """Validate Page.metadata JSONField."""
        from lightwave.schema.pydantic.models.cms import PageMetadataSchema

        cls.validate_with_schema(PageMetadataSchema, data, "metadata")

    @classmethod
    def validate_agent_permissions(cls, data: dict[str, Any]) -> None:
        """Validate AgentAPIKey.permissions JSONField."""
        from lightwave.schema.pydantic.models.cms import AgentPermissionsSchema

        cls.validate_with_schema(AgentPermissionsSchema, data, "permissions")

    @classmethod
    def validate_page_version_data(cls, data: dict[str, Any]) -> None:
        """Validate PageVersion.data JSONField."""
        from lightwave.schema.pydantic.models.cms import PageVersionDataSchema

        cls.validate_with_schema(PageVersionDataSchema, data, "data")

    @classmethod
    def validate_outbox_page_creation(cls, data: dict[str, Any]) -> None:
        """Validate OutboxItem.item_data for page creation."""
        from lightwave.schema.pydantic.models.cms import PageCreationPayloadSchema

        cls.validate_with_schema(PageCreationPayloadSchema, data, "item_data")

    @classmethod
    def validate_outbox_page_update(cls, data: dict[str, Any]) -> None:
        """Validate OutboxItem.item_data for page update."""
        from lightwave.schema.pydantic.models.cms import PageUpdatePayloadSchema

        cls.validate_with_schema(PageUpdatePayloadSchema, data, "item_data")


def validate_page_type(value: str) -> str:
    """Validate page_type field against field_options.yaml."""
    try:
        return FieldOptions.validate("page_types", value)
    except ValueError as e:
        raise ValidationError({"page_type": str(e)}) from e


def validate_page_status(value: str) -> str:
    """Validate page status field against field_options.yaml."""
    try:
        return FieldOptions.validate("page_statuses", value)
    except ValueError as e:
        raise ValidationError({"status": str(e)}) from e


def validate_content_status(value: str) -> str:
    """Validate content workflow status against field_options.yaml."""
    try:
        return FieldOptions.validate("content_statuses", value)
    except ValueError as e:
        raise ValidationError({"status": str(e)}) from e


def validate_outbox_item_type(value: str) -> str:
    """Validate outbox item type against field_options.yaml."""
    try:
        return FieldOptions.validate("outbox_item_types", value)
    except ValueError as e:
        raise ValidationError({"item_type": str(e)}) from e


def validate_block_type(value: str) -> str:
    """Validate block type against field_options.yaml."""
    try:
        return FieldOptions.validate("block_types", value)
    except ValueError as e:
        raise ValidationError({"block_type": str(e)}) from e


def validate_auth_mode(value: str) -> str:
    """Validate auth mode against field_options.yaml."""
    try:
        return FieldOptions.validate("auth_modes", value)
    except ValueError as e:
        raise ValidationError({"auth_mode": str(e)}) from e


def get_layout_choices() -> list[tuple[str, str]]:
    """
    Get Django-compatible choices for layout field from SST.

    Returns list of (value, label) tuples for use in model fields.
    """
    from lightwave.schema.pydantic.models.layouts import LayoutSchema

    layouts = LayoutSchema.get_all_from_sst()
    return [(layout.name, layout.description or layout.name) for layout in layouts]


def get_field_choices(field_name: str) -> list[tuple[str, str]]:
    """
    Get Django-compatible choices from field_options.yaml.

    Args:
        field_name: The field name in field_options.yaml

    Returns:
        List of (value, label) tuples for use in model fields.
    """
    options = FieldOptions.get_options_with_labels(field_name)
    return [(opt["value"], opt["label"]) for opt in options]
