/**
 * ai-chat-stream Edge Function
 * C端消费者聊天 - 直接大模型流式输出
 *
 * 通过 AI Hub（OpenAI 兼容 API）直接调用大模型，不再经过 Dify/Coze 中间层。
 * 无 AI Hub 时回退到 Lovable Gateway。
 */

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;
  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);

// ─── 系统提示词构建 ───
function buildSystemPrompt(catalog: any[], userContext: string): 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}`
    : '';

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

## 核心规则
1. 产品ID必须从产品目录中精确复制，严禁编造或生成
2. 所有价格、库存以目录为准，不可臆造
3. 回复使用友好、专业、简洁的中文
4. 不透露你是AI

${catalogSection}${contextSection}

## 回复末尾必须包含JSON（当有推荐时）
\`\`\`json
{"picks":[{"product_id":"从目录复制的真实UUID","note":"推荐理由"}],"intent":"SHOPPING|COMPARE|AFTERSALES|GENERAL","highlights":["要点1","要点2"],"title":"简短标题"}
\`\`\`

## 规则
- picks 最多5个，product_id 必须从目录逐字符复制
- intent: SHOPPING(推荐购物)、COMPARE(对比)、AFTERSALES(售后)、GENERAL(一般)
- highlights: 2-4个回复要点（各20-80字）
- title: 简短对话标题（≤30字）
- 对比类询问使用Markdown表格
- 售后问题友好引导联系人工客服`;
}

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

// 尝试从文本中提取完整的结构化 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)) {
        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;
}

// 检测意图
function detectIntent(message: string, answer: string): string {
  const shoppingKeywords = ['买', '购', '推荐', '价格', '多少钱', '手机', '电脑'];
  const afterSalesKeywords = ['退货', '换货', '售后', '维修'];
  const compareKeywords = ['对比', '比较', '哪个更好'];
  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';
  return 'GENERAL';
}

// ─── 主处理 ───

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, inputs } = body;

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

    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 均未配置，无法调用大模型');
    }

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

    // ─── 调用大模型流式 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 },
          { 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 chatConversationId = conversationId || crypto.randomUUID();

    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) {
                  fullAnswer += delta.content;
                  const sseData = JSON.stringify({ type: 'text', content: delta.content });
                  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`));
          }

          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;

          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 || '' }));
            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;
          } 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));

          // ─── 发送最终元数据 ───
          const finalData = JSON.stringify({
            type: 'done',
            intent,
            picks,
            highlights,
            ...(comparison ? { comparison } : {}),
            ...(next ? { next } : {}),
            ...(title ? { title } : {}),
            meta: {
              platform: 'ai-hub',
              user: inputs.user_id || 'anonymous',
              catalog_count: catalog.length,
              probe_id: traceId,
              source: structured ? 'llm-structured' : 'text-fallback',
            },
            conversation_id: chatConversationId,
          });

          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: chatConversationId,
          });
        } 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' },
      }
    );
  }
});
