from datetime import datetime from enum import Enum from typing import TYPE_CHECKING from sqlalchemy import Boolean, DateTime, Enum as SAEnum, ForeignKey, String, UniqueConstraint, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database.base import Base from app.utils.id_generator import generate_public_id if TYPE_CHECKING: from app.messages.models import Message from app.users.models import User class ChatType(str, Enum): PRIVATE = "private" GROUP = "group" CHANNEL = "channel" class ChatMemberRole(str, Enum): OWNER = "owner" ADMIN = "admin" MEMBER = "member" class Chat(Base): __tablename__ = "chats" id: Mapped[int] = mapped_column(primary_key=True, index=True) public_id: Mapped[str] = mapped_column(String(24), unique=True, index=True, nullable=False, default=generate_public_id) type: Mapped[ChatType] = mapped_column(SAEnum(ChatType), nullable=False, index=True) title: Mapped[str | None] = mapped_column(String(255), nullable=True) avatar_url: Mapped[str | None] = mapped_column(String(512), nullable=True) handle: Mapped[str | None] = mapped_column(String(64), nullable=True, unique=True, index=True) description: Mapped[str | None] = mapped_column(String(512), nullable=True) is_public: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") is_saved: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") pinned_message_id: Mapped[int | None] = mapped_column(ForeignKey("messages.id", ondelete="SET NULL"), nullable=True, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) members: Mapped[list["ChatMember"]] = relationship(back_populates="chat", cascade="all, delete-orphan") messages: Mapped[list["Message"]] = relationship( back_populates="chat", cascade="all, delete-orphan", foreign_keys="Message.chat_id", ) class ChatMember(Base): __tablename__ = "chat_members" __table_args__ = (UniqueConstraint("chat_id", "user_id", name="uq_chat_members_chat_id_user_id"),) id: Mapped[int] = mapped_column(primary_key=True, index=True) chat_id: Mapped[int] = mapped_column(ForeignKey("chats.id", ondelete="CASCADE"), nullable=False, index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) role: Mapped[ChatMemberRole] = mapped_column(SAEnum(ChatMemberRole), nullable=False, default=ChatMemberRole.MEMBER) joined_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) chat: Mapped["Chat"] = relationship(back_populates="members") user: Mapped["User"] = relationship(back_populates="memberships") class ChatNotificationSetting(Base): __tablename__ = "chat_notification_settings" __table_args__ = (UniqueConstraint("chat_id", "user_id", name="uq_chat_notification_settings_chat_user"),) id: Mapped[int] = mapped_column(primary_key=True, index=True) chat_id: Mapped[int] = mapped_column(ForeignKey("chats.id", ondelete="CASCADE"), nullable=False, index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) muted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) class ChatUserSetting(Base): __tablename__ = "chat_user_settings" __table_args__ = (UniqueConstraint("chat_id", "user_id", name="uq_chat_user_settings_chat_user"),) id: Mapped[int] = mapped_column(primary_key=True, index=True) chat_id: Mapped[int] = mapped_column(ForeignKey("chats.id", ondelete="CASCADE"), nullable=False, index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") pinned: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") pinned_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) class ChatInviteLink(Base): __tablename__ = "chat_invite_links" __table_args__ = ( UniqueConstraint("token", name="uq_chat_invite_links_token"), ) id: Mapped[int] = mapped_column(primary_key=True, index=True) chat_id: Mapped[int] = mapped_column(ForeignKey("chats.id", ondelete="CASCADE"), nullable=False, index=True) creator_user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) token: Mapped[str] = mapped_column(String(64), nullable=False, index=True) is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default="true") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) class ChatBan(Base): __tablename__ = "chat_bans" __table_args__ = (UniqueConstraint("chat_id", "user_id", name="uq_chat_bans_chat_user"),) id: Mapped[int] = mapped_column(primary_key=True, index=True) chat_id: Mapped[int] = mapped_column(ForeignKey("chats.id", ondelete="CASCADE"), nullable=False, index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) banned_by_user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)