// ST-BME: 节点类型 Schema 定义
// 定义图谱中支持的节点类型、字段、注入策略和压缩配置

/**
 * 压缩模式
 */
export const COMPRESSION_MODE = {
  NONE: "none",
  HIERARCHICAL: "hierarchical",
};

/**
 * 默认节点类型 Schema
 * 每种类型定义了：
 * - id: 唯一标识
 * - label: 显示名称
 * - tableName: 注入时的表名
 * - columns: 字段列表 [{name, hint, required}]
 * - alwaysInject: 是否常驻注入（true=Core, false=需要召回）
 * - latestOnly: 是否只保留最新版本（用于角色/地点等随时间更新的实体）
 * - forceUpdate: 每次提取是否必须产出此类型节点
 * - compression: 压缩配置
 */
export const DEFAULT_NODE_SCHEMA = [
  {
    id: "event",
    label: "事件",
    tableName: "event_table",
    columns: [
      { name: "title", hint: "简短事件名（建议 6-10 字，用于图谱显示）", required: false },
      { name: "summary", hint: "事件摘要，包含因果关系和结果", required: true },
      { name: "participants", hint: "参与角色名，逗号分隔", required: false },
      {
        name: "status",
        hint: "事件状态：ongoing/resolved/blocked",
        required: false,
      },
    ],
    alwaysInject: true,
    latestOnly: false,
    forceUpdate: true,
    compression: {
      mode: COMPRESSION_MODE.HIERARCHICAL,
      threshold: 9,
      fanIn: 3,
      maxDepth: 10,
      keepRecentLeaves: 6,
      instruction:
        "将事件节点压缩为高价值的剧情里程碑摘要。保留因果关系、不可逆结果和未解决的伏笔。",
    },
  },
  {
    id: "character",
    label: "角色",
    tableName: "character_table",
    columns: [
      { name: "name", hint: "角色名（仅规范名称）", required: true },
      { name: "traits", hint: "稳定的性格特征和外貌标记", required: false },
      { name: "state", hint: "当前状态或处境", required: false },
      { name: "goal", hint: "当前目标或动机", required: false },
      { name: "inventory", hint: "携带或拥有的关键物品", required: false },
      { name: "core_note", hint: "值得长期记住的关键备注", required: false },
    ],
    alwaysInject: false,
    latestOnly: true,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.NONE,
      threshold: 0,
      fanIn: 0,
      maxDepth: 0,
      keepRecentLeaves: 0,
      instruction: "",
    },
  },
  {
    id: "location",
    label: "地点",
    tableName: "location_table",
    columns: [
      { name: "name", hint: "地点名称（仅规范名称）", required: true },
      { name: "state", hint: "当前状态或环境条件", required: false },
      { name: "features", hint: "重要特征、资源或服务", required: false },
      { name: "danger", hint: "危险等级或威胁", required: false },
    ],
    alwaysInject: false,
    latestOnly: true,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.NONE,
      threshold: 0,
      fanIn: 0,
      maxDepth: 0,
      keepRecentLeaves: 0,
      instruction: "",
    },
  },
  {
    id: "rule",
    label: "规则",
    tableName: "rule_table",
    columns: [
      { name: "title", hint: "简短规则名", required: true },
      { name: "constraint", hint: "不可违反的规则内容", required: true },
      { name: "scope", hint: "适用范围/场景", required: false },
      {
        name: "status",
        hint: "当前有效性：active/suspended/revoked",
        required: false,
      },
    ],
    alwaysInject: true,
    latestOnly: false,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.NONE,
      threshold: 0,
      fanIn: 0,
      maxDepth: 0,
      keepRecentLeaves: 0,
      instruction: "",
    },
  },
  {
    id: "thread",
    label: "主线",
    tableName: "thread_table",
    columns: [
      { name: "title", hint: "主线名称", required: true },
      { name: "summary", hint: "当前进展摘要", required: false },
      {
        name: "status",
        hint: "状态：active/completed/abandoned",
        required: false,
      },
    ],
    alwaysInject: true,
    latestOnly: false,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.HIERARCHICAL,
      threshold: 6,
      fanIn: 3,
      maxDepth: 5,
      keepRecentLeaves: 3,
      instruction: "将主线节点压缩为阶段性进展摘要。保留关键转折和当前目标。",
    },
  },
  // ====== v2 新增节点类型 ======
  {
    id: "synopsis",
    label: "全局概要（旧）",
    tableName: "synopsis_table",
    columns: [
      {
        name: "summary",
        hint: "旧式单条全局前情提要（兼容 / 迁移兜底）",
        required: true,
      },
      { name: "scope", hint: "该旧式概要覆盖的楼层范围", required: false },
    ],
    alwaysInject: true, // 常驻注入（MemoRAG 启发）
    latestOnly: true, // 只保留最新版本
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.NONE,
      threshold: 0,
      fanIn: 0,
      maxDepth: 0,
      keepRecentLeaves: 0,
      instruction: "",
    },
  },
  {
    id: "reflection",
    label: "反思",
    tableName: "reflection_table",
    columns: [
      { name: "insight", hint: "对角色行为或情节的元认知反思", required: true },
      { name: "trigger", hint: "触发反思的事件/矛盾", required: false },
      { name: "suggestion", hint: "对后续叙事的建议", required: false },
    ],
    alwaysInject: false, // 需要被召回
    latestOnly: false,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.HIERARCHICAL,
      threshold: 6,
      fanIn: 3,
      maxDepth: 3,
      keepRecentLeaves: 3,
      instruction: "将反思条目合并为高层次的叙事指导原则。",
    },
  },
  {
    id: "pov_memory",
    label: "主观记忆",
    tableName: "pov_memory_table",
    columns: [
      { name: "summary", hint: "这个视角如何记住这件事", required: true },
      { name: "belief", hint: "她/他认为发生了什么", required: false },
      { name: "emotion", hint: "主观情绪反应", required: false },
      { name: "attitude", hint: "对人物或事件的态度", required: false },
      {
        name: "certainty",
        hint: "确定度：certain/unsure/mistaken",
        required: false,
      },
      { name: "about", hint: "关联对象或引用标签", required: false },
    ],
    alwaysInject: false,
    latestOnly: false,
    forceUpdate: false,
    compression: {
      mode: COMPRESSION_MODE.HIERARCHICAL,
      threshold: 8,
      fanIn: 3,
      maxDepth: 4,
      keepRecentLeaves: 4,
      instruction:
        "将同一视角、同一角色归属下的主观记忆压缩成更稳定的第一视角记忆摘要，保留误解、情绪和态度变化。",
    },
  },
];

