"""
Prompt模板

功能：创建Cypher生成、验证、校正的prompt模板
"""
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from neo4j import Driver
# ========== Cypher生成prompt（带 CoT、Few-shot 和 Schema 路径模板） ==========
generate_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一个严格的 Cypher 明细查询生成器，用于返回具体记录（非聚合）。必须 100% 遵守以下规则。\n\n"
        "### ⚠️ 绝对禁止（出现即错误）\n"
        "❌ 使用有向关系（如 `->`），必须写成 `-[:REL]-`\n"
        "❌ 使用 schema 未定义的标签、关系或属性名\n"
        "❌ 使用 `OPTIONAL MATCH`、`UNWIND`、子查询、`CALL` 或多个 `MATCH`\n"
        "❌ 使用 `WITH`（除非用于 `DISTINCT` 去重，且仅当必要时）\n"
        "❌ 返回非 `... AS result` 的别名（如 `AS data`、`AS details`）\n"
        "❌ 使用聚合函数（如 count, sum, avg）——本类查询只返回明细记录！\n"
        "❌ 节点未绑定变量（例如 `(:设备)` 必须写成 `(e:设备)`）\n\n"
        "### ✅ 必须遵守\n"
        "✅ 所有节点在 MATCH 中必须显式声明别名（变量），即使不直接使用（如 `(s:舰艇)`, `(sys:系统)`）\n"
        "✅ 允许使用时间属性（如 fault_time、发生时间）和日期条件（如 = '2024-01-01'）\n"
        "✅ 仅当用户明确提到实体名称（如\"发动机\"）时，才使用 `名称 IN [...]`\n"
        "✅ 所有条件（包括时间条件）必须用 AND 连接，放在单一 WHERE 子句中\n"
        "✅ **优先使用少跳数完成查询。若单节点即可满足条件，不要强行连接其他节点。**\n"
        "✅ 返回格式：`RETURN DISTINCT node AS result`（node 必须是已绑定的变量）\n"
        "✅ 若涉及多个节点，返回用户关心的主目标节点（如“故障”、“监测记录”）的属性\n\n"
        "### Schema（唯一权威，不得推测）\n{schema}\n\n"
        "### 生成步骤\n"
        "1. 判断是否提及具体实体 → 决定是否用 `名称 IN [...]`\n"
        "2. 从 schema 提取相关标签/关系/属性（包括时间属性）\n"
        "3. 构建无向路径，**为每个节点分配清晰别名**（如 s, sys, e, f）\n"
        "4. 写单一 MATCH + 单一 WHERE（可含时间条件）\n"
        "5. RETURN ... AS result\n\n"
        "### 正确示例\n"
        "• 发动机在2024年的所有故障详情：\n"
        "  ✅ MATCH (start_e:设备 {{名称: '主发动机'}})-[:包含*0..8]-(real_e:设备)-[:发生故障使用维修工作]-(w:维修工作)-[:包含故障]-(f:故障模式) WHERE f.发生时间 >= '2024-01-01' AND f.发生时间 <= '2024-12-31' RETURN DISTINCT f AS result\n\n"
        "• 主发动机可以实现什么功能：\n"
        "  ✅ MATCH (start_e:设备 {{名称: '主发动机'}})-[:实现]-(p:功能) RETURN DISTINCT p AS result\n\n"
        "• 主发动机的组成：\n"
        "  ✅ MATCH (start_e:设备 {{名称: '主发动机'}})-[:包含]-(p:组成) RETURN DISTINCT p AS result\n\n"
        "• 某型舰艇的所有故障：\n"
        "  ✅ MATCH (s:舰艇 {{名称: '某型舰艇'}})-[:包含]-(sys:系统)-[:包含]-(sub_sys:子系统)-[:包含*1..5]-(e:设备)-[:发生故障使用维修工作]-(w:维修工作)-[:包含故障]-(f:故障模式) RETURN DISTINCT f AS result\n\n"
        "• 主发动机发生排烟温度过高如何维修："
        "  ✅ MATCH (start_e:设备 {{名称: '主发动机'}})-[:包含*0..8]-(real_e:设备)-[:发生故障使用维修工作]-(w:维修工作)-[:包含故障]-(f:故障模式{{名称: '排烟温度过高'}})-[:修理]-(p:维修项目) RETURN DISTINCT p AS result\n\n"
        "### ❌ 典型错误（绝对不要做）\n"
        "• MATCH (:舰艇 {{名称: 'X'}})-[:包含]-(:系统) → 节点未绑定别名\n"
        "• RETURN f → 未使用 AS result\n"
        "• MATCH (e:设备), (f:故障) WHERE ... → 多个独立 MATCH\n"
        "• 使用 OPTIONAL MATCH → 违反结构限制\n"
        "• 关系写成 --> 或 <-- → 使用了有向关系\n"
        "• 返回未绑定的变量（如 fault）→ 变量必须在 MATCH 中声明\n\n"
        "→ 注意：本查询类型**允许时间字段**，但依然禁止聚合操作。"
    ),
    HumanMessagePromptTemplate.from_template(
        "【重要】再次提醒：\n"
        "- **每个节点必须绑定别名**，例如 `(s:舰艇)` 而非 `(:舰艇)`\n"
        "- **允许使用时间字段和日期条件**（如 fault_time, '2024-01-01'）\n"
        "- **严禁使用聚合函数**（如 count）——这是明细查询！\n"
        "- 关系必须无方向（用 `-[:REL]-`）\n"
        "- 返回必须是 `... AS result`！\n"
        "- 仅一个 MATCH，无 OPTIONAL MATCH / UNWIND / CALL！\n"
        "- 可使用 ORDER BY 和 LIMIT（如用户要求“最近”“最新”）\n\n"
        "入口节点（NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户问题:\n{query}\n\n"
        "→ 请直接输出【完整 Cypher 语句】，不要解释，不要注释。"
    ),
])

