"""Hospitality foundation aggregates — Phase 12.0.

Independent aggregates use UUID references within hospitality_db only.
No SQLAlchemy relationship graphs between aggregates.
Venue formats are configurable catalog values — no format-specific engines.
"""
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,
    OptimisticLockMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import (
    AuditAction,
    BundleKey,
    BundleStatus,
    GUID,
    LifecycleStatus,
    MenuStatus,
    TableStatus,
    VenueFormat,
)


class Venue(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    """Root hospitality venue (cafe / restaurant / bakery / …) for a tenant."""

    __tablename__ = "venues"

    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.DRAFT, nullable=False
    )
    venue_format: Mapped[VenueFormat] = mapped_column(
        default=VenueFormat.RESTAURANT, nullable=False
    )
    legal_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    timezone: Mapped[str] = mapped_column(String(64), default="Asia/Tehran", nullable=False)
    language: Mapped[str] = mapped_column(String(16), default="fa", nullable=False)
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    settings: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_venues_tenant_code"),
        Index("ix_venues_tenant_status", "tenant_id", "status"),
        Index("ix_venues_tenant_deleted", "tenant_id", "is_deleted"),
        Index("ix_venues_tenant_format", "tenant_id", "venue_format"),
    )


class Branch(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Unlimited branches / outlets under a venue."""

    __tablename__ = "branches"

    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
    )
    address: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    timezone: Mapped[str | None] = mapped_column(String(64), nullable=True)
    working_hours: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "code", name="uq_branches_tenant_code"
        ),
        Index("ix_branches_venue", "tenant_id", "venue_id"),
        Index("ix_branches_tenant_deleted", "tenant_id", "is_deleted"),
    )


class DiningArea(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Floor / dining area shell for table service."""

    __tablename__ = "dining_areas"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    branch_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    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
    )
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "code", name="uq_dining_areas_tenant_code"
        ),
        Index("ix_dining_areas_venue", "tenant_id", "venue_id"),
        Index("ix_dining_areas_tenant_deleted", "tenant_id", "is_deleted"),
    )


class DiningTable(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Table shell — seating / QR table binding engines come in later phases."""

    __tablename__ = "dining_tables"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    dining_area_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    capacity: Mapped[int] = mapped_column(Integer, default=2, nullable=False)
    status: Mapped[TableStatus] = mapped_column(
        default=TableStatus.AVAILABLE, nullable=False
    )
    qr_token: Mapped[str | None] = mapped_column(String(100), nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "code", name="uq_dining_tables_tenant_code"
        ),
        Index("ix_dining_tables_venue", "tenant_id", "venue_id"),
        Index("ix_dining_tables_area", "tenant_id", "dining_area_id"),
        Index("ix_dining_tables_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Menu(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    """Digital menu shell — catalog depth expands in later phases."""

    __tablename__ = "menus"

    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[MenuStatus] = mapped_column(default=MenuStatus.DRAFT, 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)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "venue_id", "code", name="uq_menus_tenant_code"),
        Index("ix_menus_venue", "tenant_id", "venue_id"),
        Index("ix_menus_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuCategory(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "menu_categories"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    menu_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
    )
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "menu_id", "code", name="uq_menu_categories_tenant_code"
        ),
        Index("ix_menu_categories_menu", "tenant_id", "menu_id"),
        Index("ix_menu_categories_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MenuItem(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Menu item — catalog depth (modifiers/allergens/media) in Phase 12.1."""

    __tablename__ = "menu_items"

    venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    menu_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    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
    )
    base_price: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    is_available: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    attributes: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    # Phase 12.1 catalog fields (additive, backward compatible)
    preparation_minutes: Mapped[int | None] = mapped_column(Integer, nullable=True)
    calories: Mapped[int | None] = mapped_column(Integer, nullable=True)
    spicy_level: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_vegetarian: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_vegan: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_gluten_free: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    tags: Mapped[list | None] = mapped_column(JSON, nullable=True)
    primary_media_ref: Mapped[str | None] = mapped_column(String(255), nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "menu_id", "code", name="uq_menu_items_tenant_code"
        ),
        Index("ix_menu_items_menu", "tenant_id", "menu_id"),
        Index("ix_menu_items_category", "tenant_id", "category_id"),
        Index("ix_menu_items_tenant_deleted", "tenant_id", "is_deleted"),
    )


