# =============================================================================
# 企微IT智能服务台 — 会话 Pydantic Schema
# =============================================================================
# 说明：定义会话相关的请求/响应数据结构
# 包含：创建、更新、响应三种 Schema
# tags 字段使用 JSONB 结构，定义了详细的子结构
# =============================================================================

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

from pydantic import BaseModel, Field, field_validator


# --------------------------------------------------------------------------
# tags JSONB 字段的子结构定义
# --------------------------------------------------------------------------
class ConversationTags(BaseModel):
    """会话标签集合 — 对应 conversations.tags JSONB 字段。

    记录会话的各种标记状态，用于坐席端展示和排序。

    Attributes:
        hand_raise: 举手标记（员工说"转人工"或点击摇人按钮）
        need_intervene: 需介入标记（追问超过N轮）
        emotion: 情绪标记（neutral/worried/angry/urgent）
        emotion_keywords: 触发情绪标记的关键词列表
        repeat_count: 追问轮次计数
    """

    # 举手标记（员工明确要求转人工）
    hand_raise: bool = Field(default=False, description="举手标记")
    # 需介入标记（同一问题追问超过阈值）
    need_intervene: bool = Field(default=False, description="需介入标记")
    # 情绪标记（neutral: 正常, worried: 担忧, angry: 愤怒, urgent: 紧急）
    emotion: str = Field(
        default="neutral",
        description="情绪标记: neutral/worried/angry/urgent",
    )
    # 触发情绪标记的关键词列表
    emotion_keywords: List[str] = Field(
        default_factory=list,
        description="触发情绪标记的关键词",
    )
    # 追问轮次计数（同一会话中员工连续追问的次数）
    repeat_count: int = Field(default=0, description="追问轮次计数")


# --------------------------------------------------------------------------
# 会话状态枚举值校验
# --------------------------------------------------------------------------
VALID_STATUSES = {"ai_handling", "queued", "serving", "resolved"}


# --------------------------------------------------------------------------
# 创建会话 Schema（从企微消息创建会话时使用）
# --------------------------------------------------------------------------
class ConversationCreate(BaseModel):
    """创建会话请求 Schema。

    从企微消息回调创建新会话时使用，
    只需要员工ID和姓名，其他信息后续补充。

    Attributes:
        employee_id: 企微员工UserID
        employee_name: 员工姓名
        department: 部门
        position: 岗位
        level: 等级
    """

    employee_id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID")
    employee_name: str = Field(default="", max_length=128, description="员工姓名")
    department: str = Field(default="", max_length=256, description="部门")
    position: str = Field(default="", max_length=128, description="岗位")
    level: str = Field(default="", max_length=64, description="等级")


# --------------------------------------------------------------------------
# 更新会话 Schema（坐席修改会话信息时使用）
# --------------------------------------------------------------------------
class ConversationUpdate(BaseModel):
    """更新会话请求 Schema。

    坐席更新会话信息时使用，所有字段可选（只更新传入的字段）。

    Attributes:
        employee_name: 员工姓名
        department: 部门
        position: 岗位
        level: 等级
        is_vip: VIP标记
        tags: 标签集合
        urgency_score: 紧急度评分
        assigned_agent_id: 分配的坐席ID
        last_message_summary: 最后消息摘要
    """

    employee_name: Optional[str] = Field(None, max_length=128, description="员工姓名")
    department: Optional[str] = Field(None, max_length=256, description="部门")
    position: Optional[str] = Field(None, max_length=128, description="岗位")
    level: Optional[str] = Field(None, max_length=64, description="等级")
    is_vip: Optional[bool] = Field(None, description="VIP标记")
    tags: Optional[ConversationTags] = Field(None, description="标签集合")
    urgency_score: Optional[int] = Field(None, ge=1, le=5, description="紧急度1-5")
    assigned_agent_id: Optional[str] = Field(None, max_length=64, description="分配的坐席ID")
    last_message_summary: Optional[str] = Field(None, max_length=256, description="最后消息摘要")


# --------------------------------------------------------------------------
# 更新会话状态 Schema
# --------------------------------------------------------------------------
class ConversationStatusUpdate(BaseModel):
    """更新会话状态请求 Schema。

    Attributes:
        status: 新的会话状态
    """

    status: str = Field(..., description="会话状态: ai_handling/queued/serving/resolved")

    @field_validator("status")
    @classmethod
    def validate_status(cls, v: str) -> str:
        """校验会话状态值是否合法。"""
        if v not in VALID_STATUSES:
            raise ValueError(f"无效的会话状态: {v}，合法值为: {VALID_STATUSES}")
        return v


# --------------------------------------------------------------------------
# 坐席接单 Schema
# --------------------------------------------------------------------------
class ConversationAssign(BaseModel):
    """坐席接单请求 Schema。

    Attributes:
        agent_id: 接单的坐席ID
    """

    agent_id: str = Field(..., min_length=1, max_length=64, description="坐席ID")


# --------------------------------------------------------------------------
# 摇人（邀请协作）Schema
# --------------------------------------------------------------------------
class ConversationInvite(BaseModel):
    """摇人邀请请求 Schema。

    Attributes:
        agent_id: 被邀请的坐席ID
    """

    agent_id: str = Field(..., min_length=1, max_length=64, description="被邀请的坐席ID")


