# at top of persist_snapshot.py:
from datetime import datetime
from typing import Optional, List
from sqlalchemy.orm import Mapped, mapped_column, relationship

from typing import List, Optional

from sqlalchemy import (
    String, Text, Integer, ForeignKey, DateTime, Boolean, Numeric,
    UniqueConstraint, Index, func
)

from shared.db.base import Base  # you already import Base in app.py



class Product(Base):
    __tablename__ = "products"

    id: Mapped[int] = mapped_column(primary_key=True)
    slug: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)

    title: Mapped[Optional[str]] = mapped_column(String(512))
    image: Mapped[Optional[str]] = mapped_column(Text)

    description_short: Mapped[Optional[str]] = mapped_column(Text)
    description_html: Mapped[Optional[str]] = mapped_column(Text)

    suma_href: Mapped[Optional[str]] = mapped_column(Text)
    brand: Mapped[Optional[str]] = mapped_column(String(255))

    rrp: Mapped[Optional[float]] = mapped_column(Numeric(12, 2))
    rrp_currency: Mapped[Optional[str]] = mapped_column(String(16))
    rrp_raw: Mapped[Optional[str]] = mapped_column(String(128))

    price_per_unit: Mapped[Optional[float]] = mapped_column(Numeric(12, 4))
    price_per_unit_currency: Mapped[Optional[str]] = mapped_column(String(16))
    price_per_unit_raw: Mapped[Optional[str]] = mapped_column(String(128))

    special_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2))
    special_price_currency: Mapped[Optional[str]] = mapped_column(String(16))
    special_price_raw: Mapped[Optional[str]] = mapped_column(String(128))

    regular_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2))
    regular_price_currency: Mapped[Optional[str]] = mapped_column(String(16))
    regular_price_raw: Mapped[Optional[str]] = mapped_column(String(128))

    oe_list_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2))

    case_size_count: Mapped[Optional[int]] = mapped_column(Integer)
    case_size_item_qty: Mapped[Optional[float]] = mapped_column(Numeric(12, 3))
    case_size_item_unit: Mapped[Optional[str]] = mapped_column(String(32))
    case_size_raw: Mapped[Optional[str]] = mapped_column(String(128))

    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(),
    )
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    images: Mapped[List["ProductImage"]] = relationship(
        back_populates="product",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    sections: Mapped[List["ProductSection"]] = relationship(
        back_populates="product",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    labels: Mapped[List["ProductLabel"]] = relationship(
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    stickers: Mapped[List["ProductSticker"]] = relationship(
        cascade="all, delete-orphan",
        passive_deletes=True,
    )

    ean: Mapped[Optional[str]] = mapped_column(String(64))
    sku: Mapped[Optional[str]] = mapped_column(String(128))
    unit_size: Mapped[Optional[str]] = mapped_column(String(128))
    pack_size: Mapped[Optional[str]] = mapped_column(String(128))

    attributes = relationship(
        "ProductAttribute",
        back_populates="product",
        lazy="selectin",
        cascade="all, delete-orphan",
    )
    nutrition = relationship(
        "ProductNutrition",
        back_populates="product",
        lazy="selectin",
        cascade="all, delete-orphan",
    )
    allergens = relationship(
        "ProductAllergen",
        back_populates="product",
        lazy="selectin",
        cascade="all, delete-orphan",
    )

    # cart_items and order_items live in a separate domain (cart DB)
    # — cross-domain relationships removed


class ProductImage(Base):
    __tablename__ = "product_images"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    url: Mapped[str] = mapped_column(Text, nullable=False)
    position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    kind: Mapped[str] = mapped_column(String(16), nullable=False, default="gallery")
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    product: Mapped["Product"] = relationship(back_populates="images")

    __table_args__ = (
        UniqueConstraint("product_id", "url", "kind", name="uq_product_images_product_url_kind"),
        Index("ix_product_images_position", "position"),
    )

class ProductSection(Base):
    __tablename__ = "product_sections"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(
        ForeignKey("products.id", ondelete="CASCADE"),
        index=True,
        nullable=False,
    )
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    html: Mapped[str] = mapped_column(Text, nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    product: Mapped["Product"] = relationship(back_populates="sections")
    __table_args__ = (
        UniqueConstraint("product_id", "title", name="uq_product_sections_product_title"),
    )
# --- Nav & listings ---

class NavTop(Base):
    __tablename__ = "nav_tops"
    id: Mapped[int] = mapped_column(primary_key=True)
    label: Mapped[str] = mapped_column(String(255), nullable=False)
    slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    market_id: Mapped[Optional[int]] = mapped_column(
        Integer,
        ForeignKey("market_places.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    listings: Mapped[List["Listing"]] = relationship(back_populates="top", cascade="all, delete-orphan")
    market = relationship("MarketPlace", back_populates="nav_tops")

    __table_args__ = (UniqueConstraint("label", "slug", name="uq_nav_tops_label_slug"),)

class NavSub(Base):
    __tablename__ = "nav_subs"
    id: Mapped[int] = mapped_column(primary_key=True)
    top_id: Mapped[int] = mapped_column(ForeignKey("nav_tops.id", ondelete="CASCADE"), index=True, nullable=False)
    label: Mapped[Optional[str]] = mapped_column(String(255))
    slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    href: Mapped[Optional[str]] = mapped_column(Text)
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    listings: Mapped[List["Listing"]] = relationship(back_populates="sub", cascade="all, delete-orphan")

    __table_args__ = (UniqueConstraint("top_id", "slug", name="uq_nav_subs_top_slug"),)

class Listing(Base):
    __tablename__ = "listings"

    id: Mapped[int] = mapped_column(primary_key=True)

    # Old slug-based fields (optional: remove)
    # top_slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    # sub_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True)

    top_id: Mapped[int] = mapped_column(ForeignKey("nav_tops.id", ondelete="CASCADE"), index=True, nullable=False)
    sub_id: Mapped[Optional[int]] = mapped_column(ForeignKey("nav_subs.id", ondelete="CASCADE"), index=True)

    total_pages: Mapped[Optional[int]] = mapped_column(Integer)
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))


    top: Mapped["NavTop"] = relationship(back_populates="listings")
    sub: Mapped[Optional["NavSub"]] = relationship(back_populates="listings")

    __table_args__ = (
        UniqueConstraint("top_id", "sub_id", name="uq_listings_top_sub"),
    )

class ListingItem(Base):
    __tablename__ = "listing_items"
    id: Mapped[int] = mapped_column(primary_key=True)
    listing_id: Mapped[int] = mapped_column(ForeignKey("listings.id", ondelete="CASCADE"), index=True, nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    __table_args__ = (UniqueConstraint("listing_id", "slug", name="uq_listing_items_listing_slug"),)

# --- Reports / redirects / logs ---

class LinkError(Base):
    __tablename__ = "link_errors"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True)
    href: Mapped[Optional[str]] = mapped_column(Text)
    text: Mapped[Optional[str]] = mapped_column(Text)
    top: Mapped[Optional[str]] = mapped_column(String(255))
    sub: Mapped[Optional[str]] = mapped_column(String(255))
    target_slug: Mapped[Optional[str]] = mapped_column(String(255))
    type: Mapped[Optional[str]] = mapped_column(String(255))
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

class LinkExternal(Base):
    __tablename__ = "link_externals"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True)
    href: Mapped[Optional[str]] = mapped_column(Text)
    text: Mapped[Optional[str]] = mapped_column(Text)
    host: Mapped[Optional[str]] = mapped_column(String(255))
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

class SubcategoryRedirect(Base):
    __tablename__ = "subcategory_redirects"
    id: Mapped[int] = mapped_column(primary_key=True)
    old_path: Mapped[str] = mapped_column(String(512), nullable=False, index=True)
    new_path: Mapped[str] = mapped_column(String(512), nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

class ProductLog(Base):
    __tablename__ = "product_logs"
    id: Mapped[int] = mapped_column(primary_key=True)
    slug: Mapped[Optional[str]] = mapped_column(String(255), index=True)
    href_tried: Mapped[Optional[str]] = mapped_column(Text)
    ok: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false")
    error_type: Mapped[Optional[str]] = mapped_column(String(255))
    error_message: Mapped[Optional[str]] = mapped_column(Text)
    http_status: Mapped[Optional[int]] = mapped_column(Integer)
    final_url: Mapped[Optional[str]] = mapped_column(Text)
    transport_error: Mapped[Optional[bool]] = mapped_column(Boolean)
    title: Mapped[Optional[str]] = mapped_column(String(512))
    has_description_html: Mapped[Optional[bool]] = mapped_column(Boolean)
    has_description_short: Mapped[Optional[bool]] = mapped_column(Boolean)
    sections_count: Mapped[Optional[int]] = mapped_column(Integer)
    images_count: Mapped[Optional[int]] = mapped_column(Integer)
    embedded_images_count: Mapped[Optional[int]] = mapped_column(Integer)
    all_images_count: Mapped[Optional[int]] = mapped_column(Integer)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())



# ...existing models...

class ProductLabel(Base):
    __tablename__ = "product_labels"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    product: Mapped["Product"] = relationship(back_populates="labels")

    __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_labels_product_name"),)

class ProductSticker(Base):
    __tablename__ = "product_stickers"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    product: Mapped["Product"] = relationship(back_populates="stickers")

    __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_stickers_product_name"),)

class ProductAttribute(Base):
    __tablename__ = "product_attributes"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    key: Mapped[str] = mapped_column(String(255), nullable=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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    value: Mapped[Optional[str]] = mapped_column(Text)
    product = relationship("Product", back_populates="attributes")
    __table_args__ = (UniqueConstraint("product_id", "key", name="uq_product_attributes_product_key"),)

class ProductNutrition(Base):
    __tablename__ = "product_nutrition"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    key: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[Optional[str]] = mapped_column(String(255))
    unit: Mapped[Optional[str]] = mapped_column(String(64))
    product = relationship("Product", back_populates="nutrition")
    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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    __table_args__ = (UniqueConstraint("product_id", "key", name="uq_product_nutrition_product_key"),)

class ProductAllergen(Base):
    __tablename__ = "product_allergens"
    id: Mapped[int] = mapped_column(primary_key=True)
    product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    contains: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="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())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    product: Mapped["Product"] = relationship(back_populates="allergens")
    __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_allergens_product_name"),)


class CartItem(Base):
    __tablename__ = "cart_items"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)

    # Either a logged-in user OR an anonymous session
    user_id: Mapped[int | None] = mapped_column(
        Integer,
        nullable=True,
    )
    session_id: Mapped[str | None] = mapped_column(
        String(128),
        nullable=True,
    )

    product_id: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
    )

    quantity: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=1,
        server_default="1",
    )

    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(),
    )
    market_place_id: Mapped[int | None] = mapped_column(
        Integer,
        nullable=True,
        index=True,
    )

    deleted_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True),
        nullable=True,
    )

    # Denormalized product data (snapshotted at write time)
    product_title: Mapped[str | None] = mapped_column(String(512), nullable=True)
    product_slug: Mapped[str | None] = mapped_column(String(512), nullable=True)
    product_image: Mapped[str | None] = mapped_column(Text, nullable=True)
    product_brand: Mapped[str | None] = mapped_column(String(255), nullable=True)
    product_regular_price: Mapped[float | None] = mapped_column(Numeric(12, 2), nullable=True)
    product_special_price: Mapped[float | None] = mapped_column(Numeric(12, 2), nullable=True)
    product_price_currency: Mapped[str | None] = mapped_column(String(16), nullable=True)

    # Denormalized marketplace data (snapshotted at write time)
    market_place_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    market_place_container_id: Mapped[int | None] = mapped_column(Integer, nullable=True)

    __table_args__ = (
        Index("ix_cart_items_user_product", "user_id", "product_id"),
        Index("ix_cart_items_session_product", "session_id", "product_id"),
    )
