"""Digital Menu Catalog aggregates — Phase 12.1.

Catalog depth for digital menus: allergens, modifiers, availability windows,
media refs (Storage refs only), and multi-language shells.
POS / kitchen / ordering engines are out of scope.
"""
from __future__ import annotations

import uuid

from sqlalchemy import (
    Boolean,
    Index,
    Integer,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.core.database import Base
from app.models.base import (
    ActorAuditMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import (
    AvailabilityScope,
    GUID,
    LifecycleStatus,
    LocalizationTarget,
    MediaKind,
    ModifierSelectionMode,
)


class Allergen(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Reusable allergen catalog entry for a venue."""

    __tablename__ = "allergens"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    icon_ref: Mapped[str | None] = mapped_column(String(255), nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "code", name="uq_allergens_tenant_code"
        ),
        Index("ix_allergens_venue", "tenant_id", "venue_id"),
        Index("ix_allergens_tenant_deleted", "tenant_id", "is_deleted"),
    )


class ModifierGroup(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Modifier group (size, extras, toppings) — selection rules only, no POS engine."""

    __tablename__ = "modifier_groups"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    selection_mode: Mapped[ModifierSelectionMode] = mapped_column(
        default=ModifierSelectionMode.SINGLE, nullable=False
    )
    min_select: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    max_select: Mapped[int | None] = mapped_column(Integer, nullable=True)
    is_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "code", name="uq_modifier_groups_tenant_code"
        ),
        Index("ix_modifier_groups_venue", "tenant_id", "venue_id"),
        Index("ix_modifier_groups_tenant_deleted", "tenant_id", "is_deleted"),
    )


class ModifierOption(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Option inside a modifier group with optional price delta (config, not Accounting)."""

    __tablename__ = "modifier_options"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    modifier_group_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    price_delta: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "modifier_group_id",
            "code",
            name="uq_modifier_options_tenant_code",
        ),
        Index("ix_modifier_options_group", "tenant_id", "modifier_group_id"),
        Index("ix_modifier_options_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuItemModifier(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Links a menu item to a modifier group (UUID refs only)."""

    __tablename__ = "menu_item_modifiers"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    menu_item_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    modifier_group_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_required_override: Mapped[bool | None] = mapped_column(Boolean, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "menu_item_id",
            "modifier_group_id",
            name="uq_menu_item_modifiers_link",
        ),
        Index("ix_menu_item_modifiers_item", "tenant_id", "menu_item_id"),
        Index("ix_menu_item_modifiers_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuItemAllergen(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Links a menu item to an allergen."""

    __tablename__ = "menu_item_allergens"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    menu_item_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    allergen_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    is_contains: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    is_may_contain: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "menu_item_id",
            "allergen_id",
            name="uq_menu_item_allergens_link",
        ),
        Index("ix_menu_item_allergens_item", "tenant_id", "menu_item_id"),
        Index("ix_menu_item_allergens_tenant_deleted", "tenant_id", "is_deleted"),
    )


class AvailabilityWindow(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Availability window for menu / category / item (catalog only)."""

    __tablename__ = "availability_windows"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    scope: Mapped[AvailabilityScope] = mapped_column(nullable=False)
    target_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    # 0=Monday … 6=Sunday; null means every day
    weekday: Mapped[int | None] = mapped_column(Integer, nullable=True)
    start_time: Mapped[str] = mapped_column(String(8), nullable=False)  # HH:MM:SS
    end_time: Mapped[str] = mapped_column(String(8), nullable=False)
    timezone: Mapped[str] = mapped_column(String(64), default="Asia/Tehran", nullable=False)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "venue_id",
            "scope",
            "target_id",
            "code",
            name="uq_availability_windows_tenant_code",
        ),
        Index("ix_availability_windows_target", "tenant_id", "scope", "target_id"),
        Index("ix_availability_windows_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuMediaRef(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Media reference shell — stores Storage file refs only (no binaries)."""

    __tablename__ = "menu_media_refs"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    menu_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    menu_item_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    kind: Mapped[MediaKind] = mapped_column(default=MediaKind.IMAGE, nullable=False)
    file_ref: Mapped[str] = mapped_column(String(255), nullable=False)
    alt_text: Mapped[str | None] = mapped_column(String(255), nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        Index("ix_menu_media_refs_menu", "tenant_id", "menu_id"),
        Index("ix_menu_media_refs_item", "tenant_id", "menu_item_id"),
        Index("ix_menu_media_refs_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuLocalization(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Multi-language shell for menu catalog entities."""

    __tablename__ = "menu_localizations"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    target_type: Mapped[LocalizationTarget] = mapped_column(nullable=False)
    target_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    locale: Mapped[str] = mapped_column(String(16), nullable=False)
    name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "target_type",
            "target_id",
            "locale",
            name="uq_menu_localizations_locale",
        ),
        Index("ix_menu_localizations_target", "tenant_id", "target_type", "target_id"),
        Index("ix_menu_localizations_tenant_deleted", "tenant_id", "is_deleted"),
    )