class BundleDefinition(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Catalog of licensable bundles available to a tenant (feature-based licensing)."""

    __tablename__ = "bundle_definitions"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    bundle_key: Mapped[BundleKey] = mapped_column(nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[BundleStatus] = mapped_column(
        default=BundleStatus.AVAILABLE, nullable=False
    )
    feature_keys: Mapped[list | None] = mapped_column(JSON, nullable=True)
    permission_prefixes: Mapped[list | None] = mapped_column(JSON, nullable=True)
    api_prefixes: Mapped[list | None] = mapped_column(JSON, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_bundle_definitions_tenant_code"),
        UniqueConstraint(
            "tenant_id", "bundle_key", name="uq_bundle_definitions_tenant_key"
        ),
        Index("ix_bundle_definitions_tenant_deleted", "tenant_id", "is_deleted"),
    )


class TenantBundle(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    """Activated bundle for a tenant/venue — hidden features must not expose APIs."""

    __tablename__ = "tenant_bundles"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    bundle_definition_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    bundle_key: Mapped[BundleKey] = mapped_column(nullable=False)
    status: Mapped[BundleStatus] = mapped_column(
        default=BundleStatus.ACTIVE, nullable=False
    )
    activated_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "venue_id",
            "bundle_key",
            name="uq_tenant_bundles_tenant_venue_key",
        ),
        Index("ix_tenant_bundles_status", "tenant_id", "status"),
        Index("ix_tenant_bundles_tenant_deleted", "tenant_id", "is_deleted"),
    )


class FeatureToggle(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Per-tenant/venue feature toggle for capability discovery."""

    __tablename__ = "feature_toggles"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    feature_key: Mapped[str] = mapped_column(String(100), nullable=False)
    enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    bundle_key: Mapped[BundleKey | None] = mapped_column(nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "venue_id",
            "feature_key",
            name="uq_feature_toggles_tenant_venue_key",
        ),
        Index("ix_feature_toggles_enabled", "tenant_id", "enabled"),
        Index("ix_feature_toggles_tenant_deleted", "tenant_id", "is_deleted"),
    )


class HospitalityRole(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "hospitality_roles"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    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
    )
    permission_keys: Mapped[list | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "code", name="uq_hospitality_roles_tenant_code"
        ),
        Index("ix_hospitality_roles_tenant_deleted", "tenant_id", "is_deleted"),
    )


class HospitalityPermission(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "hospitality_permissions"

    code: Mapped[str] = mapped_column(String(100), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    bundle_key: Mapped[BundleKey | None] = mapped_column(nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "code", name="uq_hospitality_permissions_tenant_code"
        ),
        Index("ix_hospitality_permissions_tenant_deleted", "tenant_id", "is_deleted"),
    )


class HospitalityConfiguration(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    __tablename__ = "hospitality_configurations"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    branch_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    config: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "code", name="uq_hospitality_configurations_tenant_code"
        ),
        Index("ix_hospitality_configurations_venue", "tenant_id", "venue_id"),
        Index(
            "ix_hospitality_configurations_tenant_deleted", "tenant_id", "is_deleted"
        ),
    )


class HospitalityEvent(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Domain event shell records (not the outbox bus)."""

    __tablename__ = "hospitality_events"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=False)
    event_type: Mapped[str] = mapped_column(String(100), nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    payload: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        Index("ix_hospitality_events_venue", "tenant_id", "venue_id"),
        Index("ix_hospitality_events_type", "tenant_id", "event_type"),
        Index("ix_hospitality_events_tenant_deleted", "tenant_id", "is_deleted"),
    )


class HospitalitySetting(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "hospitality_settings"

    venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    key: Mapped[str] = mapped_column(String(100), nullable=False)
    value: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "venue_id", "key", name="uq_hospitality_settings_tenant_key"
        ),
        Index("ix_hospitality_settings_tenant_deleted", "tenant_id", "is_deleted"),
    )


class HospitalityAuditLog(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Append-only audit trail — no soft delete."""

    __tablename__ = "hospitality_audit_logs"

    entity_type: Mapped[str] = mapped_column(String(100), nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    action: Mapped[AuditAction] = mapped_column(nullable=False)
    actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    changes: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    message: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_hospitality_audit_entity", "tenant_id", "entity_type", "entity_id"),
        Index("ix_hospitality_audit_actor", "tenant_id", "actor_user_id"),
    )