/**
 * 规范化的关系类型
 */
export const RELATION_TYPES = [
  "related", // 一般关联
  "involved_in", // 参与事件
  "occurred_at", // 发生于地点
  "advances", // 推进主线
  "updates", // 更新实体状态
  "contradicts", // 矛盾/冲突（用于抑制边）
  "evolves", // A-MEM 进化链接（新→旧）
  "temporal_update", // 时序更新（Graphiti：新状态替代旧状态）
];

/**
 * 验证 Schema 配置的合法性
 * @param {Array} schema
 * @returns {{valid: boolean, errors: string[]}}
 */
export function validateSchema(schema) {
  const errors = [];

  if (!Array.isArray(schema) || schema.length === 0) {
    errors.push("Schema 必须是非空数组");
    return { valid: false, errors };
  }

  const ids = new Set();
  const tableNames = new Set();

  for (const type of schema) {
    if (!type || typeof type !== "object") {
      errors.push("Schema 类型定义必须是对象");
      continue;
    }

    if (!type.id || typeof type.id !== "string") {
      errors.push("每种类型必须有 id");
      continue;
    }

    if (ids.has(type.id)) {
      errors.push(`类型 ID 重复：${type.id}`);
    }
    ids.add(type.id);

    if (!type.label || typeof type.label !== "string") {
      errors.push(`类型 ${type.id}：缺少 label`);
    }

    if (!type.tableName || typeof type.tableName !== "string") {
      errors.push(`类型 ${type.id}：缺少 tableName`);
    } else if (tableNames.has(type.tableName)) {
      errors.push(`表名重复：${type.tableName}`);
    } else {
      tableNames.add(type.tableName);
    }

    if (!Array.isArray(type.columns) || type.columns.length === 0) {
      errors.push(`类型 ${type.id}：至少需要一个列`);
      continue;
    }

    const columnNames = new Set();
    for (const column of type.columns) {
      if (!column?.name || typeof column.name !== "string") {
        errors.push(`类型 ${type.id}：存在缺少 name 的列定义`);
        continue;
      }
      if (columnNames.has(column.name)) {
        errors.push(`类型 ${type.id}：列名重复 ${column.name}`);
      }
      columnNames.add(column.name);
    }

    const hasRequired = type.columns.some((c) => c?.required);
    if (!hasRequired) {
      errors.push(`类型 ${type.id}：至少需要一个 required 列`);
    }

    if (type.latestOnly) {
      const hasPrimaryLikeField = ["name", "title", "summary"].some(
        (fieldName) =>
          type.columns.some((column) => column?.name === fieldName),
      );
      if (!hasPrimaryLikeField) {
        errors.push(
          `类型 ${type.id}：latestOnly 类型至少需要 name/title/summary 之一作为主标识字段`,
        );
      }
    }
  }

  return { valid: errors.length === 0, errors };
}

/**
 * 获取 Schema 中某个类型的定义
 * @param {Array} schema
 * @param {string} typeId
 * @returns {object|null}
 */
export function getSchemaType(schema, typeId) {
  return schema.find((t) => t.id === typeId) || null;
}
