/**
 * ai-chat-stream Edge Function
 * C端消费者聊天 - 直接大模型流式输出（支持多轮对话记忆 + 主动澄清 + 订单/售后/FAQ查询）
 *
 * 通过 AI Hub（OpenAI 兼容 API）直接调用大模型，不再经过 Dify/Coze 中间层。
 * 无 AI Hub 时回退到 Lovable Gateway。
 *
 * v4 更新（2026-06-18）：
 * - done 事件新增 action/actionPayload 字段，支持触发前端行为（弹出售后表单等）
 * - done 事件新增 components 数组，支持返回 UI 组件调度指令（与 B端 GenUI 对齐）
 * - System Prompt 增强：AFTERSALES 场景引导 LLM 返回 action 和 actionPayload
 *
 * v3 更新（2026-06-18）：
 * - 订单号自动提取（正则匹配多种格式）并从 orders 表查询真实订单数据
 * - 售后查询：按订单ID + 用户ID 从 after_sales_requests 表查询售后记录
 * - FAQ 知识库搜索：从 knowledge_base 表 ilike 全文匹配常见问题
 * - 预查询+上下文注入模式：LLM 调用前并行查询数据，注入 system prompt 防幻觉
 *
 * v2 更新：
 * - 从 messages 表加载对话历史，注入 LLM 上下文（多轮记忆）
 * - 系统提示词增强：意图模糊时主动追问澄清（CLARIFY intent）
 * - 支持 localConversationId 定位本地会话
 */

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-trace-id, x-user-id',
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
};

interface ChatRequest {
  message: string;
  conversationId?: string;        // AI 服务商的远程会话 ID
  localConversationId?: string;   // ★ 本地 conversations 表的主键 UUID
  inputs: {
    user_id?: string;
    trace_id: string;
    user_context?: string;
    catalog?: Array<{
      id: string;
      name: string;
      price: number;
      stock_quantity?: number;
      category?: string;
      description?: string;
    }>;
  };
}

// ─── AI Hub / LLM 配置 ───
const AI_HUB_KEY = Deno.env.get('AI_HUB_API_KEY') || '';
const AI_HUB_BASE = (Deno.env.get('AI_HUB_BASE_URL') || '').replace(/\/+$/, '');
const AI_HUB_MODEL = Deno.env.get('AI_HUB_DEFAULT_CHAT_MODEL') || 'agent-default';
const LOVABLE_KEY = Deno.env.get('LOVABLE_API_KEY') || '';
const USE_AI_HUB = !!(AI_HUB_KEY && AI_HUB_BASE);

// ─── 对话历史类型 ───
type HistoryMessage = { role: 'user' | 'assistant'; content: string };

// ─── 系统提示词构建 ───
function buildSystemPrompt(catalog: any[], userContext: string, dataContext = ''): string {
  const catalogSection = catalog.length > 0
    ? `\n\n## 可用产品目录（共${catalog.length}个，展示前30个）\n${
        catalog.slice(0, 30).map(p =>
          `• ID="${p.id}" | ${p.name} | ¥${p.price} | 库存:${p.stock_quantity ?? '未知'} | ${p.category || ''} | ${(p.description || '').substring(0, 60)}`
        ).join('\n')
      }\n\n严格规则：product_id 必须从上面目录中逐字符精确复制，禁止编造、简化或截断UUID！`
    : '\n\n（当前无可用产品目录）';

  const contextSection = userContext
    ? `\n\n## 用户上下文\n${userContext}`
    : '';

  const dataContextSection = dataContext
    ? `\n\n${dataContext}\n\n**重要：以上订单/售后/FAQ数据均为真实数据，你必须据此回复，禁止编造任何订单号、状态、金额！**`
    : '';

  return `你是"小智"，千匠商城的智能购物顾问。

## 核心规则
1. 产品ID必须从产品目录中逐字符精确复制，严禁编造或生成——你无法凭空生成有效的UUID，编造必然导致错误
2. 所有价格、库存以目录为准，不可臆造
3. 回复使用友好、专业、简洁的中文
4. 不透露你是AI
5. 理解对话历史，记住用户之前说过的话——结合上下文做推荐和回复
6. 当系统提供了用户的订单/售后数据时，直接引用真实数据回复；当没有数据时，引导用户提供订单号查询
7. **禁止生成任何URL/链接**：回复正文中绝对不能出现任何链接地址。产品推荐统一通过下方JSON的picks字段返回，前端会自动渲染产品卡片。绝对禁止输出任何形式的网址、域名或链接
8. **禁止编造标识符**：你无法生成有效的UUID、订单号、物流单号。这些必须从系统提供的目录/数据中精确复制。如果目录中没有合适的UUID，picks留空即可，不要编造

${catalogSection}${contextSection}${dataContextSection}

## 澄清规则（重要）
当用户意图模糊、信息不足时，你必须**主动追问澄清**，而不是盲目推荐。需要澄清的典型场景：

1. **购物意图模糊**：用户说"想买个手机"但没提预算、品牌偏好、用途 → 追问预算范围、偏好品牌/功能、主要用途
2. **对比请求缺标准**：用户说"哪个更好"但没说看重什么 → 追问用户最在意的因素（价格/性能/外观/售后）
3. **售后请求缺信息**：用户提退货/维修但没说订单号 → 友好索要订单号；如果系统已经查询到订单数据则直接基于数据回复
4. **预算不明确**：用户只说"便宜的"/"好一点的" → 追问具体预算区间
5. **模糊指代**：用户说"刚才那个"、"太贵了有便宜的吗" → 结合对话历史定位之前推荐的产品
6. **序数指代**：用户说"第一个"、"第二个"、"第N个"、"最后一个"、"下单第三个" → 必须回顾上一轮对话中你推荐的 picks/商品列表，按顺序定位对应商品。例如上一轮你推荐了产品A、产品B、产品C，用户说"第二个"就是指产品B

澄清要求：
- 每次最多问2-3个关键问题，不要连珠炮式提问
- 语气友好，先共情再追问（示例："我理解您想找一款手机，为了给您更精准的推荐，想了解一下您的预算和品牌偏好？"）
- 澄清后 JSON 中标记 intent: "CLARIFY"
- clarifies 字段列出具体追问点（数组，每个一条追问）

## 回复末尾必须包含JSON
\`\`\`json
{"picks":[...],"intent":"SHOPPING|COMPARE|AFTERSALES|GENERAL|CLARIFY","highlights":[...],"clarifies":[...],"title":"...","extractedOrderNumber":"用户消息中提及的订单号，无则填null","action":"show_after_sales_form|...","actionPayload":{...},"components":[...]}
\`\`\`

## 规则
- picks 最多5个，product_id 必须从目录逐字符复制；澄清模式下 picks 为空数组
- intent: SHOPPING(推荐购物)、COMPARE(对比)、AFTERSALES(售后)、GENERAL(一般)、CLARIFY(主动澄清追问)
- clarifies: 当 intent=CLARIFY 时必填，列出2-3个具体追问问题
- highlights: 2-4个回复要点（各20-80字）；澄清模式下为对话摘要
- title: 简短对话标题（≤30字）
- extractedOrderNumber: ★ 从用户消息（结合对话历史）中理解并提取订单号。即使用户说"上次那个订单"、"帮我查下订单进度"、"我之前买的那个"等模糊表达，只要能推断出订单号就填写；确实没有则填 null。LLM的理解能力可以补正则盲区
- 对比类询问使用Markdown表格
- action: 触发前端行为，可选值：show_after_sales_form(弹出售后申请表单)、show_order_detail(展示订单详情)、show_faq(展示FAQ)、none(无需操作)
- actionPayload: 当 action="show_after_sales_form" 时必填，包含订单UUID、订单号和商品列表
- components: 前端展示的UI组件数组，每项含 type(组件类型) 和 props(组件属性)
  · order_status_card: 订单状态卡片，props含 orderNumber, status
  · after_sales_progress: 售后进度组件，props含 afterSalesId, type, status, createdAt
- **售后处理规则**：如果系统提供了订单数据且用户明确要退货/换货/维修，必须设置 action="show_after_sales_form" 并填写 actionPayload；仅当确实需要人工介入（如特殊退款审批）时才引导联系人工客服`;
}

