# app/modules/messaging/models/message.py
"""
Messaging system database models.

Supports three communication channels:
- Admin <-> Store
- Store <-> Customer
- Admin <-> Customer

Multi-tenant isolation is enforced via store_id for conversations
involving customers.
"""

import enum

from sqlalchemy import (
    Boolean,
    Column,
    DateTime,
    Enum,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.orm import relationship

from app.core.database import Base
from models.database.base import TimestampMixin


class ConversationType(str, enum.Enum):
    """Defines the three supported conversation channels."""

    ADMIN_STORE = "admin_store"
    STORE_CUSTOMER = "store_customer"
    ADMIN_CUSTOMER = "admin_customer"


class ParticipantType(str, enum.Enum):
    """Type of participant in a conversation."""

    ADMIN = "admin"  # Platform admin user (super_admin or platform_admin)
    STORE = "store"  # Store team user (merchant_owner or store_member)
    CUSTOMER = "customer"  # Customer model


def _enum_values(enum_class):
    """Extract enum values for SQLAlchemy Enum column."""
    return [e.value for e in enum_class]


class Conversation(Base, TimestampMixin):
    """
    Represents a threaded conversation between participants.

    Multi-tenancy: store_id is required for store_customer and admin_customer
    conversations to ensure customer data isolation.
    """

    __tablename__ = "conversations"

    id = Column(Integer, primary_key=True, index=True)

    # Conversation type determines participant structure
    conversation_type = Column(
        Enum(ConversationType, values_callable=_enum_values),
        nullable=False,
        index=True,
    )

    # Subject line for the conversation thread
    subject = Column(String(500), nullable=False)

    # For store_customer and admin_customer conversations
    # Required for multi-tenant data isolation
    store_id = Column(
        Integer,
        ForeignKey("stores.id"),
        nullable=True,
        index=True,
    )

    # Status flags
    is_closed = Column(Boolean, default=False, nullable=False)
    closed_at = Column(DateTime, nullable=True)
    closed_by_type = Column(Enum(ParticipantType, values_callable=_enum_values), nullable=True)
    closed_by_id = Column(Integer, nullable=True)

    # Last activity tracking for sorting
    last_message_at = Column(DateTime, nullable=True, index=True)
    message_count = Column(Integer, default=0, nullable=False)

    # Relationships
    store = relationship("Store", foreign_keys=[store_id])
    participants = relationship(
        "ConversationParticipant",
        back_populates="conversation",
        cascade="all, delete-orphan",
    )
    messages = relationship(
        "Message",
        back_populates="conversation",
        cascade="all, delete-orphan",
        order_by="Message.created_at",
    )

    # Indexes for common queries
    __table_args__ = (
        Index("ix_conversations_type_store", "conversation_type", "store_id"),
    )

    def __repr__(self) -> str:
        return (
            f"<Conversation(id={self.id}, type='{self.conversation_type.value}', "
            f"subject='{self.subject[:30]}...')>"
        )


class ConversationParticipant(Base, TimestampMixin):
    """
    Links participants (users or customers) to conversations.

    Polymorphic relationship:
    - participant_type="admin" or "store": references users.id
    - participant_type="customer": references customers.id
    """

    __tablename__ = "conversation_participants"

    id = Column(Integer, primary_key=True, index=True)
    conversation_id = Column(
        Integer,
        ForeignKey("conversations.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # Polymorphic participant reference
    participant_type = Column(Enum(ParticipantType, values_callable=_enum_values), nullable=False)
    participant_id = Column(Integer, nullable=False, index=True)

    # For store participants, track which store they represent
    store_id = Column(
        Integer,
        ForeignKey("stores.id"),
        nullable=True,
    )

    # Unread tracking per participant
    unread_count = Column(Integer, default=0, nullable=False)
    last_read_at = Column(DateTime, nullable=True)

    # Notification preferences for this conversation
    email_notifications = Column(Boolean, default=True, nullable=False)
    muted = Column(Boolean, default=False, nullable=False)

    # Relationships
    conversation = relationship("Conversation", back_populates="participants")
    store = relationship("Store", foreign_keys=[store_id])

    __table_args__ = (
        UniqueConstraint(
            "conversation_id",
            "participant_type",
            "participant_id",
            name="uq_conversation_participant",
        ),
        Index(
            "ix_participant_lookup",
            "participant_type",
            "participant_id",
        ),
    )

    def __repr__(self) -> str:
        return (
            f"<ConversationParticipant(conversation_id={self.conversation_id}, "
            f"type='{self.participant_type.value}', id={self.participant_id})>"
        )


class Message(Base, TimestampMixin):
    """
    Individual message within a conversation thread.

    Sender polymorphism follows same pattern as participant.
    """

    __tablename__ = "messages"

    id = Column(Integer, primary_key=True, index=True)
    conversation_id = Column(
        Integer,
        ForeignKey("conversations.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # Polymorphic sender reference
    sender_type = Column(Enum(ParticipantType, values_callable=_enum_values), nullable=False)
    sender_id = Column(Integer, nullable=False, index=True)

    # Message content
    content = Column(Text, nullable=False)

    # System messages (e.g., "conversation closed")
    is_system_message = Column(Boolean, default=False, nullable=False)

    # Soft delete for moderation
    is_deleted = Column(Boolean, default=False, nullable=False)
    deleted_at = Column(DateTime, nullable=True)
    deleted_by_type = Column(Enum(ParticipantType, values_callable=_enum_values), nullable=True)
    deleted_by_id = Column(Integer, nullable=True)

    # Relationships
    conversation = relationship("Conversation", back_populates="messages")
    attachments = relationship(
        "MessageAttachment",
        back_populates="message",
        cascade="all, delete-orphan",
    )

    __table_args__ = (
        Index("ix_messages_conversation_created", "conversation_id", "created_at"),
    )

    def __repr__(self) -> str:
        return (
            f"<Message(id={self.id}, conversation_id={self.conversation_id}, "
            f"sender={self.sender_type.value}:{self.sender_id})>"
        )


class MessageAttachment(Base, TimestampMixin):
    """
    File attachments for messages.

    Files are stored in platform storage (local/S3) with references here.
    """

    __tablename__ = "message_attachments"

    id = Column(Integer, primary_key=True, index=True)
    message_id = Column(
        Integer,
        ForeignKey("messages.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # File metadata
    filename = Column(String(255), nullable=False)
    original_filename = Column(String(255), nullable=False)
    file_path = Column(String(1000), nullable=False)  # Storage path
    file_size = Column(Integer, nullable=False)  # Size in bytes
    mime_type = Column(String(100), nullable=False)

    # For image attachments
    is_image = Column(Boolean, default=False, nullable=False)
    image_width = Column(Integer, nullable=True)
    image_height = Column(Integer, nullable=True)
    thumbnail_path = Column(String(1000), nullable=True)

    # Relationships
    message = relationship("Message", back_populates="attachments")

    def __repr__(self) -> str:
        return f"<MessageAttachment(id={self.id}, filename='{self.original_filename}')>"