# --------------------------------------------------------------------------
# 邀请员工/部门加入会话 Schema（P0-09~P0-11 邀请功能）
# --------------------------------------------------------------------------
class ParticipantInfo(BaseModel):
    """被邀请人信息。

    Attributes:
        id: 企微员工UserID 或部门ID
        name: 姓名 或 部门名称
        department: 部门（仅员工类型有）
        type: 类型 — employee（个人）或 department（部门）
        avatar: 头像URL（从企微通讯录或employees表获取）
        joined: 是否已加入（邀请后、点击加入前为 False）
        joined_at: 加入时间（ISO 格式字符串）
    """

    id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID或部门ID")
    name: str = Field(..., min_length=1, max_length=128, description="姓名或部门名称")
    department: str = Field(default="", max_length=256, description="部门（仅员工类型）")
    type: str = Field(default="employee", description="类型: employee/department")
    avatar: str = Field(default="", max_length=512, description="头像URL")
    joined: Optional[bool] = Field(default=None, description="是否已加入")
    joined_at: Optional[str] = Field(default=None, description="加入时间（ISO格式）")


class InviteParticipantRequest(BaseModel):
    """邀请员工/部门加入会话请求 Schema。

    Attributes:
        participants: 被邀请人列表
        history_mode: 历史消息共享模式 — recent10（最近10条）/ all（全部）/ none（不共享）
    """

    participants: List[ParticipantInfo] = Field(
        ..., min_length=1, max_length=20, description="被邀请人列表"
    )
    history_mode: str = Field(
        default="recent10",
        description="历史消息共享模式: recent10/all/none",
    )

    @field_validator("history_mode")
    @classmethod
    def validate_history_mode(cls, v: str) -> str:
        """校验历史共享模式。"""
        valid_modes = {"recent10", "all", "none"}
        if v not in valid_modes:
            raise ValueError(f"无效的历史共享模式: {v}，合法值为: {valid_modes}")
        return v

    @field_validator("participants")
    @classmethod
    def validate_participants_unique(cls, v: List[ParticipantInfo]) -> List[ParticipantInfo]:
        """校验参与者ID不重复。"""
        ids = [p.id for p in v]
        if len(ids) != len(set(ids)):
            raise ValueError("参与者ID不能重复")
        return v


class JoinConversationRequest(BaseModel):
    """被邀请人加入会话请求 Schema。

    Attributes:
        employee_id: 被邀请人的企微UserID
    """

    employee_id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID")


# --------------------------------------------------------------------------
# 会话响应 Schema（返回给前端的数据结构）
# --------------------------------------------------------------------------
class ConversationResponse(BaseModel):
    """会话响应 Schema。

    返回给前端（坐席端/H5端）的会话数据结构。
    使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。

    Attributes:
        id: 会话ID
        employee_id: 企微员工UserID
        employee_name: 员工姓名
        department: 部门
        position: 岗位
        level: 等级
        status: 会话状态
        is_vip: VIP标记
        is_pinned: 置顶标记
        is_todo: 代办标记
        urgency_score: 紧急度评分
        tags: 标签集合
        assigned_agent_id: 分配的坐席ID
        collaborating_agent_ids: 协作坐席ID列表
        last_message_at: 最后消息时间
        last_message_summary: 最后消息摘要
        created_at: 创建时间
        updated_at: 更新时间
    """

    id: str
    employee_id: str
    employee_name: str
    department: str
    position: str
    level: str
    status: str
    is_vip: bool
    is_pinned: bool
    is_todo: bool
    urgency_score: int
    tags: Dict[str, Any]
    assigned_agent_id: Optional[str] = None
    collaborating_agent_ids: List[str] = Field(default_factory=list, description="协作坐席ID列表")
    # 被邀请参与会话的人员列表（邀请功能 P0-09~P0-11）
    participants: List[ParticipantInfo] = Field(default_factory=list, description="被邀请参与会话的人员列表")
    last_message_at: Optional[datetime] = None
    last_message_summary: str
    created_at: datetime
    updated_at: datetime

    # ----- 坐席会话全局可见扩展字段 -----
    # 是否为当前坐席的会话
    is_mine: bool = Field(default=False, description="是否为当前坐席的会话")
    # 分配的坐席姓名（其他坐席会话显示用）
    assigned_agent_name: Optional[str] = Field(default=None, description="分配的坐席姓名")
    # 是否可以接手（其他坐席已接单的会话为 True）
    can_grab: bool = Field(default=False, description="是否可以接手")

    # ----- 多坐席协作扩展字段 -----
    # 协作坐席姓名映射（agent_id → name）
    collaborating_agent_names: Dict[str, str] = Field(
        default_factory=dict, description="协作坐席姓名映射"
    )
    # 当前坐席是否为协作坐席（非主责）
    is_collaborator: bool = Field(default=False, description="是否为协作坐席")

    # ----- v5.3 新增：影响范围 / 阻断性 / 情绪状态 -----
    # 影响范围（受影响人数，0=未评估）
    impact_scope: int = Field(default=0, description="影响范围")
    # 阻断性标记（问题是否阻断员工正常工作流程）
    is_blocking: bool = Field(default=False, description="阻断性标记")
    # 情绪状态（normal/worried/angry/urgent）
    emotion_state: str = Field(default="normal", description="情绪状态")

    model_config = {"from_attributes": True}


# --------------------------------------------------------------------------
# 会话列表响应 Schema（包含分页信息）
# --------------------------------------------------------------------------
class ConversationListResponse(BaseModel):
    """会话列表响应 Schema。

    包含会话列表和总数，用于分页查询。

    Attributes:
        items: 会话列表
        total: 总数
    """

    items: List[ConversationResponse]
    total: int