// ─── 对话历史加载 ───

/** 从 messages 表加载指定会话的历史消息 */
async function loadConversationHistory(
  supabase: any,
  conversationId: string,
  userId: string,
  maxMessages = 20
): Promise<HistoryMessage[]> {
  try {
    console.log(`🔍 [ai-chat-stream] 查询历史: conv_id=${conversationId} user_id=${userId} limit=${maxMessages}`);
    const { data, error } = await supabase
      .from('messages')
      .select('sender_type, content')
      .eq('conversation_id', conversationId)
      .eq('user_id', userId)
      .order('created_at', { ascending: true })
      .limit(maxMessages);

    if (error) {
      console.error('❌ [ai-chat-stream] 加载历史消息失败:', JSON.stringify(error));
      return [];
    }

    console.log(`📜 [ai-chat-stream] 历史查询结果: conversation_id=${conversationId} user_id=${userId} rows=${data?.length || 0}`);
    return (data || []).map((m: any) => ({
      role: m.sender_type === 'user' ? 'user' : 'assistant',
      content: m.content,
    }));
  } catch (err) {
    console.error('[ai-chat-stream] 加载历史消息异常:', err);
    return [];
  }
}

/** 按总字符数截断历史消息，保留最近的消息 */
function truncateHistory(history: HistoryMessage[], maxChars: number): HistoryMessage[] {
  let total = 0;
  const result: HistoryMessage[] = [];
  // 从最新往最早遍历，保留最近的消息直到达到上限
  for (let i = history.length - 1; i >= 0; i--) {
    if (total + history[i].content.length > maxChars) {
      if (result.length === 0) result.unshift(history[i]); // 至少保留最新一条
      break;
    }
    total += history[i].content.length;
    result.unshift(history[i]); // 保持时间顺序
  }
  return result;
}

// ─── 结构化输出解析 ───

// 尝试从文本中提取完整的结构化 JSON（包含 picks/intent/highlights/comparison/next 等字段）
function parseStructuredPayload(text: string): any | null {
  const candidates: string[] = [];
  const fencedMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
  if (fencedMatch) candidates.push(fencedMatch[1]);
  const trimmed = text.trim();
  if (trimmed.startsWith('{')) candidates.push(trimmed);
  // 扫描第一个 { 到最后一个 } 之间的内容
  const firstBrace = text.indexOf('{');
  const lastBrace = text.lastIndexOf('}');
  if (firstBrace >= 0 && lastBrace > firstBrace) {
    candidates.push(text.slice(firstBrace, lastBrace + 1));
  }
  for (const candidate of candidates) {
    try {
      const parsed = JSON.parse(candidate);
      if (parsed && typeof parsed === 'object' && (parsed.picks || parsed.intent || parsed.highlights || parsed.clarifies)) {
        return parsed;
      }
    } catch {
      // 继续
    }
  }
  return null;
}