generate_aggregate_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一个严格的 Cypher 统计查询生成器，仅处理**不含任何时间信息**的聚合问题。必须 100% 遵守以下规则。\n\n"
        "### ⚠️ 绝对禁止（出现即错误）\n"
        "❌ 使用任何时间属性（如 fault_time、发生时间）或日期字符串（如 '2024'）\n"
        "❌ 使用有向关系（如 `->`），必须写成 `-[:REL]-`\n"
        "❌ 使用 schema 未定义的标签、关系或属性名\n"
        "❌ 使用 `OPTIONAL MATCH`、`UNWIND`、子查询、`CALL` 或多个 `MATCH`\n"
        "❌ 使用 `WITH`（除非用于 `DISTINCT` 去重，且仅当必要时）\n"
        "❌ 返回非 `... AS result` 的别名（如 `AS total`、`AS count_f`）\n\n"
        "### ✅ 必须遵守\n"
        "✅ 所有节点在 MATCH 中必须显式声明别名（变量），即使不直接使用（如 `(s:舰艇)`, `(sys:系统)`）\n"
        "✅ 仅当用户明确提到实体名称（如\"发动机\"）时，才使用 `名称 IN [...]`\n"
        "✅ 否则（如“总共有多少故障？”），直接 MATCH 目标标签，忽略入口节点\n"
        "✅ 所有条件（如 severity = 'high'）必须用 AND 连接，放在单一 WHERE 子句中\n"
        "✅ 路径最多 5 跳；若无法连接，退化为单节点匹配\n"
        "✅ **优先使用少跳数完成查询。若单节点即可满足条件（如统计所有故障或查某属性），不要强行连接其他节点。**\n"
        "✅ 返回格式：`RETURN agg_expr AS result`\n\n"
        "### Schema（唯一权威，不得推测）\n{schema}\n\n"
        "### 生成步骤\n"
        "1. 判断是否提及具体实体 → 决定是否用 `名称 IN [...]`\n"
        "2. 从 schema 提取相关标签/关系/非时间属性\n"
        "3. 构建 ≤5 跳无向路径\n"
        "4. 写单一 MATCH + 单一 WHERE（仅非时间条件）\n"
        "5. RETURN ... AS result\n\n"
        "### 正确示例\n"
        "• 主发动机的故障总数：\n"
        "  ✅ MATCH (start_e:设备 {{名称: '主发动机'}})-[:包含*0..8]-(real_e:设备)-[:发生故障使用维修工作]-(w:维修工作)-[:包含故障]-(f:故障模式) RETURN count(DISTINCT f) AS result\n\n"
        "• 发动机的故障总数：\n"
        "  ✅ MATCH (e:设备 {{名称: '发动机'}})-[:导致]-(f:故障模式) RETURN count(DISTINCT f) AS result\n\n"
        "• 技术状态为\"在役\"的设备数量：\n"
        "  ✅ MATCH (e:设备) WHERE e.技术状态 = '在役' RETURN count(DISTINCT e) AS result\n\n"
        "• 螺旋桨和发动机的故障总数：\n"
        "  ✅ MATCH (e:设备)-[:包含*0..8]-(real_e:设备)-[:发生故障使用维修工作]-(w:维修工作)-[:包含故障]-(f:故障模式) WHERE e.名称 IN ['螺旋桨', '发动机'] RETURN count(DISTINCT f) AS result\n\n"
        "### ❌ 典型错误（绝对不要做）\n"
        "• WHERE f.发生时间 > '2024' → 包含时间字段（本类查询严禁！）\n"
        "• RETURN count(f) AS total → 别名不是 'result'\n"
        "• MATCH (e:设备 {{名称: '发动机'}}), (f:故障) → 用了两个独立 MATCH\n"
        "• 使用 OPTIONAL MATCH → 违反结构限制\n\n"
        "→ 若问题隐含时间（如“最近”“今年”），**不要生成此类型查询**，应交由时间类 prompt 处理。"
    ),
    HumanMessagePromptTemplate.from_template(
        "【重要】再次提醒：\n"
        "- **每个节点必须绑定别名**，例如 `(s:舰艇)` 而非 `(:舰艇)`\n"
        "- 关系必须无方向！\n"
        "- 返回必须是 `... AS result`！\n"
        "- 仅一个 MATCH，无 WITH/OPTIONAL MATCH！\n\n"
        "入口节点（NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户问题:\n{query}\n\n"
        "→ 请直接输出【完整 Cypher 语句】，不要解释，不要注释。"
    ),
])

