# =============================================================================
# 企微IT智能服务台 — 会话模型
# =============================================================================
# 说明：对应数据库 conversations 表，存储所有会话信息
# 核心概念：每个员工的每次咨询对应一个会话（Conversation）
# 会话状态流转：ai_handling → queued → serving → resolved
# =============================================================================

import uuid
from datetime import datetime
from typing import Any, Dict, Optional

from sqlalchemy import Boolean, DateTime, Index, Integer, JSON, String
from sqlalchemy.orm import Mapped, mapped_column

from app.database import Base


class Conversation(Base):
    """会话模型 — 对应 conversations 表。

    每个员工的一次完整咨询过程对应一个会话记录。
    包含员工信息、会话状态、紧急度评分、标签等核心数据。

    Attributes:
        id: 会话唯一标识（UUID，数据库自动生成）
        employee_id: 企微员工UserID（关联企微通讯录）
        employee_name: 员工姓名（冗余存储，减少关联查询）
        department: 员工部门
        position: 员工岗位
        level: 员工等级（用于 VIP 判断）
        status: 会话状态（ai_handling/queued/serving/resolved）
        is_vip: VIP标记（基于企微通讯录规则自动匹配）
        is_pinned: 置顶标记（坐席手动操作）
        is_todo: 代办标记（坐席手动操作）
        urgency_score: 紧急度评分（1-5，数值越大越紧急）
        tags: 标签集合（JSONB，存储举手/需介入/情绪等标记）
        assigned_agent_id: 分配的坐席ID
        last_message_at: 最后消息时间（用于会话排序）
        last_message_summary: 最后消息摘要（会话列表预览用）
        created_at: 创建时间
        updated_at: 更新时间
    """

    # 表名（必须和架构文档 DDL 一致）
    __tablename__ = "conversations"

    # --------------------------------------------------------------------------
    # 字段定义
    # --------------------------------------------------------------------------

    # 主键：UUID，Python端生成（兼容PostgreSQL和SQLite）
    id: Mapped[str] = mapped_column(
        String(36),
        primary_key=True,
        default=lambda: str(uuid.uuid4()),
    )

    # 企业微信企业ID（US-7: 区分主企业和下游企业员工）
    # 默认值为主企业 corp_id，下游企业员工使用下游企业 corp_id
    corp_id: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        default="",
        comment="企业微信企业ID（主企业或下游企业）",
    )

    # 企微员工UserID（NOT NULL，配合 corp_id 唯一标识员工）
    employee_id: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        comment="企微员工UserID",
    )

    # 员工姓名（冗余存储，避免每次查询都要关联企微API）
    employee_name: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="员工姓名",
    )

    # 部门
    department: Mapped[str] = mapped_column(
        String(256),
        nullable=False,
        default="",
        comment="部门",
    )

    # 岗位
    position: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="岗位",
    )

    # 等级（用于 VIP 判断：总监及以上为 VIP）
    level: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        default="",
        comment="等级",
    )

    # 会话状态（CHECK 约束：只能取四种值）
    # ai_handling: AI处理中（第二步启用）
    # queued: 排队中，等待坐席接入
    # serving: 服务中，坐席正在处理
    # resolved: 已结单
    status: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="queued",
        comment="会话状态: ai_handling/queued/serving/resolved",
    )

    # VIP标记（基于企微通讯录API规则自动匹配）
    is_vip: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        comment="VIP标记",
    )

    # 置顶标记（坐席手动操作，置顶的会话在列表中优先显示）
    is_pinned: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        comment="置顶标记",
    )

    # 代办标记（坐席手动操作，标记需要后续跟进的会话）
    is_todo: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        comment="代办标记",
    )

    # 紧急度评分（1-5，数值越大越紧急）
    # 计算公式：基础分 + 情绪加成 + VIP加成 + 重复追问加成
    urgency_score: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=1,
        comment="紧急度1-5",
    )

    # 标签集合（JSON 格式，存储结构化标记数据，兼容所有数据库）
    # 示例：{"hand_raise": true, "emotion": "angry", "need_intervene": true}
    tags: Mapped[Dict[str, Any]] = mapped_column(
        JSON,
        nullable=False,
        default=dict,
        comment="标签集合",
    )

    # 分配的坐席ID（可为空，表示尚未分配坐席）
    assigned_agent_id: Mapped[Optional[str]] = mapped_column(
        String(64),
        nullable=True,
        comment="分配的坐席ID",
    )

    # 协作坐席ID列表（JSON 数组，存储所有被邀请来协作的坐席ID）
    # 和 assigned_agent_id 的区别：
    #   - assigned_agent_id：会话的「主责」坐席（接单人），只有他才能结单/转接
    #   - collaborating_agent_ids：被邀请来协助的坐席，可以查看和回复，但不能结单
    # 设计决策：用 JSON 而非关联表，因为协作人数少（1-3人），JSON 查询足够
    collaborating_agent_ids: Mapped[list] = mapped_column(
        JSON,
        nullable=False,
        default=list,
        comment="协作坐席ID列表",
    )

    # 被邀请参与会话的非坐席人员列表（JSON 数组，存储员工ID或部门ID）
    # 和 collaborating_agent_ids 的区别：
    #   - collaborating_agent_ids：坐席 → 坐席协作（摇人）
    #   - participants：坐席 → 任意员工/部门（邀请功能 P0-09~P0-11）
    # 格式：[ {"id": "employee_id", "name": "姓名", "department": "部门", "type": "employee"},
    #         {"id": "dept_id", "name": "部门名", "type": "department"} ]
    # 设计决策：存储完整信息，减少企微API调用
    participants: Mapped[list] = mapped_column(
        JSON,
        nullable=False,
        default=list,
        comment="被邀请参与会话的人员列表（邀请功能）",
    )

    # AI 实质性回复计数（排除打招呼/呼叫人工的引导回复）
    # 当计数 >= 3 时，前端显示「呼叫坐席」按钮
    ai_substantive_reply_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="AI实质性回复计数（满3次可呼叫坐席）",
    )

    # 影响范围（受影响人数，0=未评估，数值越大影响范围越广）
    impact_scope: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="影响范围",
    )

    # 阻断性标记（问题是否阻断员工正常工作流程）
    is_blocking: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        comment="阻断性标记",
    )

    # 情绪状态（normal: 正常, worried: 担忧, angry: 愤怒, urgent: 紧急）
    emotion_state: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="normal",
        comment="情绪状态",
    )

    # Dify 会话ID（用于多轮对话上下文保持）
    # Dify 侧通过此 ID 关联同一员工的多轮对话
    dify_conversation_id: Mapped[Optional[str]] = mapped_column(
        String(128),
        nullable=True,
        default=None,
        comment="Dify会话ID（多轮对话上下文）",
    )

    # 最后消息时间（用于会话列表按最新消息排序）
    last_message_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime(timezone=True),
        nullable=True,
        comment="最后消息时间",
    )

    # 最后消息摘要（会话列表预览用，截取消息前256字符）
    last_message_summary: Mapped[str] = mapped_column(
        String(256),
        nullable=False,
        default="",
        comment="最后消息摘要",
    )

    # 创建时间
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        default=datetime.now,
        comment="创建时间",
    )

    # 更新时间
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        default=datetime.now,
        onupdate=datetime.now,
        comment="更新时间",
    )

    # --------------------------------------------------------------------------
    # 索引定义（和架构文档 DDL 严格一致）
    # --------------------------------------------------------------------------
    __table_args__ = (
        # 按状态查询（如查询所有排队中的会话）
        Index("idx_conversations_status", "status"),
        # 按员工ID查询（如查询某个员工的所有会话）
        Index("idx_conversations_employee_id", "employee_id"),
        # US-7: 按企业ID查询（如查询某企业所有会话）
        Index("idx_conversations_corp_id", "corp_id"),
        # 按坐席ID查询（如查询某个坐席正在服务的所有会话）
        Index("idx_conversations_assigned_agent", "assigned_agent_id"),
        # 按紧急度倒序查询（紧急度高的排前面）
        Index("idx_conversations_urgency_score", "urgency_score"),
        # 按最后消息时间倒序查询（最新消息的排前面）
        Index("idx_conversations_last_message_at", "last_message_at"),
    )

    def __repr__(self) -> str:
        """会话对象的字符串表示，方便调试。"""
        return (
            f"<Conversation(id={self.id}, employee={self.employee_name}, "
            f"status={self.status}, urgency={self.urgency_score})>"
        )

