"""Places and shop places (Stream K2; plan W19). A place is where a shop is, of any kind: an
airport, a mall, a port, a ship that moves, a border crossing; a kind is a registry entry in
`services/places.py`, never a migration. A shop has one primary place (the comparison unit:
shops counted once per primary place) unless its kind says otherwise (the online catalogue).

Sources of truth: this module, `services/places.py`, `alembic/versions/d2e3f4a5b6c7_*.py`.
"""

from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import BigInteger, Boolean, CheckConstraint, DateTime, ForeignKey, Index, Integer, PrimaryKeyConstraint, String, \
    UniqueConstraint, Uuid, func, text
from sqlalchemy.orm import Mapped, mapped_column

from app.models.accounts import JSONVariant
from app.models.base import Base

BigId = BigInteger().with_variant(Integer(), "sqlite")


class Place(Base):
    __tablename__ = "places"
    __table_args__ = (
        UniqueConstraint("slug", name="uq_places_slug"),
        UniqueConstraint("uid", name="uq_places_uid"),
        Index("ix_places_kind", "kind"),
        Index("ix_places_parent_id", "parent_id"),
    )

    id: Mapped[int] = mapped_column(BigId, primary_key=True)
    uid: Mapped[uuid.UUID] = mapped_column(Uuid(as_uuid=True), nullable=False, default=uuid.uuid4)
    slug: Mapped[str] = mapped_column(String(240), nullable=False)
    kind: Mapped[str] = mapped_column(String(24), nullable=False)
    name: Mapped[str] = mapped_column(String(200), nullable=False)
    parent_id: Mapped[int | None] = mapped_column(BigId, ForeignKey("places.id"))
    #: An open list of `{scheme, value}` (IATA, UN/LOCODE, none), never a column per scheme.
    identifiers: Mapped[list] = mapped_column(JSONVariant, nullable=False, default=list, server_default="[]")
    attributes: Mapped[dict] = mapped_column(JSONVariant, nullable=False, default=dict, server_default="{}")
    hidden: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))
    indexed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())


class ShopPlace(Base):
    __tablename__ = "shop_places"
    __table_args__ = (
        PrimaryKeyConstraint("shop_id", "place_id", name="pk_shop_places"),
        CheckConstraint("role IN ('primary', 'also')", name="ck_shop_places_role"),
        Index("ix_shop_places_place", "place_id"),
    )

    shop_id: Mapped[int] = mapped_column(ForeignKey("shops.id"), nullable=False)
    place_id: Mapped[int] = mapped_column(BigId, ForeignKey("places.id"), nullable=False)
    role: Mapped[str] = mapped_column(String(16), nullable=False)
