"""
頻道 / 訊息資料表 Model(階段 3 切片 a)。

階段 3a 先建四張核心表:
  - channels         ─ 頻道(公告、靈感、求助、會議紀錄、庫柏動態、閒聊)
  - channel_members  ─ 誰在哪個頻道(LINE 群組概念)
  - messages         ─ 對話訊息
  - message_reactions ─ 表情回應(👍❤️😂)

延後到 3c 之後:
  - message_reads        ─ 已讀回執(等 WebSocket 完才有意義)
  - message_attachments  ─ 附件(等檔案上傳功能)
  - coba_conversations   ─ 庫柏一對一對話(切片 3e)
  - coba_memory_summaries ─ 庫柏長期記憶摘要(切片 3f)

設計重點:
  1. channels 有特殊型別 'public' / 'private' / 'direct'(1對1)/ 'coba_dm'(跟庫柏私聊)
  2. messages 的 user_id 可為 NULL → 庫柏發的訊息(系統訊息)
  3. message_type 採用字串而非 enum,方便擴充新類型
  4. reply_to_id 支援引用回覆(LINE 風格的 thread)
"""

from datetime import datetime
from uuid import UUID, uuid4

from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, Index, String, Text, Uuid, func
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base
from app.models.user import TimestampMixin, User


class Channel(Base, TimestampMixin):
    """頻道:對話的容器。

    channel_type:
      - public   : 公開頻道,所有人都能加入(公告、閒聊等)
      - private  : 私密頻道,owner 邀請才能加入
      - direct   : 1 對 1 私訊
      - coba_dm  : 跟庫柏的私聊(每個使用者一個)
    """

    __tablename__ = "channels"

    id: Mapped[UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid4)
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    icon: Mapped[str] = mapped_column(String(10), nullable=False, default="💬")
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    channel_type: Mapped[str] = mapped_column(
        String(20), nullable=False, default="public", index=True
    )
    # 預設頻道(系統建的,不能刪)
    is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    # 排序權重(越小越上面),預設頻道有固定順序
    sort_order: Mapped[int] = mapped_column(default=100, nullable=False)

    created_by: Mapped[UUID | None] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )

    # 階段 6:LINE 群組 / 多人聊天室 / 1對1 對應。
    # 同一個 line_source_id 全表唯一(不會有兩個 channel 對到同一個 LINE 群)。
    # line_source_type ∈ ('group', 'room', 'user')
    line_source_type: Mapped[str | None] = mapped_column(String(20), nullable=True)
    line_source_id: Mapped[str | None] = mapped_column(
        String(64), nullable=True, unique=True, index=True
    )

    # 反向關聯
    members: Mapped[list["ChannelMember"]] = relationship(
        back_populates="channel",
        cascade="all, delete-orphan",
        lazy="selectin",
    )
    messages: Mapped[list["Message"]] = relationship(
        back_populates="channel",
        cascade="all, delete-orphan",
        lazy="noload",   # 訊息很多,需要時才查
    )

    def __repr__(self) -> str:
        return f"<Channel {self.icon}{self.name} type={self.channel_type}>"


class ChannelMember(Base, TimestampMixin):
    """頻道成員 = LINE 群組成員。

    public 頻道:預設「全公司」都會自動加入(seed 時建立)
    private 頻道:由 owner 邀請才能加入
    coba_dm:每個 user 一個,只有 user 自己跟庫柏在裡面
    """

    __tablename__ = "channel_members"

    id: Mapped[UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid4)
    channel_id: Mapped[UUID] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("channels.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    user_id: Mapped[UUID] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # 通知層級:all(全部通知)/ mentions(只有 @ 我)/ none(完全靜音)
    notification_level: Mapped[str] = mapped_column(
        String(20), nullable=False, default="all"
    )

    # 上次讀到的時間(LINE 風格的「已讀」依據)
    # NULL = 從沒進來看過
    # 有值 = 此人在這個頻道讀到該時間點為止
    # 計算「某訊息是否被某人讀過」:user.last_read_at >= message.created_at
    last_read_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    # 反向關聯
    channel: Mapped[Channel] = relationship(back_populates="members")
    user: Mapped[User] = relationship(lazy="joined")

    __table_args__ = (
        # 同一個人不能在同一頻道有兩筆紀錄
        Index("ux_channel_member_unique", "channel_id", "user_id", unique=True),
    )

    def __repr__(self) -> str:
        return f"<ChannelMember channel={self.channel_id} user={self.user_id}>"


class Message(Base, TimestampMixin):
    """訊息。

    user_id 可為 NULL,代表這是「庫柏」或系統訊息。

    message_type:
      - text    : 純文字(目前主要用這個)
      - image   : 圖片(階段 3+)
      - file    : 檔案
      - audio   : 語音
      - sticker : 貼圖
      - system  : 系統訊息(誰加入、誰離開等)
      - coba_action : 庫柏的特殊動作(轉任務、推薦等)
    """

    __tablename__ = "messages"

    id: Mapped[UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid4)

    channel_id: Mapped[UUID] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("channels.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # NULL = 庫柏 / 系統訊息
    user_id: Mapped[UUID | None] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )

    content: Mapped[str] = mapped_column(Text, nullable=False)
    message_type: Mapped[str] = mapped_column(String(20), nullable=False, default="text")

    # 引用回覆:這則訊息回覆的對象
    reply_to_id: Mapped[UUID | None] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("messages.id", ondelete="SET NULL"),
        nullable=True,
    )

    # 釘選 / 編輯標記
    is_pinned: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    is_edited: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    # 階段 3e:Coba 自動加的分類標籤(取代頻道分類)
    # 例如 ["📢 公告", "客戶A"] 或 ["🆘 求助", "React"]
    # 由背景任務以 Haiku 4.5 產生,寫回此欄位後再廣播 message_tagged 事件
    tags: Mapped[list[str] | None] = mapped_column(JSON, nullable=True)

    # 階段 6:來自 LINE 的訊息會帶上原始 messageId,用來去重 / 對應原始訊息。
    line_message_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)

    # 反向關聯
    channel: Mapped[Channel] = relationship(back_populates="messages")
    user: Mapped[User | None] = relationship(lazy="joined")
    reply_to: Mapped["Message | None"] = relationship(
        remote_side="Message.id", lazy="joined"
    )
    reactions: Mapped[list["MessageReaction"]] = relationship(
        back_populates="message",
        cascade="all, delete-orphan",
        lazy="selectin",
    )

    def __repr__(self) -> str:
        sender = "Coba" if self.user_id is None else str(self.user_id)[:8]
        return f"<Message {sender}: {self.content[:30]!r}>"


class MessageReaction(Base):
    """訊息表情回應(👍❤️😂)。

    一個使用者可以對同一則訊息加多個不同的 emoji,
    但同一個 emoji 只能加一次。
    """

    __tablename__ = "message_reactions"

    id: Mapped[UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid4)
    message_id: Mapped[UUID] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("messages.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    user_id: Mapped[UUID] = mapped_column(
        Uuid(as_uuid=True),
        ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
    )
    emoji: Mapped[str] = mapped_column(String(20), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )

    # 反向關聯
    message: Mapped[Message] = relationship(back_populates="reactions")

    __table_args__ = (
        # 同人對同訊息加同 emoji 只能一次
        Index("ux_reaction_unique", "message_id", "user_id", "emoji", unique=True),
    )

    def __repr__(self) -> str:
        return f"<Reaction {self.emoji} by {str(self.user_id)[:8]}>"