// 解析文本中的 picks - 支持三种格式：① 纯 JSON 对象 ② ```json``` 代码块 ③ 文本中的 JSON 片段
function parsePicksFromText(text: string, catalog: any[]): Array<{ product_id: string; note: string }> {
  const picks: Array<{ product_id: string; note: string }> = [];
  const candidates: string[] = [];

  // 候选 1：```json ... ``` 代码块
  const fencedMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
  if (fencedMatch) candidates.push(fencedMatch[1]);

  // 候选 2：整个文本 trim 后若以 { 开头，尝试整体解析
  const trimmed = text.trim();
  if (trimmed.startsWith('{')) candidates.push(trimmed);

  // 候选 3：搜索 "picks" 关键字附近的 JSON 数组片段
  const picksArrMatch = text.match(/"picks"\s*:\s*(\[[\s\S]*?\])/);
  if (picksArrMatch) candidates.push(`{"picks":${picksArrMatch[1]}}`);

  for (const candidate of candidates) {
    try {
      const parsed = JSON.parse(candidate);
      if (parsed?.picks && Array.isArray(parsed.picks)) {
        for (const p of parsed.picks) {
          if (!p?.product_id) continue;
          const exists = catalog.some(c => c.id === p.product_id);
          if (exists && !picks.some(pick => pick.product_id === p.product_id)) {
            picks.push({ product_id: p.product_id, note: p.note || '' });
          }
        }
        if (picks.length > 0) return picks;
      }
    } catch {
      // 继续尝试下一个候选
    }
  }

  // 兜底候选 4：扫描文本中所有 UUID，匹配 catalog
  if (picks.length === 0) {
    const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
    const found = text.match(uuidRegex) || [];
    for (const id of found) {
      const product = catalog.find(c => c.id === id);
      if (product && !picks.some(pick => pick.product_id === id)) {
        picks.push({ product_id: id, note: '' });
      }
    }
  }

  return picks;
}

// 检测意图（兜底方案，LLM 返回结构化 JSON 时优先使用 LLM 的判断）
function detectIntent(message: string, answer: string): string {
  const shoppingKeywords = ['买', '购', '推荐', '价格', '多少钱', '手机', '电脑'];
  const afterSalesKeywords = ['退货', '换货', '售后', '维修'];
  const compareKeywords = ['对比', '比较', '哪个更好'];
  const clarifyKeywords = ['是吗', '什么', '哪个', '哪款', '多少', '怎样', '如何', '怎么', '能不能', '可以吗'];
  if (compareKeywords.some(kw => message.includes(kw))) return 'COMPARE';
  if (shoppingKeywords.some(kw => message.includes(kw)) || answer.includes('picks')) return 'SHOPPING';
  if (afterSalesKeywords.some(kw => message.includes(kw))) return 'AFTERSALES';
  // 如果消息很短且以问号结尾，可能是模糊询问，倾向 CLARIFY
  if (message.length < 20 && clarifyKeywords.some(kw => message.includes(kw))) return 'CLARIFY';
  return 'GENERAL';
}

// ─── 订单号提取 ───

