"""
项目内部数据结构定义

这是本项目真正使用的核心数据模型,所有业务逻辑都基于这些类型。
与 sillytavern.py 不同,这里的模型不参与导入导出兼容,而是专注于:
- 内部业务逻辑处理
- API 响应数据结构
- 数据存储格式
- 工作流引擎数据交换

所有从 SillyTavern 导入的数据都会转换为这些内部模型进行处理,
导出时再从内部模型转换回 SillyTavern 格式。
"""
from enum import Enum
from typing import List, Optional, Dict, Any
from pydantic import BaseModel, Field
from datetime import datetime


# ==================== 世界书 (World Info) ====================

class ActivationType(str, Enum):
    """
    自定义激活方式类型（4种枚举）
    
    这是项目的核心创新点之一,相比 SillyTavern 的简单 constant/selective 标志,
    我们提供了更灵活的激活机制。
    """
    PERMANENT = 'permanent'    # 永久激活 - 始终包含在上下文中
    KEYWORD = 'keyword'        # 关键词触发 - 匹配关键词时激活
    RAG = 'rag'               # RAG 检索激活 - 基于向量相似度检索
    LOGIC = 'logic'           # 逻辑表达式激活 - 基于变量条件判断


class LogicOperator(str, Enum):
    """逻辑运算符（用于 LOGIC 激活类型）"""
    EQUALS = 'equals'              # 等于
    NOT_EQUALS = 'not_equals'      # 不等于
    CONTAINS = 'contains'          # 包含
    NOT_CONTAINS = 'not_contains'  # 不包含
    GREATER = 'greater'            # 大于
    LESS = 'less'                  # 小于


class LogicExpression(BaseModel):
    """
    逻辑表达式结构（用于 LOGIC 激活类型）
    
    示例: variable1="mood", operator="equals", variable2="happy"
    表示当 mood 变量等于 happy 时激活该条目
    """
    variable1: str = Field(..., description="第一个变量名")
    operator: LogicOperator = Field(..., description="比较运算符")
    variable2: str = Field(..., description="第二个变量名或值")


class RAGConfig(BaseModel):
    """
    RAG 配置（用于 RAG 激活类型）
    
    控制如何从向量数据库中检索相关内容
    """
    libraryId: str = Field(..., description="绑定的 RAG 库 ID")
    threshold: Optional[float] = Field(0.7, ge=0, le=1, description="相似度阈值 (0-1)")
    maxEntries: Optional[int] = Field(5, gt=0, description="最大返回条目数")


class WorldInfoEntry(BaseModel):
    """
    项目内部世界书条目结构
    
    这是世界书的核心单元,每个条目代表一段可以被动态注入到对话上下文中的知识。
    相比 SillyTavern,我们添加了 activationType、logicExpression、ragConfig 等高级功能。
    """
    uid: str = Field(..., description="条目唯一标识符 (UUID)")
    key: Optional[List[str]] = Field(None, description="主关键词列表 (用于 KEYWORD 激活)")
    keysecondary: Optional[List[str]] = Field(None, description="次要关键词列表 (可选过滤)")
    content: str = Field(..., description="条目内容 - 激活时注入的文本")
    activationType: ActivationType = Field(..., description="激活方式")
    logicExpression: Optional[LogicExpression] = Field(None, description="逻辑表达式 (LOGIC 类型使用)")
    ragConfig: Optional[RAGConfig] = Field(None, description="RAG 配置 (RAG 类型使用)")
    order: int = Field(0, description="插入顺序 - 数值越大越靠近末尾")
    position: Optional[str] = Field('after_char', description="插入位置")
    depth: Optional[int] = Field(None, description="插入深度 (当 position='at_depth' 时使用)")
    probability: Optional[float] = Field(100, ge=0, le=100, description="激活概率 (0-100)")
    group: Optional[List[str]] = Field(None, description="所属组标签")
    disable: bool = Field(False, description="是否禁用")
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")


class WorldInfo(BaseModel):
    """
    项目内部世界书结构
    
    世界书是角色知识的集合,可以绑定到角色卡上,在对话中动态提供背景信息。
    """
    id: str = Field(..., description="世界书唯一标识符 (UUID)")
    name: str = Field(..., description="世界书名称")
    description: Optional[str] = Field(None, description="世界书描述")
    entries: List[WorldInfoEntry] = Field(default_factory=list, description="条目数组")
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
    version: int = Field(1, description="版本号 (用于数据迁移)")


# ==================== 角色卡 (Character Card) ====================

