"""WhatsApp Flow schema definitions using Pydantic models.

This module provides complete type-safe models for all WhatsApp Flow components,
enabling validation and serialization of flow JSON.
"""

from __future__ import annotations

from typing import Any, Dict, List, Literal, Optional, Union

from pydantic import BaseModel, ConfigDict, Field, ValidationError


class WhatsAppBaseModel(BaseModel):
    """Base model with WhatsApp Flow-specific configuration."""

    model_config = ConfigDict(
        extra="ignore",
        populate_by_name=True,
    )

    def model_dump(self, **kwargs: Any) -> dict[str, Any]:
        """Override to exclude None values by default."""
        kwargs.setdefault("exclude_none", True)
        kwargs.setdefault("by_alias", True)
        return super().model_dump(**kwargs)

    def model_dump_json(self, **kwargs: Any) -> str:
        """Override to exclude None values by default."""
        kwargs.setdefault("exclude_none", True)
        kwargs.setdefault("by_alias", True)
        return super().model_dump_json(**kwargs)


# ---------- Data Source Item ----------
class DataSourceItem(WhatsAppBaseModel):
    """Item in a data source list for selection components."""

    id: str
    title: str


# ---------- Navigation Target ----------
class NavigationTarget(WhatsAppBaseModel):
    """Target for navigation actions."""

    type: Literal["screen"]
    name: str


# ---------- Click Actions ----------
class NavigateAction(WhatsAppBaseModel):
    """Action to navigate to another screen."""

    name: Literal["navigate"]
    next: NavigationTarget
    payload: Optional[Dict[str, str]] = None


class CompleteAction(WhatsAppBaseModel):
    """Action to complete the flow and submit data."""

    name: Literal["complete"]
    payload: Dict[str, str]


class OpenUrlAction(WhatsAppBaseModel):
    """Action to open an external URL."""

    name: Literal["open_url"]
    url: str


# ---------- Text Components ----------
class TextHeading(WhatsAppBaseModel):
    """Main heading text component."""

    type: Literal["TextHeading"]
    text: str


class TextSubheading(WhatsAppBaseModel):
    """Subheading text component."""

    type: Literal["TextSubheading"]
    text: str


class TextBody(WhatsAppBaseModel):
    """Body text component with optional font weight."""

    type: Literal["TextBody"]
    text: str
    font_weight: Optional[str] = Field(default=None, alias="font-weight")


class TextCaption(WhatsAppBaseModel):
    """Small caption text component."""

    type: Literal["TextCaption"]
    text: str


# ---------- Input Components ----------
class TextInput(WhatsAppBaseModel):
    """Single-line text input component."""

    type: Literal["TextInput"]
    name: str
    label: str
    input_type: Optional[Literal["text", "email", "phone", "number", "password"]] = (
        Field(default=None, alias="input-type")
    )
    required: Optional[bool] = None
    helper_text: Optional[str] = Field(default=None, alias="helper-text")
    min_chars: Optional[int] = Field(default=None, alias="min-chars")
    max_chars: Optional[int] = Field(default=None, alias="max-chars")
    label_variant: Optional[str] = Field(default=None, alias="label-variant")


class TextArea(WhatsAppBaseModel):
    """Multi-line text input component."""

    type: Literal["TextArea"]
    name: str
    label: str
    required: Optional[bool] = None
    helper_text: Optional[str] = Field(default=None, alias="helper-text")


# ---------- Selection Components ----------
class Dropdown(WhatsAppBaseModel):
    """Dropdown selector component."""

    type: Literal["Dropdown"]
    name: str
    label: str
    required: Optional[bool] = None
    data_source: List[DataSourceItem] = Field(alias="data-source")


class ChipsSelector(WhatsAppBaseModel):
    """Multi-select chips component. Always returns an array."""

    type: Literal["ChipsSelector"]
    name: str
    label: str
    description: Optional[str] = None
    max_selected_items: Optional[int] = Field(default=None, alias="max-selected-items")
    required: Optional[bool] = None
    data_source: List[DataSourceItem] = Field(alias="data-source")


