from __future__ import annotations from datetime import datetime, timezone from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from .database import Base def utcnow() -> datetime: return datetime.now(timezone.utc) class UserSession(Base): __tablename__ = "user_sessions" id: Mapped[int] = mapped_column(primary_key=True) session_token: Mapped[str] = mapped_column(String(128), unique=True, index=True) provider: Mapped[str] = mapped_column(String(32), default="teamsnap") external_user_id: Mapped[str | None] = mapped_column(String(128), index=True) external_team_id: Mapped[str | None] = mapped_column(String(128), index=True) external_player_id: Mapped[str | None] = mapped_column(String(128), index=True) access_token: Mapped[str | None] = mapped_column(Text()) refresh_token: Mapped[str | None] = mapped_column(Text()) token_expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) is_admin: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow) class AudioAsset(Base): __tablename__ = "audio_assets" id: Mapped[int] = mapped_column(primary_key=True) external_team_id: Mapped[str] = mapped_column(String(128), index=True) owner_external_player_id: Mapped[str] = mapped_column(String(128), index=True) uploaded_by_session_id: Mapped[int | None] = mapped_column(ForeignKey("user_sessions.id")) title: Mapped[str] = mapped_column(String(255)) original_filename: Mapped[str] = mapped_column(String(255)) mime_type: Mapped[str] = mapped_column(String(128)) size_bytes: Mapped[int] = mapped_column(Integer) storage_path: Mapped[str] = mapped_column(String(512)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) uploaded_by: Mapped[UserSession | None] = relationship() clips: Mapped[list[AudioClip]] = relationship(back_populates="asset", cascade="all, delete-orphan") class AudioClip(Base): __tablename__ = "audio_clips" id: Mapped[int] = mapped_column(primary_key=True) asset_id: Mapped[int] = mapped_column(ForeignKey("audio_assets.id"), index=True) label: Mapped[str] = mapped_column(String(255)) start_ms: Mapped[int] = mapped_column(Integer) end_ms: Mapped[int] = mapped_column(Integer) normalization_status: Mapped[str] = mapped_column(String(32), default="pending") normalized_path: Mapped[str | None] = mapped_column(String(512)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) asset: Mapped[AudioAsset] = relationship(back_populates="clips") class GameAssignment(Base): __tablename__ = "game_assignments" __table_args__ = ( UniqueConstraint("external_game_id", "external_player_id", "clip_id", name="uq_game_assignment_player_clip"), ) id: Mapped[int] = mapped_column(primary_key=True) external_team_id: Mapped[str] = mapped_column(String(128), index=True) external_game_id: Mapped[str] = mapped_column(String(128), index=True) external_player_id: Mapped[str] = mapped_column(String(128), index=True) clip_id: Mapped[int] = mapped_column(ForeignKey("audio_clips.id"), index=True) batting_slot: Mapped[int | None] = mapped_column(Integer) status: Mapped[str] = mapped_column(String(32), default="ready") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow) clip: Mapped[AudioClip] = relationship() class PlaybackSession(Base): __tablename__ = "playback_sessions" id: Mapped[int] = mapped_column(primary_key=True) external_team_id: Mapped[str] = mapped_column(String(128), index=True) external_game_id: Mapped[str] = mapped_column(String(128), index=True) operator_session_id: Mapped[int | None] = mapped_column(ForeignKey("user_sessions.id")) current_assignment_id: Mapped[int | None] = mapped_column(ForeignKey("game_assignments.id")) state: Mapped[str] = mapped_column(String(32), default="idle") last_triggered_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow) operator_session: Mapped[UserSession | None] = relationship() current_assignment: Mapped[GameAssignment | None] = relationship()