class OutputSchemaField(BaseModel):
    """
    Vercel AI SDK Output.object() 的表头定义
    
    用于结构化输出,让 LLM 按照指定格式返回数据。
    这是项目的特色功能,支持动态表格生成。
    """
    name: str = Field(..., description="字段名称")
    type: str = Field(..., description="字段类型 (string/number/boolean/array/object)")
    description: str = Field(..., description="字段描述")
    required: Optional[bool] = Field(None, description="是否必需")
    enum: Optional[List[str]] = Field(None, description="枚举值 (字符串固定选项)")
    fields: Optional[List['OutputSchemaField']] = Field(None, description="嵌套字段 (object 类型)")


class CharacterCard(BaseModel):
    """
    项目内部角色卡结构
    
    角色卡是对话 AI 的核心定义,包含人设、场景、开场白等。
    相比 SillyTavern,我们添加了 categories、outputSchema、worldInfoId 等功能。
    """
    id: str = Field(..., description="角色唯一标识符 (UUID)")
    name: str = Field(..., description="角色名称")
    description: str = Field(..., description="角色详细描述")
    personality: str = Field(..., description="角色性格特征")
    scenario: str = Field(..., description="场景设定")
    first_mes: str = Field(..., description="首条开场消息")
    mes_example: str = Field(..., description="对话示例")
    categories: List[str] = Field(default_factory=list, description="分类标签 (用于前端筛选)")
    tags: Optional[List[str]] = Field(None, description="动态表格标签数组 (SillyTavern 关键字机制)")
    worldInfoId: Optional[str] = Field(None, description="绑定的世界书 ID")
    outputSchema: Optional[List[OutputSchemaField]] = Field(None, description="输出 schema 定义 (结构化输出)")
    avatarPath: Optional[str] = Field(None, description="角色头像路径")
    alternate_greetings: Optional[List[str]] = Field(None, description="替代问候语数组")
    
    # TODO: 拓展提示词设置（插件/拓展系统预留接口）
    # - tableMaintenancePrompt: 用于指导 AI 维护动态表格（RPG状态、任务追踪等）
    # - imageGenerationPrompt: 用于指导 AI 生成图片描述提示词
    # 当前状态：字段已定义，默认值为 None，等待插件系统实现
    tableMaintenancePrompt: Optional[str] = Field(None, description="动态表格维护提示词 - 指导 AI 如何更新表格数据")
    imageGenerationPrompt: Optional[str] = Field(None, description="生图提示词模板 - 指导 AI 如何生成图片描述")
    
    # ✅ 动态表格数据（SillyTavern 关键字机制扩展）
    tableHeaders: Optional[List[str]] = Field(None, description="动态表格表头数组")
    tableDefaults: Optional[Dict[str, Any]] = Field(None, description="动态表格默认值对象")
    
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
    lastChatAt: Optional[int] = Field(None, description="最后聊天时间戳")
    isFavorite: bool = Field(False, description="收藏状态")
    version: int = Field(1, description="版本号")


# ==================== 聊天记录 (Chat Log) ====================

# 历史记录模式枚举
class HistoryMode(str, Enum):
    """
    历史记录处理模式
    
    - FULL: 全量模式，保留所有消息（需经正则处理）
    - SUMMARY: 总结模式，定期用LLM总结历史消息
    - RAG: RAG模式，基于向量检索（暂不实现）
    """
    FULL = 'full'        # 全量模式
    SUMMARY = 'summary'  # 总结模式
    RAG = 'rag'          # RAG模式（预留）


class SummaryConfig(BaseModel):
    """
    总结配置
    
    用于控制历史消息的总结行为
    """
    enabled: bool = Field(True, description="是否启用总结")
    interval: int = Field(10, ge=2, description="总结间隔（每隔多少条消息总结一次）")
    includeUserInput: bool = Field(True, description="总结时是否包含用户输入")
    summaryPrompt: str = Field(
        "请总结以下对话内容，保留关键信息和上下文。用简洁的语言概括主要事件、人物状态和重要细节。",
        description="总结提示词"
    )
    maxSummaryLength: int = Field(500, ge=100, description="总结文本的最大长度（字符数）")
    

class ChatHeader(BaseModel):
    """
    项目内部聊天记录头
    
    包含聊天的元数据，如参与角色、创建时间等。
    """
    id: str = Field(..., description="聊天唯一标识符 (UUID)")
    displayName: str = Field(..., description="显示名称 (聊天标题)")
    characterId: str = Field(..., description="关联的角色卡 ID")
    userName: str = Field("User", description="用户角色名")
    characterName: str = Field(..., description="AI 角色名称")
    tags: Optional[List[str]] = Field(None, description="动态表格标签数组 (从角色卡继承)")
    
    # ✅ 历史记录模式配置
    historyMode: HistoryMode = Field(HistoryMode.FULL, description="历史记录处理模式 (full/summary/rag)")
    summaryConfig: Optional[SummaryConfig] = Field(None, description="总结配置 (当 historyMode='summary' 时使用)")
    summaryCounter: int = Field(0, ge=0, description="总结计数器（独立于楼层，用于跟踪需要总结的消息数）")
    
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
    messageCount: int = Field(0, description="消息数量")
    ragLibraryId: Optional[str] = Field(None, description="关联的 RAG 历史消息库 ID")

    # Agent workflow engine (optional, backward compatible)
    workflowTemplateId: Optional[str] = Field(None, description="工作流模板 ID")
    engineRunId: Optional[str] = Field(None, description="最近一次引擎运行 ID")