class RadioButtonsGroup(WhatsAppBaseModel):
    """Radio button group for single selection. Returns a string value."""

    type: Literal["RadioButtonsGroup"]
    name: str
    label: str
    description: Optional[str] = None
    required: Optional[bool] = None
    data_source: List[DataSourceItem] = Field(alias="data-source")


class OptIn(WhatsAppBaseModel):
    """Opt-in checkbox component. Returns a boolean."""

    type: Literal["OptIn"]
    name: str
    label: str
    required: Optional[bool] = None
    on_click_action: Optional[OpenUrlAction] = Field(
        default=None, alias="on-click-action"
    )


class CheckboxGroup(WhatsAppBaseModel):
    """Checkbox group for multiple selections."""

    type: Literal["CheckboxGroup"]
    name: str
    label: str
    description: Optional[str] = None
    required: Optional[bool] = None
    min_selected_items: Optional[int] = Field(default=None, alias="min-selected-items")
    max_selected_items: Optional[int] = Field(default=None, alias="max-selected-items")
    data_source: List[DataSourceItem] = Field(alias="data-source")


# ---------- Date Picker ----------
class CalendarPicker(WhatsAppBaseModel):
    """Calendar-based date picker component."""

    type: Literal["CalendarPicker"]
    name: str
    label: str
    helper_text: Optional[str] = Field(default=None, alias="helper-text")
    required: Optional[bool] = None
    mode: Optional[Literal["single", "range"]] = None
    min_date: Optional[str] = Field(default=None, alias="min-date")
    max_date: Optional[str] = Field(default=None, alias="max-date")
    unavailable_dates: Optional[List[str]] = Field(
        default=None, alias="unavailable-dates"
    )
    include_days: Optional[List[str]] = Field(default=None, alias="include-days")


class DatePicker(WhatsAppBaseModel):
    """Simple date picker component."""

    type: Literal["DatePicker"]
    name: str
    label: str
    helper_text: Optional[str] = Field(default=None, alias="helper-text")
    required: Optional[bool] = None
    min_date: Optional[str] = Field(default=None, alias="min-date")
    max_date: Optional[str] = Field(default=None, alias="max-date")
    unavailable_dates: Optional[List[str]] = Field(
        default=None, alias="unavailable-dates"
    )


# ---------- Media Components ----------
class Image(WhatsAppBaseModel):
    """Image display component."""

    type: Literal["Image"]
    src: str
    width: Optional[int] = None
    height: Optional[int] = None
    scale_type: Optional[str] = Field(default=None, alias="scale-type")
    aspect_ratio: Optional[float] = Field(default=None, alias="aspect-ratio")
    alt_text: Optional[str] = Field(default=None, alias="alt-text")


class PhotoPicker(WhatsAppBaseModel):
    """Photo upload component. Cannot be passed in navigate payload."""

    type: Literal["PhotoPicker"]
    name: str
    label: str
    description: Optional[str] = None
    photo_source: Literal["camera", "gallery", "camera_gallery"] = Field(
        alias="photo-source"
    )
    min_uploaded_photos: Optional[int] = Field(
        default=None, alias="min-uploaded-photos"
    )
    max_uploaded_photos: Optional[int] = Field(
        default=None, alias="max-uploaded-photos"
    )
    max_file_size_kb: Optional[int] = Field(default=None, alias="max-file-size-kb")


class DocumentPicker(WhatsAppBaseModel):
    """Document upload component. Cannot be passed in navigate payload."""

    type: Literal["DocumentPicker"]
    name: str
    label: str
    description: Optional[str] = None
    min_uploaded_documents: Optional[int] = Field(
        default=None, alias="min-uploaded-documents"
    )
    max_uploaded_documents: Optional[int] = Field(
        default=None, alias="max-uploaded-documents"
    )
    max_file_size_kb: Optional[int] = Field(default=None, alias="max-file-size-kb")
    allowed_mime_types: Optional[List[str]] = Field(
        default=None, alias="allowed-mime-types"
    )


