"""
Base Pydantic schema classes for LightWave platform.

This module defines the foundational schema classes used throughout the platform:

- LightwaveBaseSchema: Universal base for all schemas
- SSTSchema: Base for SST YAML-derived schemas (immutable)
- APIRequestSchema: Base for API request validation
- APIResponseSchema: Base for API response serialization
- JSONFieldSchema: Base for Django model JSONField validation

All schemas enforce strict typing and validation at runtime.

Example:
    from lightwave.schema.pydantic.core.base import LightwaveBaseSchema, JSONFieldSchema

    class MySchema(LightwaveBaseSchema):
        name: str
        count: int = 0

    class MySettingsSchema(JSONFieldSchema):
        enabled: bool = True
        options: list[str] = []
"""

from datetime import datetime
from typing import Any, Generic, TypeVar

from pydantic import BaseModel, ConfigDict, Field


class LightwaveBaseSchema(BaseModel):
    """
    Universal base schema for all Pydantic models in LightWave platform.

    Enforces:
    - Strict validation (no extra fields by default)
    - JSON serialization compatibility
    - Consistent datetime/UUID handling
    - Django model compatibility via from_attributes

    All schemas should inherit from this class or one of its specialized
    subclasses (SSTSchema, APIRequestSchema, etc.).

    Example:
        class UserSchema(LightwaveBaseSchema):
            id: int
            email: str
            is_active: bool = True
    """

    model_config = ConfigDict(
        # Strict validation - reject unknown fields
        extra="forbid",
        # Allow arbitrary types for Django model instances
        arbitrary_types_allowed=True,
        # Use enum values in JSON serialization
        use_enum_values=True,
        # Populate by field name (not alias)
        populate_by_name=True,
        # Enable from_attributes for Django model conversion
        from_attributes=True,
        # JSON serialization configuration
        ser_json_timedelta="iso8601",
        ser_json_bytes="base64",
        # Validate default values
        validate_default=True,
    )

    def to_dict(self) -> dict[str, Any]:
        """
        Convert schema to dictionary.

        Shorthand for model_dump() with sensible defaults.
        """
        return self.model_dump(mode="python", exclude_unset=False)

    def to_json_dict(self) -> dict[str, Any]:
        """
        Convert schema to JSON-serializable dictionary.

        Use this when you need to serialize to JSON.
        """
        return self.model_dump(mode="json", exclude_unset=False)


class SSTSchema(LightwaveBaseSchema):
    """
    Base for schemas derived from SST YAML files.

    SST schemas represent the IDEAL state defined in SST YAML files.
    They are immutable (frozen) to prevent accidental modification.

    Use cases:
    - Validating database records against SST definitions
    - Generating migration data from SST
    - API responses that expose SST-defined structures
    - Test fixtures generated from SST

    Example:
        class LayoutSchema(SSTSchema):
            name: str
            template_path: str
            is_active: bool = True

        # Load from SST YAML
        layouts = LayoutSchema.get_all_from_sst()
    """

    model_config = ConfigDict(
        # SST schemas are immutable snapshots
        frozen=True,
        # Inherit from base
        extra="forbid",
        arbitrary_types_allowed=True,
        use_enum_values=True,
        populate_by_name=True,
        from_attributes=True,
        validate_default=True,
    )


class APIRequestSchema(LightwaveBaseSchema):
    """
    Base for API request schemas.

    Request schemas are more permissive - they ignore extra fields
    for forward compatibility with evolving APIs.

    Use cases:
    - POST/PUT/PATCH request body validation
    - Query parameter validation
    - Form data validation

    Example:
        class PageCreateRequest(APIRequestSchema):
            title: str
            slug: str | None = None
            data: dict = Field(default_factory=dict)

        # In django-ninja view
        @router.post("/pages/")
        def create_page(request, payload: PageCreateRequest):
            ...
    """

    model_config = ConfigDict(
        # Allow extra fields in requests (forward compatibility)
        extra="ignore",
        # Inherit from base
        arbitrary_types_allowed=True,
        use_enum_values=True,
        populate_by_name=True,
        from_attributes=True,
        validate_default=True,
    )


class APIResponseSchema(LightwaveBaseSchema):
    """
    Base for API response schemas.

    Response schemas include common metadata fields like id, created_at,
    updated_at. These are optional to support both creation and retrieval.

    Use cases:
    - GET response serialization
    - POST/PUT/PATCH response serialization
    - List item serialization

    Example:
        class PageResponse(APIResponseSchema):
            title: str
            path: str
            status: str

        # In django-ninja view
        @router.get("/pages/{page_id}", response=PageResponse)
        def get_page(request, page_id: int):
            page = Page.objects.get(id=page_id)
            return PageResponse.model_validate(page)
    """

    # Common response fields - optional since some responses don't have them
    id: int | None = Field(None, description="Primary key")
    created_at: datetime | None = Field(None, description="Creation timestamp")
    updated_at: datetime | None = Field(None, description="Last update timestamp")