class ChatMessage(BaseModel):
    """
    项目内部聊天消息
    
    单条对话消息，支持多版本 (swipes)、token 统计、历史记录总结等功能。
    """
    id: str = Field(..., description="消息唯一标识符 (UUID)")
    name: str = Field(..., description="发送者名称")
    is_user: bool = Field(..., description="是否为用户消息")
    is_system: Optional[bool] = Field(None, description="是否为系统消息")
    sendDate: str = Field(..., description="发送日期 ISO 字符串")
    mes: str = Field(..., description="消息内容文本")
    chatId: str = Field(..., description="关联的聊天 ID")
    swipes: Optional[List[str]] = Field(None, description="替换回答数组 (多版本)")
    swipe_id: Optional[int] = Field(0, description="当前选择的版本索引")
    tokenCount: Optional[int] = Field(None, description="Token 数量 (用于统计)")
    isTemporary: Optional[bool] = Field(None, description="是否为临时消息 (未保存)")
    
    # ✅ 历史记录总结相关字段
    is_summarized: bool = Field(False, description="是否已被总结（中间楼层，内容为空）")
    is_summary: bool = Field(False, description="是否是总结消息（包含总结文本的楼层）")
    summary_range: Optional[str] = Field(None, description="总结范围描述（如 'L1-L8'，仅在 is_summary=True 时有值）")


class ChatLog(BaseModel):
    """
    项目内部完整聊天记录
    
    包含聊天头和所有消息,是完整的对话历史。
    """
    header: ChatHeader = Field(..., description="聊天头")
    messages: List[ChatMessage] = Field(default_factory=list, description="消息列表")


# ==================== 预设 (Preset) ====================

class GenerationPreset(BaseModel):
    """
    项目内部采样参数预设
    
    控制 LLM 生成的参数配置,如温度、top_p 等。
    """
    id: str = Field(..., description="预设唯一标识符 (UUID)")
    name: str = Field(..., description="预设名称")
    temperature: float = Field(1.0, ge=0, le=2, description="温度 (控制随机性)")
    topP: float = Field(1.0, ge=0, le=1, description="Top P (核采样)")
    topK: int = Field(0, ge=0, description="Top K")
    repetitionPenalty: float = Field(1.0, ge=0, description="重复惩罚")
    frequencyPenalty: Optional[float] = Field(None, description="频率惩罚")
    presencePenalty: Optional[float] = Field(None, description="存在惩罚")
    maxLength: Optional[int] = Field(None, gt=0, description="最大生成长度")
    isDefault: bool = Field(False, description="是否为默认预设")
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")


# ==================== 提示词预设 (Prompt Preset) ====================

class PromptRole(str, Enum):
    """
    Prompt 角色类型
    
    内部业务层只保留三种角色,简化了 SillyTavern 的复杂角色系统。
    """
    SYSTEM = 'system'  # 系统指令
    AI = 'ai'         # AI 助手
    USER = 'user'     # 用户


class PromptEntry(BaseModel):
    """
    内部业务层 - Prompt 条目
    
    提示词模板的基本单元,可以组合成完整的提示词预设。
    这是基于某个 character_id 生成的"当前视图"。
    """
    identifier: str = Field(..., description="稳定关联键 (用于回写)")
    name: str = Field(..., description="条目名 (前端显示)")
    enabled: bool = Field(True, description="是否启用 (当前作用域下的业务状态)")
    content: str = Field(..., description="条目内容 (静态内容视图)")
    order: int = Field(..., description="条目顺序 (前端展示和拖拽排序)")
    role: PromptRole = Field(..., description="角色类型")
    tokenCount: int = Field(0, description="总 token 数 (派生显示字段)")
    isSystemNode: bool = Field(False, description="是否固有节点 (不可删除)")


class PromptPresetView(BaseModel):
    """
    内部业务层 - Prompt 预设视图
    
    基于某个 character_id 的"当前视图",包含已排序、已过滤的条目列表。
    """
    characterId: str = Field(..., description="关联的角色 ID")
    entries: List[PromptEntry] = Field(default_factory=list, description="当前视图的条目列表")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
    version: int = Field(1, description="版本号")


# ==================== RAG 配置 ====================