# ---------- Navigation Components ----------
class Footer(WhatsAppBaseModel):
    """Screen footer with navigation or complete action."""

    type: Literal["Footer"]
    label: str
    enabled: Optional[bool] = None
    on_click_action: Union[NavigateAction, CompleteAction] = Field(
        alias="on-click-action"
    )


class Button(WhatsAppBaseModel):
    """Action button component."""

    type: Literal["Button"]
    label: str
    on_click_action: Union[NavigateAction, CompleteAction] = Field(
        alias="on-click-action"
    )


class EmbeddedLink(WhatsAppBaseModel):
    """Inline link component."""

    type: Literal["EmbeddedLink"]
    text: str
    on_click_action: OpenUrlAction = Field(alias="on-click-action")


# ---------- All Base Components ----------
BaseComponent = Union[
    TextHeading,
    TextSubheading,
    TextBody,
    TextCaption,
    TextInput,
    TextArea,
    Dropdown,
    ChipsSelector,
    RadioButtonsGroup,
    OptIn,
    CheckboxGroup,
    CalendarPicker,
    DatePicker,
    Image,
    PhotoPicker,
    DocumentPicker,
    Footer,
    Button,
    EmbeddedLink,
]


# Forward declaration for recursive types
Component = Union[BaseComponent, "IfConditional", "SwitchConditional", "Form"]


# ---------- Conditional: If ----------
class IfConditional(WhatsAppBaseModel):
    """Conditional rendering based on a condition expression."""

    type: Literal["If"]
    condition: str
    then: List["Component"]
    else_: Optional[List["Component"]] = Field(default=None, alias="else")


# ---------- Conditional: Switch ----------
class SwitchConditional(WhatsAppBaseModel):
    """Switch/case conditional rendering."""

    type: Literal["Switch"]
    value: str
    cases: Dict[str, List["Component"]]


# ---------- Form Container ----------
class Form(WhatsAppBaseModel):
    """Form container component."""

    type: Literal["Form"]
    name: str
    children: List["Component"]
    init_values: Optional[Dict[str, Any]] = Field(default=None, alias="init-values")
    error_messages: Optional[Dict[str, Any]] = Field(
        default=None, alias="error-messages"
    )


# Now that Form/If/Switch are defined, finalize Component union
Component = Union[BaseComponent, IfConditional, SwitchConditional, Form]


# ---------- Layout ----------
class SingleColumnLayout(WhatsAppBaseModel):
    """Single column layout for screens."""

    type: Literal["SingleColumnLayout"]
    children: List[Component]


# ---------- Screen ----------
class Screen(WhatsAppBaseModel):
    """A screen in the WhatsApp Flow."""

    id: str
    title: str
    terminal: Optional[bool] = None
    success: Optional[bool] = None
    data: Optional[Dict[str, Any]] = None
    layout: SingleColumnLayout


# ---------- Routing Model ----------
RoutingModel = Dict[str, List[str]]


# ---------- Main Flow Schema ----------
class WhatsAppFlow(WhatsAppBaseModel):
    """Complete WhatsApp Flow definition."""

    version: str
    routing_model: Optional[RoutingModel] = None
    screens: List[Screen]


def validate_flow(flow: Any) -> dict[str, Any]:
    """Validate an arbitrary object as a WhatsAppFlow.

    Args:
        flow: Dictionary or object to validate as a WhatsApp Flow.

    Returns:
        A dict with either:
        - {"success": True, "data": WhatsAppFlow} on success
        - {"success": False, "error": ValidationError} on failure
    """
    try:
        data = WhatsAppFlow.model_validate(flow)
        return {"success": True, "data": data}
    except ValidationError as exc:
        return {"success": False, "error": exc}


# Type aliases for external use
WhatsAppScreen = Screen
WhatsAppComponent = Component
WhatsAppDataSourceItem = DataSourceItem
