import uuid from datetime import datetime from sqlalchemy import String, Text, DateTime, Integer, ForeignKey, JSON, Enum as SAEnum from sqlalchemy.orm import Mapped, mapped_column, relationship from app.models.database import Base, gen_uuid from app.models.schemas import GamePhase, CharacterRole, CharacterStatus, ClueType, ClueVisibility class Script(Base): __tablename__ = "scripts" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) title: Mapped[str] = mapped_column(String(200), nullable=False) background: Mapped[str] = mapped_column(Text, default="") raw_content: Mapped[str] = mapped_column(Text, default="") parsed_data: Mapped[dict] = mapped_column(JSON, default=dict) character_count: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) characters: Mapped[list["Character"]] = relationship(back_populates="script", cascade="all, delete-orphan") clues: Mapped[list["Clue"]] = relationship(back_populates="script", cascade="all, delete-orphan") class Character(Base): __tablename__ = "characters" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) script_id: Mapped[str] = mapped_column(String(36), ForeignKey("scripts.id"), nullable=False) name: Mapped[str] = mapped_column(String(100), nullable=False) role: Mapped[CharacterRole] = mapped_column(SAEnum(CharacterRole), default=CharacterRole.SUSPECT) status: Mapped[CharacterStatus] = mapped_column(SAEnum(CharacterStatus), default=CharacterStatus.ALIVE) personality: Mapped[str] = mapped_column(Text, default="") speaking_style: Mapped[str] = mapped_column(Text, default="") background: Mapped[str] = mapped_column(Text, default="") secret: Mapped[str] = mapped_column(Text, default="") motive: Mapped[str] = mapped_column(Text, default="") avatar_url: Mapped[str] = mapped_column(String(500), default="") hermes_profile: Mapped[str] = mapped_column(String(100), default="") soul_md: Mapped[str] = mapped_column(Text, default="") knowledge_base: Mapped[str] = mapped_column(Text, default="") is_revealed_killer: Mapped[bool] = mapped_column(default=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) script: Mapped["Script"] = relationship(back_populates="characters") messages: Mapped[list["Message"]] = relationship(back_populates="character", cascade="all, delete-orphan") class Message(Base): __tablename__ = "messages" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) session_id: Mapped[str] = mapped_column(String(36), default="") character_id: Mapped[str] = mapped_column(String(36), ForeignKey("characters.id"), nullable=True) game_phase: Mapped[str] = mapped_column(String(50), default="") msg_type: Mapped[str] = mapped_column(String(50), default="character") content: Mapped[str] = mapped_column(Text, nullable=False) target_character_id: Mapped[str] = mapped_column(String(36), nullable=True) clue_id: Mapped[str] = mapped_column(String(36), nullable=True) metadata: Mapped[dict] = mapped_column(JSON, default=dict) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) character: Mapped["Character"] = relationship(back_populates="messages") class Clue(Base): __tablename__ = "clues" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) script_id: Mapped[str] = mapped_column(String(36), ForeignKey("scripts.id"), nullable=False) name: Mapped[str] = mapped_column(String(200), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) clue_type: Mapped[ClueType] = mapped_column(SAEnum(ClueType), default=ClueType.PHYSICAL) owner_id: Mapped[str] = mapped_column(String(36), nullable=True) phase: Mapped[str] = mapped_column(String(50), default="round1_search") visibility: Mapped[ClueVisibility] = mapped_column(SAEnum(ClueVisibility), default=ClueVisibility.ALL) visible_to: Mapped[list] = mapped_column(JSON, default=list) is_unlocked: Mapped[bool] = mapped_column(default=False) unlocked_by: Mapped[str] = mapped_column(String(36), nullable=True) unlocked_at: Mapped[datetime] = mapped_column(DateTime, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) script: Mapped["Script"] = relationship(back_populates="clues") class GameState(Base): __tablename__ = "game_state" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) current_phase: Mapped[str] = mapped_column(String(50), default="intro") current_speaker_id: Mapped[str] = mapped_column(String(36), nullable=True) speaker_order: Mapped[list] = mapped_column(JSON, default=list) active_script_id: Mapped[str] = mapped_column(String(36), nullable=True) is_running: Mapped[bool] = mapped_column(default=False) is_paused: Mapped[bool] = mapped_column(default=False) phase_started_at: Mapped[datetime] = mapped_column(DateTime, nullable=True) config: Mapped[dict] = mapped_column(JSON, default=dict) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class Vote(Base): __tablename__ = "votes" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) voter_id: Mapped[str] = mapped_column(String(36), nullable=False) target_id: Mapped[str] = mapped_column(String(36), nullable=False) round_number: Mapped[int] = mapped_column(Integer, default=1) reason: Mapped[str] = mapped_column(Text, default="") created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)