/** 从用户消息中提取订单号（支持多种格式） */
function extractOrderNumber(message: string): string | null {
  // 格式1: ORD + 日期 + 序号，如 ORD20240618001
  const ordMatch = message.match(/ORD\d{8,}/i);
  if (ordMatch) return ordMatch[0].toUpperCase();

  // 格式2: # 开头 + 数字/字母，如 #20240618001
  const hashMatch = message.match(/#([A-Z0-9]{6,})/i);
  if (hashMatch) return hashMatch[1].toUpperCase();

  // 格式3: 纯数字订单号（8位以上），如 20240618001
  const numMatch = message.match(/\b(\d{8,20})\b/);
  if (numMatch) {
    const num = numMatch[1];
    // 排除明显是手机号或金额的数字
    if (/^1[3-9]\d{9}$/.test(num)) return null; // 手机号
    if (/^\d{1,7}$/.test(num)) return null;     // 太短
    return num;
  }

  // 格式4: 关键词 "订单号" "订单编号" "订单" 后跟的编号
  const keywordMatch = message.match(/订单(?:号|编号)?\s*[：:]\s*([A-Z0-9-]{6,30})/i);
  if (keywordMatch) return keywordMatch[1].toUpperCase();

  // 格式5: 中英文混合 UUID 风格的订单引用
  const uuidLike = message.match(/\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b/i);
  if (uuidLike) return uuidLike[1];

  return null;
}


// ─── 数据查询函数 ───

/** 根据订单号查询订单详情（含商品） */
async function queryOrderByNumber(
  supabase: any,
  orderNumber: string
): Promise<any | null> {
  try {
    console.log(`🔍 [ai-chat-stream] 查询订单: order_number=${orderNumber}`);
    const { data, error } = await supabase
      .from('orders')
      .select(`
        id, order_number, status, total_amount, shipping_address,
        tracking_number, notes, created_at, updated_at,
        order_items (
          id, quantity, unit_price, product_id,
          products ( id, name, price, image_url, brand, category )
        )
      `)
      .eq('order_number', orderNumber)
      .single();

    if (error) {
      console.error('❌ [ai-chat-stream] 订单查询失败:', JSON.stringify(error));
      return null;
    }

    console.log('✅ [ai-chat-stream] 订单查询成功:', {
      order_number: data.order_number,
      status: data.status,
      items_count: data.order_items?.length || 0,
    });
    return data;
  } catch (err) {
    console.error('[ai-chat-stream] 订单查询异常:', err);
    return null;
  }
}

/** 根据用户ID查询最近订单列表 */
async function queryUserOrders(
  supabase: any,
  userId: string,
  limit = 5
): Promise<any[]> {
  try {
    console.log(`🔍 [ai-chat-stream] 查询用户订单: user_id=${userId}`);
    const { data, error } = await supabase
      .from('orders')
      .select(`
        id, order_number, status, total_amount, created_at,
        order_items (
          id, quantity, unit_price, product_id,
          products ( id, name, price, image_url )
        )
      `)
      .eq('user_id', userId)
      .order('created_at', { ascending: false })
      .limit(limit);

    if (error) {
      console.error('❌ [ai-chat-stream] 用户订单查询失败:', JSON.stringify(error));
      return [];
    }

    console.log('✅ [ai-chat-stream] 用户订单查询成功:', { count: data?.length || 0 });
    return data || [];
  } catch (err) {
    console.error('[ai-chat-stream] 用户订单查询异常:', err);
    return [];
  }
}

/** 根据订单ID查询售后记录 */
async function queryAfterSalesByOrder(
  supabase: any,
  orderId: string
): Promise<any[]> {
  try {
    console.log(`🔍 [ai-chat-stream] 查询售后: order_id=${orderId}`);
    const { data, error } = await supabase
      .from('after_sales_requests')
      .select(`
        id, type, status, reason, description,
        tracking_number, refund_amount, admin_notes,
        processed_at, created_at, updated_at, order_id
      `)
      .eq('order_id', orderId)
      .order('created_at', { ascending: false });

    if (error) {
      console.error('❌ [ai-chat-stream] 售后查询失败:', JSON.stringify(error));
      return [];
    }

    console.log('✅ [ai-chat-stream] 售后查询成功:', { count: data?.length || 0 });
    return data || [];
  } catch (err) {
    console.error('[ai-chat-stream] 售后查询异常:', err);
    return [];
  }
}

/** 根据用户ID查询售后记录 */
async function queryAfterSalesByUser(
  supabase: any,
  userId: string,
  limit = 5
): Promise<any[]> {
  try {
    console.log(`🔍 [ai-chat-stream] 查询用户售后: user_id=${userId}`);
    const { data, error } = await supabase
      .from('after_sales_requests')
      .select(`
        id, type, status, reason, description,
        tracking_number, refund_amount, admin_notes,
        processed_at, created_at, updated_at, order_id
      `)
      .eq('user_id', userId)
      .order('created_at', { ascending: false })
      .limit(limit);

    if (error) {
      console.error('❌ [ai-chat-stream] 用户售后查询失败:', JSON.stringify(error));
      return [];
    }

    console.log('✅ [ai-chat-stream] 用户售后查询成功:', { count: data?.length || 0 });
    return data || [];
  } catch (err) {
    console.error('[ai-chat-stream] 用户售后查询异常:', err);
    return [];
  }
}

/** 从知识库搜索FAQ（ilike全文匹配） */
async function searchFAQ(
  supabase: any,
  query: string,
  limit = 5
): Promise<Array<{ title: string; content: string; category: string }>> {
  try {
    console.log(`🔍 [ai-chat-stream] 搜索FAQ: query="${query.substring(0, 60)}"`);

    // 提取关键词用于 ilike 搜索
    const keywords = query
      .replace(/[？?！!，,。.、\s]+/g, ' ')
      .trim()
      .split(/\s+/)
      .filter(w => w.length >= 2)
      .slice(0, 3); // 最多3个关键词

    if (keywords.length === 0) return [];

    // 对每个关键词进行 ilike 搜索（OR 拼接）
    let dbQuery = supabase
      .from('knowledge_base')
      .select('title, content, category')
      .eq('is_active', true);

    // 构建 OR 条件
    const conditions = keywords.map(kw =>
      `title.ilike.%${kw}%,content.ilike.%${kw}%`
    ).join(',');
    dbQuery = dbQuery.or(conditions);

    const { data, error } = await dbQuery.limit(limit);

    if (error) {
      console.error('❌ [ai-chat-stream] FAQ搜索失败:', JSON.stringify(error));
      return [];
    }

    console.log('✅ [ai-chat-stream] FAQ搜索成功:', { keywords, results: data?.length || 0 });
    return (data || []).map((row: any) => ({
      title: row.title,
      content: row.content?.substring(0, 500) || '', // 截断长文本
      category: row.category || '',
    }));
  } catch (err) {
    console.error('[ai-chat-stream] FAQ搜索异常:', err);
    return [];
  }
}


// ─── 数据上下文构建 ───

/** 将查询到的数据格式化为注入 system prompt 的 Markdown 文本 */
function buildDataContext(params: {
  order: any | null;
  userOrders: any[];
  afterSales: any[];
  faqResults: Array<{ title: string; content: string; category: string }>;
  userId: string | null;
}): string {
  const sections: string[] = [];

  // ── 订单数据 ──
  if (params.order) {
    const o = params.order;
    const statusMap: Record<string, string> = {
      pending: '待处理',
      confirmed: '已确认',
      shipped: '已发货',
      delivered: '已签收',
      cancelled: '已取消',
    };
    const statusCN = statusMap[o.status] || o.status;

    let orderSection = `## 用户查询的订单（真实数据，禁止编造）

| 项目 | 详情 |
|------|------|
| 订单号 | ${o.order_number} |
| 状态 | ${statusCN}（${o.status}） |
| 金额 | ¥${Number(o.total_amount).toFixed(2)} |
| 下单时间 | ${o.created_at || '未知'} |`;

    if (o.tracking_number) {
      orderSection += `\n| 物流单号 | ${o.tracking_number} |`;
    }
    if (o.shipping_address) {
      orderSection += `\n| 收货地址 | ${o.shipping_address} |`;
    }

    // 订单商品
    if (o.order_items?.length > 0) {
      orderSection += `\n\n**商品明细：**\n`;
      orderSection += `| 商品 | 单价 | 数量 | 小计 |\n|------|------|------|------|\n`;
      for (const item of o.order_items) {
        const name = item.products?.name || '未知商品';
        const price = Number(item.unit_price).toFixed(2);
        const qty = item.quantity;
        const subtotal = (Number(item.unit_price) * item.quantity).toFixed(2);
        orderSection += `| ${name} | ¥${price} | ×${qty} | ¥${subtotal} |\n`;
      }
    }

    sections.push(orderSection);
  }

  // ── 用户最近订单列表 ──
  if (params.userOrders.length > 0 && !params.order) {
    let userOrdersSection = `## 用户最近订单（真实数据，禁止编造）

| 订单号 | 状态 | 金额 | 时间 |
|------|------|------|------|
`;
    const statusMap: Record<string, string> = {
      pending: '待处理', confirmed: '已确认', shipped: '已发货',
      delivered: '已签收', cancelled: '已取消',
    };
    for (const o of params.userOrders) {
      const statusCN = statusMap[o.status] || o.status;
      const date = o.created_at ? new Date(o.created_at).toLocaleDateString('zh-CN') : '-';
      userOrdersSection += `| ${o.order_number} | ${statusCN} | ¥${Number(o.total_amount).toFixed(2)} | ${date} |\n`;
    }
    sections.push(userOrdersSection);
  }

  // ── 售后数据 ──
  if (params.afterSales.length > 0) {
    const typeMap: Record<string, string> = {
      return: '退货', exchange: '换货', repair: '维修',
    };
    const statusMap: Record<string, string> = {
      pending: '待审核', approved: '已通过', processing: '处理中',
      completed: '已完成', rejected: '已拒绝',
    };

    let asSection = `## 关联售后记录（真实数据，禁止编造）

| 类型 | 状态 | 原因 | 申请时间 |
|------|------|------|------|
`;
    for (const as of params.afterSales) {
      const typeCN = typeMap[as.type] || as.type;
      const statusCN = statusMap[as.status] || as.status;
      const date = as.created_at ? new Date(as.created_at).toLocaleDateString('zh-CN') : '-';
      asSection += `| ${typeCN} | ${statusCN} | ${as.reason?.substring(0, 30) || '-'} | ${date} |\n`;
    }

    // 附上第一条售后的详细信息
    const first = params.afterSales[0];
    if (first) {
      asSection += `\n**最近售后详情：**\n`;
      asSection += `- 类型：${typeMap[first.type] || first.type}\n`;
      asSection += `- 状态：${statusMap[first.status] || first.status}\n`;
      asSection += `- 原因：${first.reason || '-'}\n`;
      if (first.description) asSection += `- 描述：${first.description.substring(0, 200)}\n`;
      if (first.tracking_number) asSection += `- 退回物流单号：${first.tracking_number}\n`;
      if (first.refund_amount) asSection += `- 退款金额：¥${Number(first.refund_amount).toFixed(2)}\n`;
      if (first.admin_notes) asSection += `- 客服备注：${first.admin_notes.substring(0, 200)}\n`;
    }

    sections.push(asSection);
  }

  // ── FAQ / 知识库结果 ──
  if (params.faqResults.length > 0) {
    let faqSection = `## 知识库FAQ（可参考，但需结合上下文回复）

`;
    for (const faq of params.faqResults) {
      faqSection += `### ${faq.title}\n${faq.content}\n\n`;
    }
    sections.push(faqSection);
  }

  // ── 无数据提示 ──
  if (sections.length === 0) {
    sections.push(`## 数据查询结果

（本次查询未获取到订单/售后/FAQ数据。如果用户询问订单或售后问题，请友好引导用户提供正确的订单号。）`);
  }

  return sections.join('\n\n---\n\n');
}


// ─── 主处理 ───

Deno.serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  const traceId = req.headers.get('x-trace-id') || crypto.randomUUID();

  try {
    const body: ChatRequest = await req.json();
    const { message, conversationId, localConversationId, inputs } = body;

    console.log('📥 [ai-chat-stream] request:', {
      trace_id: traceId,
      message: message?.substring(0, 80),
      catalogCount: inputs?.catalog?.length || 0,
      conversationId: conversationId || '(empty)',
      localConversationId: localConversationId || '(empty)',
      localConversationId_type: typeof localConversationId,
      inputs_user_id: inputs?.user_id || '(empty)',
      inputs_user_id_type: typeof inputs?.user_id,
    });

    const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
    const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
    const supabase = createClient(supabaseUrl, supabaseKey);

    // ─── 构建产品目录（合并前端传入 + 数据库查询）───
    const inputCatalog = Array.isArray(inputs.catalog) ? inputs.catalog : [];
    const { data: products } = await supabase
      .from('products')
      .select('id, name, price, stock_quantity, category, description, brand, is_hot, is_recommended')
      .eq('is_active', true)
      .order('is_hot', { ascending: false })
      .order('is_recommended', { ascending: false })
      .limit(500);
    const catalogById = new Map<string, any>();
    [...inputCatalog, ...(products || [])].forEach((product: any) => {
      if (product?.id) catalogById.set(product.id, product);
    });
    const catalog = Array.from(catalogById.values());

    // ─── 确定 LLM API 端点 ───
    let apiUrl: string;
    let apiKey: string;
    let model: string;

    if (USE_AI_HUB) {
      apiUrl = `${AI_HUB_BASE}/chat/completions`;
      apiKey = AI_HUB_KEY;
      model = AI_HUB_MODEL;
      console.log('🤖 [ai-chat-stream] Using AI Hub:', { base: AI_HUB_BASE, model });
    } else if (LOVABLE_KEY) {
      apiUrl = 'https://ai.gateway.lovable.dev/v1/chat/completions';
      apiKey = LOVABLE_KEY;
      model = 'google/gemini-2.5-flash';
      console.log('🤖 [ai-chat-stream] Using Lovable Gateway');
    } else {
      throw new Error('AI_HUB_API_KEY/AI_HUB_BASE_URL 和 LOVABLE_API_KEY 均未配置，无法调用大模型');
    }

    // ─── 加载对话历史 ───
    let historyMessages: HistoryMessage[] = [];
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    const rawUserId = inputs.user_id;
    const userIdPassed = !!(rawUserId && uuidRegex.test(rawUserId));
    const userId = userIdPassed ? rawUserId : crypto.randomUUID();
    const localConvIdPassed = !!(localConversationId && uuidRegex.test(localConversationId));
    const localConvId = localConvIdPassed ? localConversationId! : crypto.randomUUID();

    console.log('🔑 [ai-chat-stream] ID resolution:', {
      rawUserId,
      userIdPassed,
      userId_used: userId,
      rawLocalConvId: localConversationId,
      localConvIdPassed,
      localConvId_used: localConvId,
    });

    if (localConvId && userId) {
      historyMessages = await loadConversationHistory(supabase, localConvId, userId, 20);
      historyMessages = truncateHistory(historyMessages, 12000);
      console.log('📜 [ai-chat-stream] 历史消息:', {
        loaded: historyMessages.length,
        totalChars: historyMessages.reduce((s, m) => s + m.content.length, 0),
        oldestRole: historyMessages[0]?.role || 'n/a',
        newestRole: historyMessages[historyMessages.length - 1]?.role || 'n/a',
      });
    }

    // ─── 数据查询：订单/售后/FAQ ───
    const orderNumber = extractOrderNumber(message);
    console.log('🔎 [ai-chat-stream] 订单号提取:', { orderNumber, messageLength: message.length });

    let order: any = null;
    let afterSales: any[] = [];
    let userOrders: any[] = [];
    let faqResults: Array<{ title: string; content: string; category: string }> = [];

    // 并行查询：订单详情 + 售后记录 + FAQ + 用户最近订单
    const [orderResult, asUserResult, faqResult, userOrdersResult] = await Promise.allSettled([
      // 1. 如果提取到订单号，查询订单详情
      orderNumber
        ? queryOrderByNumber(supabase, orderNumber)
        : Promise.resolve(null),
      // 2. 如果用户已登录，查询该用户的售后记录
      userIdPassed
        ? queryAfterSalesByUser(supabase, userId, 5)
        : Promise.resolve([]),
      // 3. FAQ 搜索（总是执行，帮助回答常见问题）
      searchFAQ(supabase, message, 5),
      // 4. 用户最近订单（如果没有具体订单号但有 userId）
      (userIdPassed && !orderNumber)
        ? queryUserOrders(supabase, userId, 5)
        : Promise.resolve([]),
    ]);

    if (orderResult.status === 'fulfilled') order = orderResult.value;
    if (asUserResult.status === 'fulfilled') afterSales = asUserResult.value;
    if (faqResult.status === 'fulfilled') faqResults = faqResult.value;
    if (userOrdersResult.status === 'fulfilled') userOrders = userOrdersResult.value;

    // 如果查到订单，再查该订单的售后记录（优先于用户级别的售后）
    if (order?.id) {
      try {
        const orderAS = await queryAfterSalesByOrder(supabase, order.id);
        if (orderAS.length > 0) afterSales = orderAS; // 订单级别售后记录覆盖用户级别
      } catch {
        // 保持已有售后数据
      }
    }

    // 构建数据上下文
    const dataContext = buildDataContext({
      order,
      userOrders,
      afterSales,
      faqResults,
      userId: userIdPassed ? userId : null,
    });

    console.log('📊 [ai-chat-stream] 数据上下文构建完成:', {
      hasOrder: !!order,
      afterSalesCount: afterSales.length,
      userOrdersCount: userOrders.length,
      faqResultsCount: faqResults.length,
      contextLength: dataContext.length,
    });

    // ─── 构建系统提示词 ───
    const systemPrompt = buildSystemPrompt(catalog, inputs.user_context || '', dataContext);

    // ─── 调用大模型流式 API ───
    console.log('🚀 [ai-chat-stream] Calling LLM API...');

    const llmResponse = await fetch(apiUrl, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        model,
        messages: [
          { role: 'system', content: systemPrompt },
          ...historyMessages,
          { role: 'user', content: message },
        ],
        stream: true,
        stream_options: { include_usage: true },
        temperature: 0.7,
        max_tokens: 4096,
      }),
    });

    if (!llmResponse.ok) {
      const errorText = await llmResponse.text();
      console.error('❌ [ai-chat-stream] LLM API error:', llmResponse.status, errorText);
      throw new Error(`LLM API error: ${llmResponse.status} - ${errorText.substring(0, 300)}`);
    }

    // ─── 流式转换：OpenAI SSE → 自定义 SSE ───
    const encoder = new TextEncoder();
    let fullAnswer = '';

    const stream = new ReadableStream({
      async start(controller) {
        try {
          const reader = llmResponse.body?.getReader();
          if (!reader) throw new Error('LLM response body is empty');

          const decoder = new TextDecoder();
          let buffer = '';
          let openaiDone = false;

          while (!openaiDone) {
            const { done, value } = await reader.read();
            if (done) break;

            buffer += decoder.decode(value, { stream: true });
            const lines = buffer.split('\n');
            buffer = lines.pop() || '';

            for (const line of lines) {
              const trimmed = line.trim();
              if (!trimmed) continue;

              // OpenAI SSE 格式: "data: {...}" 或 "data: [DONE]"
              if (!trimmed.startsWith('data:')) continue;

              const dataContent = trimmed.slice(5).trim(); // 去掉 "data:" 前缀
              if (dataContent === '[DONE]') {
                openaiDone = true;
                break;
              }
              if (!dataContent) continue; // 跳过空的 keepalive 行

              try {
                const parsed = JSON.parse(dataContent);
                const delta = parsed?.choices?.[0]?.delta;

                if (delta?.content) {
                  // ★ 逐 chunk 拦截清洗——防止幻觉 URL 通过 SSE 流到达前端
                  let cleanContent = delta.content;
                  const streamUrlPatterns = [
                    /\[([^\]]*)\]\(https?:\/\/[^\)]+\)/gi,
                    /https?:\/\/[^\s]+/gi,
                  ];
                  for (const p of streamUrlPatterns) {
                    if (p.test(cleanContent)) {
                      console.log('🧹 [ai-chat-stream] Stripped hallucinated URL from stream chunk');
                      cleanContent = cleanContent.replace(p, '');
                    }
                  }
                  fullAnswer += cleanContent;
                  const sseData = JSON.stringify({ type: 'text', content: cleanContent });
                  controller.enqueue(encoder.encode(`data: ${sseData}\n\n`));
                }
              } catch {
                // 非 JSON 行，跳过（如注释行、空 data 等）
              }
            }
          }

          // ─── 流结束，后处理 ───
          // 如果 LLM 没有返回内容，发送友好提示
          if (!fullAnswer) {
            fullAnswer = '抱歉，AI助手暂时无法回复，请稍后再试。';
            const sseData = JSON.stringify({ type: 'text', content: fullAnswer });
            controller.enqueue(encoder.encode(`data: ${sseData}\n\n`));
          }

          // ★ 第二道防线：全文本再次清洗幻觉 URL（保护 DB 写入副本）
          const postUrlPatterns = [
            /\[([^\]]*)\]\(https?:\/\/[^\)]+\)/gi,
            /https?:\/\/[^\s]+/gi,
          ];
          for (const p of postUrlPatterns) {
            fullAnswer = fullAnswer.replace(p, '');
          }

          console.log('📝 [ai-chat-stream] Full answer length:', fullAnswer.length);

          // 优先尝试解析 LLM 直接返回的结构化 JSON
          const structured = parseStructuredPayload(fullAnswer);

          let picks: Array<{ product_id: string; note: string }> = [];
          let intent = 'GENERAL';
          let highlights: string[] = [];
          let comparison: string | undefined;
          let next: string[] | undefined;
          let title: string | undefined;
          let clarifies: string[] | undefined;
          let action: string | undefined;
          let actionPayload: any | undefined;
          let components: any[] | undefined;

          if (structured) {
            console.log('✅ [ai-chat-stream] Using structured payload from LLM');
            // 校验 picks 中的 product_id 必须存在于 catalog（防幻觉）
            const rawPicks = Array.isArray(structured.picks) ? structured.picks : [];
            const catalogIdSet = new Set(catalog.map((c: any) => c?.id).filter(Boolean));
            const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

            type PickAudit = { product_id: string | null; note: string | null; accepted: boolean; reasons: string[] };
            const pickAudit: PickAudit[] = rawPicks.map((p: any) => {
              const id = typeof p?.product_id === 'string' ? p.product_id.trim() : '';
              const reasons: string[] = [];
              if (!id) reasons.push('missing_product_id');
              else if (!uuidRegex.test(id)) reasons.push('invalid_uuid_format');
              else if (!catalogIdSet.has(id)) reasons.push('not_in_catalog');
              return { product_id: id || null, note: p?.note ?? null, accepted: reasons.length === 0, reasons };
            });

            const accepted = pickAudit.filter(a => a.accepted);
            const rejected = pickAudit.filter(a => !a.accepted);
            console.log(`📊 [ai-chat-stream] Pick audit — total:${pickAudit.length} accepted:${accepted.length} rejected:${rejected.length} catalog_size:${catalogIdSet.size}`);
            if (rejected.length > 0) {
              console.log('🚫 [ai-chat-stream] Rejected picks:', JSON.stringify(rejected, null, 2));
              rejected.forEach(r => {
                if (r.product_id && r.reasons.includes('not_in_catalog')) {
                  const prefix = r.product_id.slice(0, 8);
                  const prefixHits = catalog
                    .filter((c: any) => typeof c?.id === 'string' && c.id.startsWith(prefix))
                    .map((c: any) => ({ id: c.id, name: c.name }));
                  console.log(`   ↳ ${r.product_id} not in catalog. Same-prefix(${prefix}) entries in catalog:`, JSON.stringify(prefixHits));
                }
              });
            }

            picks = accepted.map(a => ({ product_id: a.product_id as string, note: a.note || '' }));

            // ★ 回退：结构化 JSON 存在但所有 picks 被审计拒绝 → 尝试文本解析兜底
            if (picks.length === 0 && rawPicks.length > 0 && catalog.length > 0) {
              console.log('⚠️ [ai-chat-stream] All structured picks rejected by audit, falling back to text parsing');
              const textPicks = parsePicksFromText(fullAnswer, catalog);
              if (textPicks.length > 0) {
                picks = textPicks;
                console.log('✅ [ai-chat-stream] Text fallback recovered picks:', picks.length);
              } else {
                console.log('❌ [ai-chat-stream] Text fallback also found no valid picks');
              }
            }

            intent = structured.intent || detectIntent(message, fullAnswer);
            highlights = Array.isArray(structured.highlights) ? structured.highlights : [];
            comparison = typeof structured.comparison === 'string' ? structured.comparison : undefined;
            next = Array.isArray(structured.next) ? structured.next : undefined;
            title = typeof structured.title === 'string' ? structured.title : undefined;
            clarifies = Array.isArray(structured.clarifies) ? structured.clarifies : undefined;
            action = (typeof structured.action === 'string' && structured.action !== 'none') ? structured.action : undefined;
            actionPayload = (action && structured.actionPayload && typeof structured.actionPayload === 'object') ? structured.actionPayload : undefined;
            components = (Array.isArray(structured.components) && structured.components.length > 0) ? structured.components : undefined;

            // LLM 提取的订单号（补正则盲区：如"上次那个订单"、"帮我查下进度"）
            const llmOrderNumber = (typeof structured.extractedOrderNumber === 'string' && structured.extractedOrderNumber.trim())
              ? structured.extractedOrderNumber.trim()
              : null;
            if (llmOrderNumber && !order) {
              console.log('🧠 [ai-chat-stream] LLM 提取到正则漏掉的订单号:', llmOrderNumber);
              try {
                order = await queryOrderByNumber(supabase, llmOrderNumber);
                if (order?.id) {
                  const orderAS = await queryAfterSalesByOrder(supabase, order.id);
                  if (orderAS.length > 0) afterSales = orderAS;
                  console.log('✅ [ai-chat-stream] LLM订单号查询成功，补充数据注入');
                }
              } catch { /* 查询失败不影响主流程 */ }
            }
          } else {
            console.log('⚠️ [ai-chat-stream] No structured payload, falling back to text parsing');
            picks = parsePicksFromText(fullAnswer, catalog);
            intent = detectIntent(message, fullAnswer);
            highlights = fullAnswer.split(/[。！？\n]/).filter((s: string) => s.length > 10 && s.length < 100).slice(0, 3);
          }

          console.log('🔍 [ai-chat-stream] Parsed picks:', JSON.stringify(picks));
          const mentionedIds = catalog
            .filter(c => fullAnswer.includes(c.id))
            .map(c => ({ id: c.id, name: c.name }));
          console.log('🔎 [ai-chat-stream] Product IDs mentioned in text:', JSON.stringify(mentionedIds));

          // ─── 保存消息到数据库（服务端写入，保障多轮记忆）───
          if (localConvId && userId) {
            const baseTime = new Date();
            const userTs = baseTime.toISOString();
            const aiTs = new Date(baseTime.getTime() + 1).toISOString(); // +1ms 保证顺序

            console.log('💾 [ai-chat-stream] 准备写入 DB:', {
              conversation_id: localConvId,
              user_id: userId,
              user_msg_len: message.length,
              ai_msg_len: fullAnswer.length,
            });

            // 确保 conversations 表有对应行（访客场景下前端只写 localStorage，FK 约束会拒绝 messages 写入）
            const { error: upsertConvError } = await supabase.from('conversations').upsert({
              id: localConvId,
              user_id: userId,
            }, { onConflict: 'id' });
            if (upsertConvError) {
              console.error('❌ [ai-chat-stream] upsert conversations 失败:', JSON.stringify(upsertConvError));
            } else {
              console.log('✅ [ai-chat-stream] conversations 已 upsert');
            }

            // 保存用户消息
            const { error: saveUserError } = await supabase.from('messages').insert({
              conversation_id: localConvId,
              user_id: userId,
              content: message,
              sender_type: 'user',
              message_type: 'text',
              created_at: userTs,
            });
            if (saveUserError) {
              console.error('❌ [ai-chat-stream] 保存用户消息失败:', JSON.stringify(saveUserError));
            } else {
              console.log('✅ [ai-chat-stream] 用户消息已保存');
            }

            // 保存 AI 回复
            const { error: saveAiError } = await supabase.from('messages').insert({
              conversation_id: localConvId,
              user_id: userId,
              content: fullAnswer,
              sender_type: 'ai',
              message_type: 'text',
              metadata: { picks, intent, highlights, title },
              created_at: aiTs,
            });
            if (saveAiError) {
              console.error('❌ [ai-chat-stream] 保存 AI 回复失败:', JSON.stringify(saveAiError));
            } else {
              console.log('✅ [ai-chat-stream] AI 回复已保存');
            }
          } else {
            console.error('❌ [ai-chat-stream] 跳过 DB 写入 — localConvId 或 userId 为空:', { localConvId, userId });
          }

          // ─── 发送最终元数据 ───
          const finalData = JSON.stringify({
            type: 'done',
            intent,
            picks,
            highlights,
            ...(comparison ? { comparison } : {}),
            ...(next ? { next } : {}),
            ...(title ? { title } : {}),
            ...(clarifies ? { clarifies } : {}),
            ...(action ? { action } : {}),
            ...(actionPayload ? { actionPayload } : {}),
            ...(components ? { components } : {}),
            meta: {
              platform: 'ai-hub',
              user: userId,
              catalog_count: catalog.length,
              probe_id: traceId,
              source: structured ? 'llm-structured' : 'text-fallback',
              hasHistory: historyMessages.length > 0,
              historyMessages: historyMessages.length,
              hasOrderData: !!order,
              hasAfterSalesData: afterSales.length > 0,
              hasFAQData: faqResults.length > 0,
            },
            conversation_id: localConvId,
          });

          controller.enqueue(encoder.encode(`data: ${finalData}\n\n`));
          controller.enqueue(encoder.encode('data: [DONE]\n\n'));
          controller.close();

          console.log('✅ [ai-chat-stream] Stream completed:', {
            answerLength: fullAnswer.length,
            picksCount: picks.length,
            intent,
            hasComparison: !!comparison,
            conversation_id: localConvId,
          });
        } catch (error) {
          console.error('❌ [ai-chat-stream] Stream error:', error);
          const errorData = JSON.stringify({
            type: 'error',
            message: (error instanceof Error ? error.message : '流式响应失败'),
          });
          controller.enqueue(encoder.encode(`data: ${errorData}\n\n`));
          controller.close();
        }
      },
    });

    return new Response(stream, {
      headers: {
        ...corsHeaders,
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'X-Trace-Id': traceId,
      },
    });
  } catch (error) {
    console.error('❌ [ai-chat-stream] Fatal error:', error);
    return new Response(
      JSON.stringify({ error: (error instanceof Error ? error.message : 'Unknown error') }),
      {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      }
    );
  }
});
