"""Messaging models: Conversation (per listing, per buyer/seller pair) + Message. One conversation per (listing, buyer) pair — enforced by unique constraint. The seller is always listing.user; buyer is any other authenticated user. Messages are append-only; soft-delete not needed at Phase 3. """ from datetime import datetime from app.utils.time import utcnow from app.extensions import db class Conversation(db.Model): __tablename__ = "conversations" __table_args__ = ( db.UniqueConstraint("listing_id", "buyer_id", name="uq_conv_listing_buyer"), ) id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True, autoincrement=True) listing_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey("listings.id"), nullable=False, index=True) buyer_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey("users.id"), nullable=False, index=True) seller_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey("users.id"), nullable=False, index=True) last_message_at = db.Column(db.DateTime, nullable=True, index=True) created_at = db.Column(db.DateTime, default=utcnow, nullable=False) listing = db.relationship("Listing", backref=db.backref("conversations", lazy="dynamic")) buyer = db.relationship("User", foreign_keys=[buyer_id], backref=db.backref("bought_conversations", lazy="dynamic")) seller = db.relationship("User", foreign_keys=[seller_id], backref=db.backref("sold_conversations", lazy="dynamic")) messages = db.relationship("Message", back_populates="conversation", order_by="Message.created_at", cascade="all, delete-orphan", lazy="selectin") def other_party(self, user): return self.seller if user.id == self.buyer_id else self.buyer def unread_count(self, user): return sum(1 for m in self.messages if m.sender_id != user.id and m.read_at is None) def __repr__(self): return f"" class Message(db.Model): __tablename__ = "messages" id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True, autoincrement=True) conversation_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey("conversations.id"), nullable=False, index=True) sender_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey("users.id"), nullable=False) body = db.Column(db.Text, nullable=False) read_at = db.Column(db.DateTime, nullable=True) created_at = db.Column(db.DateTime, default=utcnow, nullable=False) conversation = db.relationship("Conversation", back_populates="messages") sender = db.relationship("User", backref=db.backref("sent_messages", lazy="dynamic")) @property def is_read(self): return self.read_at is not None def mark_read(self): if self.read_at is None: self.read_at = utcnow() def __repr__(self): return f""