generate_single_time_aggregate_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一个严格的单时间段统计 Cypher 生成器。仅处理含**一个明确时间范围**的计数问题（如“2024年”“本月”“截至某日”）。必须 100% 遵守以下规则。\n\n"
        "### ⚠️ 绝对禁止（出现即错误）\n"
        "❌ 使用任何时间函数（如 datetime(), date(), timestamp()）\n"
        "❌ 使用有向关系（如 `->`），必须写成 `-[:REL]-`\n"
        "❌ 使用 schema 未定义的属性名（如写成 'time' 而非 'fault_time'）\n"
        "❌ 使用多个 MATCH、OPTIONAL MATCH、WITH、UNWIND 或子查询\n"
        "❌ 返回非 `count(...) AS result` 的表达式\n\n"
        "### ✅ 必须遵守\n"
        "✅ 时间比较必须使用字符串字面量，格式如 `'2024-01-01'`\n"
        "✅ 年/月区间必须为左闭右开：\n"
        "    • \"2024年\" → >= '2024-01-01' AND < '2025-01-01'\n"
        "    • \"2024年6月\" → >= '2024-06-01' AND < '2024-07-01'\n"
        "    • \"截至2024年底\" → <= '2024-12-31'（仅此情况用 ≤）\n"
        "✅ 相对时间映射（系统时间：{current_date_info}）：\n"
        "    • \"今年\" → >= '{current_year}-01-01' AND < '{next_year}-01-01'\n"
        "    • \"本月\" → >= '{current_year}-{current_month:02d}-01' AND < '{next_month_start}'\n"
        "✅ 仅当用户明确提到实体名称时，才使用 `名称 IN [...]`\n"
        "✅ 路径最多 5 跳；否则退化为单节点\n"
        "✅ **优先使用少跳数完成查询。若单节点即可满足条件（如统计所有故障或查某属性），不要强行连接其他节点。**\n"
        "✅ 返回格式：`RETURN count(x) AS result`\n\n"
        "### Schema（唯一权威）\n{schema}\n\n"
        "### 生成步骤\n"
        "1. 判断是否使用入口节点（仅当明确提及实体名）\n"
        "2. 从 schema 提取正确的时间属性名（如 fault_time）\n"
        "3. 将时间语义解析为精确字符串区间\n"
        "4. 构建 ≤5 跳无向路径\n"
        "5. 写单一 MATCH + 单一 WHERE（含时间 + 其他非时间条件）\n"
        "6. RETURN count(...) AS result\n\n"
        "### 正确示例\n"
        "• 2024年维修工作总数：\n"
        "  ✅ MATCH (f:维修工作) WHERE f.发生时间 >= '2024-01-01' AND f.发生时间 < '2025-01-01' RETURN count(DISTINCT f) AS result\n\n"
        "• 截至2024年底的累计故障次数：\n"
        "  ✅ MATCH (f:故障模式) WHERE f.发生时间 <= '2024-12-31' RETURN count(DISTINCT f) AS result\n\n"
        "### ❌ 典型错误（绝对不要做）\n"
        "• WHERE datetime(f.发生时间) > '2024' → 用了函数\n"
        "• WHERE f.发生时间 >= '2024-01-01' AND f.发生时间 <= '2024-12-31' → 年区间应为 < '2025-01-01'\n"
        "• RETURN count(f) → 缺少 AS result\n"
        "• 使用 (e:设备 {{名称: '发动机'}}), (r:修理) → 多个独立 MATCH\n\n"
        "→ 若时间描述模糊（如“最近”），优先按“截至今日”处理，但必须用字符串！"
    ),
    HumanMessagePromptTemplate.from_template(
        "【重要】再次提醒：\n"
        "- **每个节点必须绑定别名**，例如 `(s:舰艇)` 而非 `(:舰艇)`\n"
        "- 时间必须用字符串字面量（如 '2025-01-01'），禁用任何函数！\n"
        "- 年/月区间必须左闭右开（< 下一年/月）！\n"
        "- 关系无方向，仅一个 MATCH！\n"
        "- 返回必须是 `count(...) AS result`！\n\n"
        "入口节点（NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户问题:\n{query}\n\n"
        "系统时间信息:\n{current_date_info}\n\n"
        "→ 请直接输出【完整 Cypher 语句】，不要解释，不要注释。"
    ),
])

