from __future__ import annotations from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, func from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.utils.ids import prefixed_id class UserPlan(Base): __tablename__ = "user_plans" id: Mapped[str] = mapped_column( String(40), primary_key=True, default=lambda: prefixed_id("plan"), ) user_id: Mapped[str] = mapped_column( String(40), ForeignKey("users.id"), unique=True, index=True, nullable=False, ) selected_plan: Mapped[str] = mapped_column( String(60), default="free", nullable=False, ) status: Mapped[str] = mapped_column( String(40), default="active", nullable=False, ) trial_started_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, ) trial_ends_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, ) # Tracks the start of the current billing period for monthly reset logic period_start: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, ) monthly_video_limit: Mapped[int] = mapped_column(Integer, default=3, nullable=False) monthly_video_used: Mapped[int] = mapped_column(Integer, default=0, nullable=False) monthly_generation_limit: Mapped[int] = mapped_column(Integer, default=30, nullable=False) monthly_generation_used: Mapped[int] = mapped_column(Integer, default=0, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False, ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) # Supabase/Postgres best practices: explicit indexes for common query patterns # (user lookups already have unique+index on user_id; add for plan filtering + period resets) __table_args__ = ( Index("ix_user_plans_selected_plan_status", "selected_plan", "status"), Index("ix_user_plans_period_start", "period_start"), )