class JSONFieldSchema(LightwaveBaseSchema):
    """
    Base for Django JSONField schemas.

    JSONField schemas validate data stored in PostgreSQL JSONB columns.
    They allow extra fields for backwards compatibility during schema evolution.

    Use cases:
    - Site.settings validation
    - Page.data validation
    - Page.metadata validation
    - Component.props validation

    Example:
        class SiteSettingsSchema(JSONFieldSchema):
            class BrandSettings(JSONFieldSchema):
                name: str
                tagline: str = ""

            brand: BrandSettings = Field(default_factory=BrandSettings)

        # In Django model
        class Site(models.Model):
            settings = models.JSONField(default=dict)

            def clean(self):
                SiteSettingsSchema(**self.settings)  # Validates on save

            @property
            def settings_typed(self) -> SiteSettingsSchema:
                return SiteSettingsSchema(**self.settings)
    """

    model_config = ConfigDict(
        # JSONFields can evolve - allow extra fields for backwards compatibility
        extra="allow",
        # Inherit from base
        arbitrary_types_allowed=True,
        use_enum_values=True,
        populate_by_name=True,
        from_attributes=True,
        validate_default=True,
    )


# =============================================================================
# Generic Response Wrappers
# =============================================================================

T = TypeVar("T")


class PaginatedResponse(BaseModel, Generic[T]):
    """
    Generic paginated response wrapper.

    Used by list endpoints to provide consistent pagination metadata.

    Example:
        @router.get("/pages/", response=PaginatedResponse[PageListResponse])
        def list_pages(request, limit: int = 20, offset: int = 0):
            pages = Page.objects.all()[offset:offset + limit]
            return PaginatedResponse(
                count=Page.objects.count(),
                results=[PageListResponse.model_validate(p) for p in pages],
            )
    """

    count: int = Field(..., description="Total number of items")
    next: str | None = Field(None, description="URL to next page")
    previous: str | None = Field(None, description="URL to previous page")
    results: list[T] = Field(..., description="Page of results")

    model_config = ConfigDict(
        # Allow generic types
        arbitrary_types_allowed=True,
    )


class ErrorDetail(BaseModel):
    """
    Individual error detail in an error response.

    Provides granular error information including:
    - Which field caused the error (if applicable)
    - Human-readable error message
    - Machine-readable error code for frontend handling
    """

    field: str | None = Field(None, description="Field that caused error (if applicable)")
    message: str = Field(..., description="Human-readable error message")
    code: str = Field(..., description="Machine-readable error code")
    context: dict[str, Any] | None = Field(None, description="Additional error context")


class ErrorResponse(BaseModel):
    """
    Standard API error response.

    Provides a consistent error format across all API endpoints.

    Example response:
        {
            "detail": "Validation failed",
            "errors": [
                {"field": "title", "message": "This field is required", "code": "required"},
                {"field": "data.headline", "message": "Must be at most 100 characters", "code": "max_length"}
            ]
        }
    """

    detail: str = Field(..., description="High-level error message")
    errors: list[ErrorDetail] = Field(default_factory=list, description="Detailed errors")
    request_id: str | None = Field(None, description="Request ID for debugging")


# =============================================================================
# Utility Mixins
# =============================================================================


class TimestampMixin(BaseModel):
    """
    Mixin for models with timestamp fields.

    Provides created_at and updated_at fields.
    Use with multiple inheritance.

    Example:
        class PageResponse(APIResponseSchema, TimestampMixin):
            title: str
    """

    created_at: datetime = Field(..., description="Creation timestamp")
    updated_at: datetime = Field(..., description="Last update timestamp")


class SoftDeleteMixin(BaseModel):
    """
    Mixin for soft-deletable models.

    Provides is_active and deleted_at fields.
    Use with multiple inheritance.

    Example:
        class UserResponse(APIResponseSchema, SoftDeleteMixin):
            email: str
    """

    is_active: bool = Field(True, description="Whether the record is active")
    deleted_at: datetime | None = Field(None, description="Soft delete timestamp")


class AuditMixin(BaseModel):
    """
    Mixin for audited models.

    Provides created_by and updated_by fields.
    Use with multiple inheritance.

    Example:
        class PageResponse(APIResponseSchema, AuditMixin):
            title: str
    """

    created_by_id: int | None = Field(None, description="User ID who created this record")
    updated_by_id: int | None = Field(None, description="User ID who last updated this record")