# ========== 双时间段统计查询 Prompt（同比/环比/跨年对比） ==========
generate_dual_time_aggregate_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一个严格的双时间段对比统计 Cypher 生成器。仅处理含**两个明确时间范围**的计数对比问题（如“今年 vs 去年”“2023 vs 2024”）。必须 100% 遵守以下规则。\n\n"
        "### ⚠️ 绝对禁止（出现即错误）\n"
        "❌ 使用任何时间函数（datetime/date/timestamp）\n"
        "❌ 使用有向关系（-> 或 <-），必须写成 `-[:REL]-`\n"
        "❌ 使用 schema 未定义的属性名\n"
        "❌ 使用多个 MATCH、OPTIONAL MATCH、WITH、UNWIND 或子查询\n"
        "❌ 在 CASE WHEN 中只写一个边界（如仅 >= 而无 <）\n"
        "❌ WHERE 子句用 AND 连接两个时间段（必须用 OR）\n"
        "❌ 返回字段命名错误（见下方 ✅ 规则）\n\n"
        "### ✅ 必须遵守\n"
        "✅ 时间比较必须使用字符串字面量，格式如 `'2025-01-01'`\n"
        "✅ 每个时间段必须表示为完整左闭右开区间：>= start AND < end\n"
        "✅ WHERE 条件：(time in period1) OR (time in period2)\n"
        "✅ RETURN 必须包含两个字段，命名规则：\n"
        "    • 若为相对时间（今年/去年、本月/上月）→ `current, previous`\n"
        "    • 若为绝对年份（2023 vs 2024）→ `year_2023, year_2024`\n"
        "✅ 每个字段必须是：\n"
        "    count(CASE WHEN time >= 'start' AND time < 'end' THEN 1 END) AS name\n"
        "✅ 仅当用户明确提到实体名称时，才使用 `名称 IN [...]`\n"
        "✅ 路径最多 5 跳；否则退化为单节点\n"
        "✅ **优先使用少跳数完成查询。若单节点即可满足条件（如统计所有故障或查某属性），不要强行连接其他节点。**\n\n"
        "### Schema（唯一权威）\n{schema}\n\n"
        "### 系统时间上下文\n{current_date_info}\n\n"
        "### 生成步骤\n"
        "1. 判断是否使用入口节点（仅当明确提及实体名）\n"
        "2. 识别两个时间段类型：相对 or 绝对 → 决定返回字段名\n"
        "3. 为每个时间段生成完整 [start, end) 字符串（系统已提供）\n"
        "4. 构建 ≤5 跳无向路径\n"
        "5. 写单一 MATCH + WHERE (period1 OR period2)\n"
        "6. RETURN 两个带完整区间的 count(CASE ...) AS ...\n\n"
        "### 正确示例\n"
        "• 今年 vs 去年故障次数：\n"
        "  ✅ MATCH (f:故障模式)\n"
        "      WHERE (f.发生时间 >= '2025-01-01' AND f.发生时间 < '2026-01-01') OR\n"
        "            (f.发生时间 >= '2024-01-01' AND f.发生时间 < '2025-01-01')\n"
        "      RETURN\n"
        "        count(CASE WHEN f.发生时间 >= '2025-01-01' AND f.发生时间 < '2026-01-01' THEN 1 END) AS current,\n"
        "        count(CASE WHEN f.发生时间 >= '2024-01-01' AND f.发生时间 < '2025-01-01' THEN 1 END) AS previous\n\n"
        "• 2023 vs 2024年修理次数：\n"
        "  ✅ MATCH (r:维修工作)\n"
        "      WHERE (r.发生时间 >= '2023-01-01' AND r.发生时间 < '2024-01-01') OR\n"
        "            (r.发生时间 >= '2024-01-01' AND r.发生时间 < '2025-01-01')\n"
        "      RETURN\n"
        "        count(CASE WHEN r.发生时间 >= '2023-01-01' AND r.发生时间 < '2024-01-01' THEN 1 END) AS year_2023,\n"
        "        count(CASE WHEN r.发生时间 >= '2024-01-01' AND r.发生时间 < '2025-01-01' THEN 1 END) AS year_2024\n\n"
        "### ❌ 典型错误（绝对不要做）\n"
        "• CASE WHEN f.发生时间 >= '2025-01-01' THEN 1 END → 缺少 < '2026-01-01'\n"
        "• WHERE (period1) AND (period2) → 逻辑错误，应为 OR\n"
        "• 返回 AS this_year, last_year → 相对时间必须用 current/previous\n"
        "• 使用 WITH 或额外变量 → 违反单一 MATCH 原则\n\n"
        "→ 你必须严格复制示例中的结构。宁可输出简单正确语句，也不要‘优化’逻辑。"
    ),
    HumanMessagePromptTemplate.from_template(
        "【重要】再次提醒：\n"
        "- **每个节点必须绑定别名**，例如 `(s:舰艇)` 而非 `(:舰艇)`\n"
        "- 每个 CASE 必须同时包含 >= start AND < end！\n"
        "- WHERE 必须用 OR 连接两个时间段！\n"
        "- 相对时间 → 返回 current, previous；绝对年份 → year_YYYY！\n"
        "- 禁用任何时间函数，仅用字符串！\n"
        "- 仅一个 MATCH，无 WITH！\n\n"
        "入口节点（NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户问题:\n{query}\n\n"
        "系统时间信息（包含完整时间段字符串）:\n{current_date_info}\n\n"
        "→ 请直接输出【完整 Cypher 语句】，不要解释，不要注释。"
    ),
])