class RAGSearchConfig(BaseModel):
    """RAG 搜索配置"""
    topK: int = Field(5, gt=0, description="每次检索返回的结果数")
    threshold: float = Field(0.7, ge=0, le=1, description="相似度阈值 (0-1)")
    maxContextLength: int = Field(2000, gt=0, description="最大上下文长度 (字符数)")


class CharacterRAGConfig(BaseModel):
    """
    角色卡 RAG 世界书库配置
    
    记录角色卡关联的 RAG 知识库,用于动态检索相关知识。
    """
    characterId: str = Field(..., description="角色卡ID")
    ragLibraryIds: List[str] = Field(default_factory=list, description="关联的RAG库ID列表")
    enabled: bool = Field(True, description="是否启用")
    searchConfig: Optional[RAGSearchConfig] = Field(None, description="搜索配置")
    position: str = Field('after_char', description="RAG内容插入位置")
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")


class ChatRAGConfig(BaseModel):
    """
    聊天会话 RAG 历史消息配置
    
    记录聊天会话关联的 RAG 历史消息库,用于智能检索历史对话。
    """
    chatId: str = Field(..., description="聊天会话ID")
    ragLibraryId: Optional[str] = Field(None, description="关联的RAG历史消息库ID")
    enabled: bool = Field(True, description="是否启用")
    searchConfig: Optional[Dict[str, Any]] = Field(None, description="搜索配置")
    autoIndex: bool = Field(True, description="是否自动索引新消息")
    indexConfig: Optional[Dict[str, Any]] = Field(None, description="索引配置")
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
    updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")


# ==================== Token 统计 ====================

class TokenUsageStatus(str, Enum):
    """Token 使用状态"""
    COMPLETED = 'completed'    # 成功完成
    INTERRUPTED = 'interrupted'  # 被用户中断
    FAILED = 'failed'          # 请求失败（API错误等）


class TokenUsageRecord(BaseModel):
    """
    Token 使用记录
    
    记录每次 LLM 调用的 token 使用情况，支持按时间、角色、聊天维度统计
    """
    id: str = Field(..., description="记录唯一标识符 (UUID)")
    chatId: str = Field(..., description="聊天ID (role_name/chat_name)")
    roleName: str = Field(..., description="角色名称")
    chatName: str = Field(..., description="聊天名称")
    messageId: Optional[str] = Field(None, description="关联的消息ID")
    floor: Optional[int] = Field(None, description="楼层号")
    
    # Token 统计
    promptTokens: int = Field(0, description="输入 token 数")
    completionTokens: int = Field(0, description="输出 token 数")
    totalTokens: int = Field(0, description="总 token 数")
    
    # 状态信息
    status: TokenUsageStatus = Field(TokenUsageStatus.COMPLETED, description="请求状态")
    errorMessage: Optional[str] = Field(None, description="错误信息（如果失败）")
    
    # 时间信息
    timestamp: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="请求时间戳")
    duration: Optional[float] = Field(None, description="请求耗时（秒）")
    
    # API 信息
    model: Optional[str] = Field(None, description="使用的模型")
    apiProvider: Optional[str] = Field(None, description="API 提供商")
    apiUrl: Optional[str] = Field(None, description="API URL地址")


# ==================== 图片元数据 ====================

class ImageMetadata(BaseModel):
    """
    图片元数据
    
    记录生成的图片信息，绑定到角色/聊天的特定楼层
    """
    id: str = Field(..., description="图片唯一标识符 (UUID)")
    chatId: str = Field(..., description="聊天ID (role_name/chat_name)")
    roleName: str = Field(..., description="角色名称")
    chatName: str = Field(..., description="聊天名称")
    floor: int = Field(..., description="楼层号")
    
    # 图片信息
    filename: str = Field(..., description="文件名")
    filepath: str = Field(..., description="文件相对路径")
    width: Optional[int] = Field(None, description="图片宽度")
    height: Optional[int] = Field(None, description="图片高度")
    fileSize: Optional[int] = Field(None, description="文件大小（字节）")
    
    # Swipe 支持
    swipeIndex: int = Field(0, description="Swipe 索引（同一楼层多张图片）")
    isCurrentSwipe: bool = Field(True, description="是否为当前显示的 swipe")
    
    # 生成信息
    prompt: Optional[str] = Field(None, description="生成使用的提示词")
    negativePrompt: Optional[str] = Field(None, description="负面提示词")
    seed: Optional[int] = Field(None, description="随机种子")
    model: Optional[str] = Field(None, description="使用的模型/checkpoint")
    workflowName: Optional[str] = Field(None, description="使用的工作流名称")
    
    # 任务信息
    taskId: Optional[str] = Field(None, description="关联的任务ID")
    generationTime: Optional[float] = Field(None, description="生成耗时（秒）")
    
    # 时间信息
    createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