# ========== 多时间段统计查询 Prompt（3个及以上时间段） ==========
generate_multi_time_aggregate_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一个严格的多时间段趋势统计 Cypher 生成器。仅处理含**三个或以上明确年份**的计数问题（如“2023、2024、2025年”或“历年趋势”）。必须 100% 遵守以下规则。\n\n"
        "### ⚠️ 绝对禁止（出现即错误）\n"
        "❌ 使用任何时间函数（datetime/date/timestamp）\n"
        "❌ 使用有向关系（-> 或 <-），必须写成 `-[:REL]-`\n"
        "❌ 使用 schema 未定义的属性名\n"
        "❌ 使用多个 MATCH、OPTIONAL MATCH、WITH、UNWIND 或子查询\n"
        "❌ 在 CASE WHEN 中缺失 < end 边界\n"
        "❌ 返回字段命名不符合 `year_YYYY` 格式（如 y2023、count_2023）\n"
        "❌ 字段顺序不是时间升序（早 → 晚）\n"
        "❌ 默认年份计算错误（当前系统时间：{current_date_info}）\n\n"
        "### ✅ 必须遵守\n"
        "✅ 时间比较必须使用字符串字面量，格式如 `'2025-01-01'`\n"
        "✅ 每个年份对应一个完整左闭右开区间：>= 'YYYY-01-01' AND < '(YYYY+1)-01-01'\n"
        "✅ 若用户说“各年”“历年”“过去几年”且未指定年份 → 默认使用最近3年：{last_last_year}, {last_year}, {current_year}\n"
        "✅ WHERE 条件：所有时间段用 OR 连接，并用括号包裹整体时间条件\n"
        "✅ RETURN 必须包含 N 个字段，每个格式为：\n"
        "    count(CASE WHEN time >= 'YYYY-01-01' AND time < '(YYYY+1)-01-01' THEN 1 END) AS year_YYYY\n"
        "✅ 字段顺序必须按年份升序（如 year_2023, year_2024, year_2025）\n"
        "✅ 仅当用户明确提到实体名称时，才使用 `名称 IN [...]`\n"
        "✅ 路径最多 5 跳；否则退化为单节点\n"
        "✅ **优先使用少跳数完成查询。若单节点即可满足条件（如统计所有故障或查某属性），不要强行连接其他节点。**\n\n"
        "### Schema（唯一权威）\n{schema}\n\n"
        "### 系统时间上下文\n{current_date_info}\n\n"
        "### 生成步骤\n"
        "1. 判断是否使用入口节点（仅当明确提及实体名）\n"
        "2. 提取所有年份：显式列出 or 默认近3年（{last_last_year}, {last_year}, {current_year}）\n"
        "3. 为每个年份 Y 生成区间：>= 'Y-01-01' AND < '(Y+1)-01-01'\n"
        "4. 构建 ≤5 跳无向路径\n"
        "5. 写单一 MATCH + WHERE（非时间条件 AND (time in Y1 OR Y2 OR Y3)）\n"
        "6. RETURN 多个 year_YYYY 字段，按年份升序排列\n\n"
        "### 正确示例\n"
        "• 2023、2024、2025年的故障数：\n"
        "  ✅ MATCH (f:故障模式)\n"
        "      WHERE (f.发生时间 >= '2023-01-01' AND f.发生时间 < '2024-01-01') OR\n"
        "            (f.发生时间 >= '2024-01-01' AND f.发生时间 < '2025-01-01') OR\n"
        "            (f.发生时间 >= '2025-01-01' AND f.发生时间 < '2026-01-01')\n"
        "      RETURN\n"
        "        count(CASE WHEN f.发生时间 >= '2023-01-01' AND f.发生时间 < '2024-01-01' THEN 1 END) AS year_2023,\n"
        "        count(CASE WHEN f.发生时间 >= '2024-01-01' AND f.发生时间 < '2025-01-01' THEN 1 END) AS year_2024,\n"
        "        count(CASE WHEN f.发生时间 >= '2025-01-01' AND f.发生时间 < '2026-01-01' THEN 1 END) AS year_2025\n\n"
        "• 各年的修理数（默认近3年：2023,2024,2025）：\n"
        "  ✅ MATCH (r:维修工作)\n"
        "      WHERE (r.发生时间 >= '2023-01-01' AND r.发生时间 < '2024-01-01') OR\n"
        "            (r.发生时间 >= '2024-01-01' AND r.发生时间 < '2025-01-01') OR\n"
        "            (r.发生时间 >= '2025-01-01' AND r.发生时间 < '2026-01-01')\n"
        "      RETURN\n"
        "        count(CASE WHEN r.发生时间 >= '2023-01-01' AND r.发生时间 < '2024-01-01' THEN 1 END) AS year_2023,\n"
        "        count(CASE WHEN r.发生时间 >= '2024-01-01' AND r.发生时间 < '2025-01-01' THEN 1 END) AS year_2024,\n"
        "        count(CASE WHEN r.发生时间 >= '2025-01-01' AND r.发生时间 < '2026-01-01' THEN 1 END) AS year_2025\n\n"
        "### ❌ 典型错误（绝对不要做）\n"
        "• 返回 year_2025, year_2024, year_2023 → 顺序错误（应按年份升序）\n"
        "• CASE WHEN f.发生时间 >= '2025-01-01' THEN 1 END → 缺少 < '2026-01-01'\n"
        "• 使用 year2023（无下划线）→ 命名格式错误\n"
        "• 默认年份写成 2022,2023,2024（当前是 2026）→ 逻辑错误\n"
        "• WHERE 未用括号包裹 OR 条件 → 与非时间条件优先级混乱\n\n"
        "→ 你必须像复制模板一样输出。每个年份的 CASE 结构必须完全一致。"
    ),
    HumanMessagePromptTemplate.from_template(
        "【重要】再次提醒：\n"
        "- **每个节点必须绑定别名**，例如 `(s:舰艇)` 而非 `(:舰艇)`\n"
        "- 默认近3年：{last_last_year}, {last_year}, {current_year}（当前是 {current_year} 年）！\n"
        "- 每个 CASE 必须包含 >= YYYY-01-01 AND < (YYYY+1)-01-01！\n"
        "- 返回字段必须命名为 year_2023、year_2024 等，按年份升序！\n"
        "- 仅一个 MATCH，禁用 WITH/OPTIONAL MATCH！\n"
        "- 时间必须用字符串，禁用函数！\n\n"
        "入口节点（NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户问题:\n{query}\n\n"
        "系统时间信息:\n{current_date_info}\n\n"
        "→ 请直接输出【完整 Cypher 语句】，不要解释，不要注释。"
    ),
])


# ========== Cypher校正prompt（按错误类型分类） ==========

# 1. 语法错误修正 prompt（适用于统计类和明细类）
correct_syntax_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一位资深 Cypher 专家，负责修正 Cypher 查询中的**语法错误**。\n\n"
        "【错误类型】语法错误（括号不匹配、关键字错误、格式问题等）\n\n"
        "【修正原则】\n"
        "1. **仅修复语法问题**，不得改变查询的语义和意图。\n"
        "2. **保持原始查询结构**：节点标签、关系类型、属性名等保持不变。\n"
        "3. **保持原始查询类型**：\n"
        "   - 如果原查询是统计类（包含聚合函数如 count、sum 等），修正后仍应为统计类\n"
        "   - 如果原查询是明细类（返回节点/关系），修正后仍应为明细类\n"
        "4. **修复常见语法错误**：\n"
        "   - 括号不匹配 → 补充缺失的括号或删除多余的括号\n"
        "   - 关键字拼写错误 → 修正为正确的关键字（MATCH、RETURN、WHERE等）\n"
        "   - 关系定义格式错误 → 修正为 `-[:RELATIONSHIP]-` 格式\n"
        "   - 多余的括号嵌套 → 删除多余的括号\n"
        "   - 缺少逗号或空格 → 补充必要的分隔符\n"
        "5. **输出要求**：\n"
        "   - 仅返回修正后的完整 Cypher 语句，不要任何解释、注释或 Markdown。\n\n"
        "schema:\n{schema}"
    ),
    HumanMessagePromptTemplate.from_template(
        "入口节点（格式：NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户原始输入:\n{query}\n\n"
        "语法错误信息:\n{errors}\n\n"
        "待修正的 Cypher 语句:\n{cypher}\n\n"
        "请输出修正后的 Cypher 语句（仅代码，无其他内容）："
    ),
])

# 2. Schema错误修正 prompt（适用于统计类和明细类）
correct_schema_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一位资深 Cypher 专家，负责修正 Cypher 查询中的**Schema错误**。\n\n"
        "【错误类型】Schema错误（关系类型不存在、节点标签不存在、属性名不存在）\n\n"
        "【修正原则】\n"
        "1. **Schema 是唯一权威**：\n"
        "   - 所有节点标签和关系类型必须严格存在于以下 schema 中。\n"
        "   - 禁止使用 schema 中未定义的标签、关系或属性。\n"
        "2. **保持原始查询意图**：\n"
        "   - 修正仅针对错误的标签/关系/属性，不得改变用户要查询的目标。\n"
        "   - 目标节点类型必须与用户问题一致。\n"
        "3. **保持原始查询类型**：\n"
        "   - 如果原查询是统计类（包含聚合函数如 count、sum 等），修正后仍应为统计类\n"
        "   - 如果原查询是明细类（返回节点/关系），修正后仍应为明细类\n"
        "4. **修正策略**：\n"
        "   - **关系类型不存在** → 在 schema 中查找语义相近的关系类型替换\n"
        "   - **节点标签不存在** → 在 schema 中查找语义相近的节点标签替换\n"
        "   - **属性名不存在** → 使用 schema 中该节点类型实际存在的属性名（如 `name`、`名称`）\n"
        "   - 如果无法找到替代，考虑通过中间节点构建多跳路径（如 设备 → 故障 → 维修方案）\n"
        "5. **强制无方向关系**：所有关系必须写成 `-[:RELATIONSHIP]-`，禁止 `->` 或 `<-`。\n"
        "6. **输出要求**：\n"
        "   - 仅返回修正后的完整 Cypher 语句，不要任何解释、注释或 Markdown。\n\n"
        "schema:\n{schema}"
    ),
    HumanMessagePromptTemplate.from_template(
        "入口节点（格式：NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户原始输入:\n{query}\n\n"
        "Schema错误信息（关系类型/节点标签/属性名不存在）:\n{errors}\n\n"
        "待修正的 Cypher 语句:\n{cypher}\n\n"
        "请输出修正后的 Cypher 语句（仅代码，无其他内容）："
    ),
])

# 3. 空结果错误修正 prompt（适用于统计类和明细类）
correct_empty_result_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一位资深 Cypher 专家，负责修正返回空结果的 Cypher 查询。\n\n"
        "【错误类型】查询执行成功但返回空结果或0结果\n\n"
        "【修正原则】\n"
        "1. **保持原始查询意图**：不得改变用户要查询的目标。\n"
        "2. **Schema 是唯一权威**：所有标签/关系必须存在于 schema 中。\n"
        "3. **保持原始查询类型**：\n"
        "   - 如果原查询是统计类（包含聚合函数如 count、sum 等），修正后仍应为统计类\n"
        "   - 如果原查询是明细类（返回节点/关系），修正后仍应为明细类\n"
        "4. **修正策略（按优先级）**：\n"
        "   - **检查路径是否可达**：\n"
        "     • 若直接关系不存在，必须通过 schema 中存在的中间节点构建多跳路径\n"
        "     • 每一跳的关系必须在 schema 中存在，最多允许 5 跳\n"
        "   - **检查入口节点名称**：\n"
        "     • 确保使用的节点名称与入口节点列表中的名称完全一致\n"
        "     • 考虑使用 `name IN [...]` 匹配多个入口节点\n"
        "   - **如果仍无法连接**：\n"
        "     • 保留入口节点匹配，但不扩展无效路径（避免空 MATCH）\n"
        "     • 返回入口节点本身作为结果\n"
        "5. **强制无方向关系**：所有关系必须写成 `-[:RELATIONSHIP]-`，禁止 `->` 或 `<-`。\n"
        "6. **输出要求**：\n"
        "   - 仅返回修正后的完整 Cypher 语句，不要任何解释、注释或 Markdown。\n\n"
        "schema:\n{schema}"
    ),
    HumanMessagePromptTemplate.from_template(
        "入口节点（格式：NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户原始输入:\n{query}\n\n"
        "错误信息（查询返回空结果）:\n{errors}\n\n"
        "待修正的 Cypher 语句:\n{cypher}\n\n"
        "请输出修正后的 Cypher 语句（仅代码，无其他内容）："
    ),
])

# ========== 通用修正 prompt（向后兼容，用于未分类的错误） ==========
correct_cypher_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        "你是一位资深 Cypher 专家，负责根据 schema、入口节点、用户意图和执行错误，精准修正一条有缺陷的 Cypher 查询。\n\n"

        "【核心原则】\n"
        "1. **Schema 是唯一权威**：\n"
        "   - 所有节点标签、关系类型和属性必须严格存在于以下 schema 中。\n"
        "   - 禁止使用 schema 中未定义的元素。\n"
        "2. **保持原始查询意图不变**：\n"
        "   - 若原查询目标是具体实体（如故障现象），修正后仍返回该类节点；\n"
        "   - 若原查询是统计（如“有多少个”），修正后必须仍是纯聚合查询。\n"
        "3. **强制无方向关系**：\n"
        "   - 所有关系必须写成 `-[:RELATIONSHIP]-`，禁止 `->` 或 `<-`。\n"
        "4. **实体来源可靠**：\n"
        "   - 节点匹配必须使用入口节点中提供的名称（已验证存在于图谱）。\n"
        "5. **输出要求**：\n"
        "   - 仅返回修正后的完整 Cypher 语句；\n"
        "   - 不要任何解释、注释、Markdown 或额外文本；\n"
        "   - 确保语法合法、可直接在 Neo4j 中执行。\n\n"

        "【针对非聚合查询】\n"
        "- RETURN 可包含节点或属性，但属性必须存在于 schema；\n"
        "- 允许多跳路径（最多 5 跳），每跳关系须在 schema 中存在；\n"
        "- 若路径不可达，优先尝试通过中间节点重构路径；若仍失败，保留入口节点匹配但不扩展无效部分。\n\n"

        "【针对聚合查询（含 count/sum/avg 等）】\n"
        "- 必须是纯统计：仅一个 `MATCH`（可用逗号分隔多个模式）；\n"
        "- 禁止 `OPTIONAL MATCH`、`WITH`、`UNWIND`、子查询；\n"
        "- RETURN 只能包含一个聚合表达式（如 `count(*) AS total`）；\n"
        "- 若路径不可达，退化为仅匹配入口节点进行计数。\n\n"

        "【通用修正策略】\n"
        "- 标签/关系不在 schema → 替换为 schema 中存在的等效项；\n"
        "- 属性不存在 → 仅保留通用属性（如 `名称`）或移除条件；\n"
        "- 语法错误 → 修复括号、关键字、RETURN 缺失等；\n"
        "- 有向关系 → 改为无方向 `-[:REL]-`。\n\n"

        "schema:\n{schema}"
    ),
    HumanMessagePromptTemplate.from_template(
        "入口节点（格式：NodeLabel: [name1, name2, ...]）:\n{entry_nodes}\n\n"
        "用户原始输入:\n{query}\n\n"
        "执行错误信息:\n{errors}\n\n"
        "待修正的 Cypher 语句:\n{cypher}\n\n"
        "请输出修正后的 Cypher 语句（仅代码，无其他内容）："
    ),
])
correct_aggregate_cypher_prompt = correct_cypher_prompt


# ========== 路由标签识别 Prompt（实体抽取和查询重写） ==========
def build_schema_section(schema: str = None) -> str:
    """
    构建schema部分（供路由标签识别使用）
    
    Args:
        schema: Neo4j图谱schema信息（可选）
        
    Returns:
        str: schema部分的字符串
    """
    if not schema:
        return ""
    return f"""
        【重要：图谱Schema结构（必须仔细阅读）】
        以下是从Neo4j知识图谱中提取的完整schema结构，包含了所有可用的节点标签（Node Labels）和关系类型（Relationship Types）。
        **你必须根据这个schema来识别实体应该属于哪个节点标签，确保标签名称与schema中的节点标签完全一致。**

        {schema}

        【关键要求】
        - 你选择的label必须与schema中列出的节点标签完全匹配（大小写敏感）
        - 如果schema中没有某个标签，不要使用该标签
        - 仔细查看schema中的节点标签列表，确保选择的标签是实际存在的

        """


def get_extract_entities_prompt(query: str, optional_labels: str, schema: str = None) -> str:
    schema_section = build_schema_section(schema) if schema else ""

    prompt = f"""你是一个智能检索路由Agent，负责从用户自然语言问题中准确抽取**主语实体**及其对应的**图谱节点标签**。
    
### 🎯 核心任务
从用户问题中识别出**查询的起点实体（主语）**，并为其分配一个或多个**合法的节点标签**（必须严格来自图谱schema）。

### ⚠️ 关键原则（违反任一将导致错误）
1. **主语 ≠ 查询目标**  
   - 示例：  
     - ❌ "声呐基阵系统由哪些零部件组成？" → 主语不是"零部件"  
     - ✅ 主语是"声呐基阵系统"，标签应为 "系统"（依schema而定）  
     - 查询目标（如"零部件"、"维修项目"）由后续图遍历处理，**不得作为当前主语的标签**

2. **标签必须存在于schema中**  
   - 大小写敏感，名称必须完全一致  
   - 若schema未定义某标签（如"子系统"），即使语义合理也**禁止使用**（实际图谱中存在"系统"标签）

3. **允许多标签候选**  
   - 当实体可能属于多个合法标签时，可输出多个条目  
   - 每个标签都必须在schema中真实存在

4. **禁止模糊占位符**  
   - 不得使用 "Unknown"、"Other"、"不确定"、"" 等无效标签

{schema_section}
【可选标签说明（仅作语义参考，最终以schema为准）】
{optional_labels}

### 📤 输出要求
请严格按照以下 JSON 格式输出，不要包含任何其他文字、解释或 Markdown：

{{
  "entities": [
    {{"label": "节点类型", "entity": "实体名称"}}
  ]
}}

注意：
- 必须是合法的 JSON；
- 不要使用 ```json 或任何代码块；
- 如果没有匹配项，返回 {{"entities": []}}；

用户输入:
{query}"""
    return prompt


def get_rewrite_query_prompt(query: str, schema: str = None) -> str:
    schema_section = build_schema_section(schema) if schema else ""

    prompt = f"""你是一个专业的智能检索路由Agent，负责将用户自然语言问题**重写为标准查询语句**，并进行**精确分类**，以支持知识图谱检索或后置计算。

{schema_section}

### 🔍 任务一：判断 query_type（三选一，优先级从高到低）

| 类型        | 判断依据 |
|-------------|--------|
| **complex** | 问题涉及 **“备品备件需求量”** 或其他**图谱无法直接计算的指标**（如预测、优化、动态库存等） |
| **aggregate** | 涉及 **统计、计数、聚合、比较、存在性**：<br>• 词汇：多少、几个、总数、占比、平均、最大值、是否存在、有没有<br>• 时间对比：同比、环比、相比去年<br>• 排名：前N名、排行 |
| **detail**   | 要求 **具体实体列表或详情**：<br>• 词汇：有哪些、列出、显示、是什么、包含哪些 |

> 💡 判断顺序：先检查是否为 complex → 否则按 aggregate/detail 规则判断

---

### ✍️ 任务二：重写规则（按 query_type 分类处理）

#### ▶ 若为 **detail** 或 **aggregate**：
- **保留所有限定条件**（时间、状态、属性等）  
  ✅ 正确："2025年新增的声呐基阵系统有哪些？"  
  ❌ 错误："有哪些声呐基阵系统？"
   - **术语标准化**（仅当明确对应图谱实体时）：  
     • "组件" → "零部件"  
     • "维修项目" → "维修项目"（保持原样，图谱中存在此节点类型）  
     • "故障记录" → "故障"  
     > 注意：仅在上下文明确指代图谱实体时替换（如"研发项目"不替换）
- **保持自然语言结构**，不强制改为问句

#### ▶ 若为 **complex**：
执行 **“意图降级”**：将“求指标”问题转为“列原始实体”问题
- **保留所有原始条件**（时间、系统、状态等）
- **应用术语标准化**
- **改写为目标实体的明细查询**

✅ 示例：  
- 输入："发动机备品备件的需求量是多少？"  
- 输出："列出所有发动机的备品备件。"  
- 输入："今年螺旋桨所需备件数量？"  
- 输出："列出2025年螺旋桨所需的所有备品备件。"

> ⚠️ 即使原问题含“多少”“需求量”，complex 类必须输出 **可直接图谱查询的实体列表请求**

---

### 🏷️ 任务三：分类字段（如实填写）

- **has_time**:  
  `true` 若含：年/月/日、今年/去年、2024年、近一年、Q1、同比 等  
  `false` 否则

- **time_type**（仅当 query_type="aggregate" 且 has_time=true 时有效）：  
  - `"single"`：单一时间段（如“今年”）  
  - `"dual"`：两个时间段对比（如“同比”“相比去年”）  
  - `"multi"`：多个时间段（如“各季度”）  
  - `"null"`：其他情况（包括 complex 或 detail 类）

> ✅ 即使 query_type="complex"，也需正确填写 has_time 和 time_type

---

### 📤 输出格式（严格 JSON，无额外内容）

{{
  "rewritten_query": "重写后的标准查询语句",
  "query_classification": {{
    "query_type": "detail|aggregate|complex",
    "has_time": true|false,
    "time_type": "single|dual|multi|null"
  }}
}}

⚠️ 输出必须满足：
- **仅输出一个合法 JSON 对象**，严格遵循以下结构；
- **不要包含任何其他文字、说明、注释、换行、反引号或 Markdown**；
- **不要使用 ```json 或任何代码块包裹**；
- **确保 JSON 可被 Python json.loads() 直接解析**；
- 若无法判断，使用默认值：query_type="detail", has_time=false, time_type=null。

用户输入：{query}
"""
    return prompt

def get_all_ontology_labels(driver: Driver) -> list[str]:
    """
    从 Neo4j 图数据库动态获取所有节点标签（即本体类别）。
    替换原来的硬编码列表。
    """
    with driver.session() as session:
        result = session.run("CALL db.labels() YIELD label RETURN label")
        labels = [record["label"] for record in result]
        return sorted(labels)  # 可选：排序便于调试


def get_all_relationship_types(driver: Driver) -> list[str]:
    """
    从 Neo4j 图数据库动态获取所有关系类型。
    替换原来的硬编码列表。
    """
    with driver.session() as session:
        result = session.run("CALL db.relationshipTypes() YIELD relationshipType RETURN relationshipType")
        rel_types = [record["relationshipType"] for record in result]
        return sorted(rel_types)  # 可选：排序
