const express = require("express");
const router = express.Router();
const steedosAuth = require('@steedos/auth');
const axios = require('axios');
const bodyParser = require('body-parser');

router.use(bodyParser.json({ limit: '20mb' }));

/**
 * Streaming AI Workflow Designer endpoint (SSE)
 *
 * Same logic as /am/ai/design but uses Server-Sent Events to stream
 * progress updates so the client can show intermediate status.
 * Also supports images (base64 data URLs) for multimodal analysis.
 *
 * Events sent:
 *   { event: "progress", data: { stage: string, message: string } }
 *   { event: "chunk",    data: { text: string, accumulated: string } }  – raw AI text chunks
 *   { event: "result",   data: { steps: [...] } }
 *   { event: "error",    data: { error: string } }
 *   { event: "done",     data: {} }
 */
router.post('/am/ai/design-stream', async function auth(req, res, next) {
  try {
    const result = await steedosAuth.auth(req, res);
    if (result && result.userId) {
      req.user = result;
      next();
    } else {
      res.status(401).json({ error: '请先登录' });
    }
  } catch (e) {
    res.status(401).json({ error: '认证失败' });
  }
}, async function (req, res) {
  // ---------- Set up SSE headers ----------
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.setHeader('X-Accel-Buffering', 'no'); // nginx
  res.flushHeaders();

  /** Helper: send an SSE event */
  function sendEvent(event, data) {
    if (res.writableEnded) return;
    res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
    if (typeof res.flush === 'function') res.flush();
  }

  // If client disconnects early, stop writing
  let aborted = false;
  req.on('close', () => { aborted = true; });

  try {
    const apiKey = process.env.WORKFLOW_AI_API_KEY;
    if (!apiKey) {
      sendEvent('error', { error: '请先配置环境变量 WORKFLOW_AI_API_KEY' });
      sendEvent('done', {});
      return res.end();
    }

    const baseURL = process.env.WORKFLOW_AI_BASE_URL || 'https://api.openai.com/v1';
    const model = process.env.WORKFLOW_AI_MODEL || 'gpt-4o';

    const { steps, formFields, userRequest, images } = req.body;

    if (!userRequest) {
      sendEvent('error', { error: '请输入需求描述' });
      sendEvent('done', {});
      return res.end();
    }

    // ---- Build system prompt ----
    sendEvent('progress', { stage: 'prepare', message: '正在准备请求...' });

    const systemPrompt = buildSystemPrompt();

    // Build form fields description
    const fieldsDesc = (formFields || [])
      .filter(f => f.code || f.name)
      .map(f => `  - ${f.name || f.code} (编码: ${f.code || f.name}, 类型: ${f.type || 'text'})`)
      .join('\n');

    const userContent = `## 当前表单字段
${fieldsDesc || '（无字段信息）'}

## 当前流程步骤
${JSON.stringify(steps || [], null, 2)}

## 用户需求
${userRequest}

请返回修改后的完整 steps JSON 数组。`;

    let userMessageContent;
    if (Array.isArray(images) && images.length > 0) {
      userMessageContent = [
        { type: 'text', text: userContent },
        ...images.slice(0, 5).map(img => ({
          type: 'image_url',
          image_url: { url: img, detail: 'auto' },
        })),
      ];
    } else {
      userMessageContent = userContent;
    }

    if (aborted) return res.end();

    // ---- Call LLM with streaming ----
    sendEvent('progress', { stage: 'calling', message: '正在连接 AI 服务...' });

    // Log messages sent to AI
    console.log('===== AI Workflow Design Stream - Messages sent to AI =====');
    console.log('System prompt length:', systemPrompt.length);
    console.log('User message type:', typeof userMessageContent === 'string' ? 'text' : 'multipart (with images)');
    console.log('User message length:', typeof userMessageContent === 'string' ? userMessageContent.length : JSON.stringify(userMessageContent).length);
    console.log('===== End of AI Messages =====');

    let accumulated = '';
    let chunkCount = 0;

    try {
      const streamResponse = await axios.post(`${baseURL}/chat/completions`, {
        model,
        messages: [
          { role: 'system', content: systemPrompt },
          { role: 'user', content: userMessageContent },
        ],
        temperature: 0.3,
        max_tokens: 8000,
        stream: true,
      }, {
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
        },
        timeout: 300000,
        responseType: 'stream',
      });

      sendEvent('progress', { stage: 'streaming', message: 'AI 正在生成内容...' });

      // Process the SSE stream from OpenAI
      await new Promise((resolve, reject) => {
        let buffer = '';

        streamResponse.data.on('data', (chunk) => {
          if (aborted) {
            streamResponse.data.destroy();
            return resolve();
          }

          buffer += chunk.toString();
          const lines = buffer.split('\n');
          buffer = lines.pop() || '';

          for (const line of lines) {
            const trimmed = line.trim();
            if (!trimmed || trimmed === 'data: [DONE]') continue;
            if (!trimmed.startsWith('data: ')) continue;

            try {
              const json = JSON.parse(trimmed.slice(6));
              const delta = json.choices?.[0]?.delta?.content;
              if (delta) {
                accumulated += delta;
                chunkCount++;
                sendEvent('chunk', {
                  text: delta,
                  accumulated: accumulated,
                });
              }
            } catch (_) {
              // Ignore parse errors in stream chunks
            }
          }
        });

        streamResponse.data.on('end', () => {
          if (buffer.trim() && buffer.trim() !== 'data: [DONE]' && buffer.trim().startsWith('data: ')) {
            try {
              const json = JSON.parse(buffer.trim().slice(6));
              const delta = json.choices?.[0]?.delta?.content;
              if (delta) accumulated += delta;
            } catch (_) {}
          }
          resolve();
        });

        streamResponse.data.on('error', (err) => {
          reject(err);
        });
      });

    } catch (streamErr) {
      // If streaming fails, fall back to non-streaming call
      if (streamErr.response && streamErr.response.status) {
        const status = streamErr.response.status;
        if (status === 401 || status === 403) {
          sendEvent('error', { error: 'AI API 密钥无效或权限不足，请检查 WORKFLOW_AI_API_KEY' });
          sendEvent('done', {});
          return res.end();
        }
        if (status === 429) {
          sendEvent('error', { error: 'AI API 请求频率超限，请稍后重试' });
          sendEvent('done', {});
          return res.end();
        }
      }

      sendEvent('progress', { stage: 'fallback', message: '流式传输不可用，切换为普通请求...' });

      try {
        const fallbackResponse = await axios.post(`${baseURL}/chat/completions`, {
          model,
          messages: [
            { role: 'system', content: systemPrompt },
            { role: 'user', content: userMessageContent },
          ],
          temperature: 0.3,
          max_tokens: 8000,
        }, {
          headers: {
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json',
          },
          timeout: 300000,
        });

        accumulated = fallbackResponse.data?.choices?.[0]?.message?.content || '';
      } catch (fallbackErr) {
        const errMsg = fallbackErr?.response?.data?.error?.message || fallbackErr?.message || 'AI 服务调用失败';
        sendEvent('error', { error: errMsg });
        sendEvent('done', {});
        return res.end();
      }
    }

    if (aborted) return res.end();

    if (!accumulated) {
      sendEvent('error', { error: 'AI 未返回有效内容' });
      sendEvent('done', {});
      return res.end();
    }

    // ---- Parse JSON ----
    sendEvent('progress', { stage: 'parsing', message: `正在解析 AI 返回内容 (${accumulated.length} 字符)...` });

    let stepsResult;
    try {
      let text = accumulated.trim();
      // Strip markdown code block if present
      if (text.startsWith('```json')) text = text.slice(7);
      else if (text.startsWith('```')) text = text.slice(3);
      if (text.endsWith('```')) text = text.slice(0, -3);
      text = text.trim();

      // Find JSON array
      const arrStart = text.indexOf('[');
      const arrEnd = text.lastIndexOf(']');
      if (arrStart !== -1 && arrEnd > arrStart) {
        stepsResult = JSON.parse(text.substring(arrStart, arrEnd + 1));
      } else {
        stepsResult = JSON.parse(text);
      }
    } catch (parseErr) {
      sendEvent('error', { error: 'AI 返回内容无法解析为 JSON', raw: accumulated });
      sendEvent('done', {});
      return res.end();
    }

    if (!Array.isArray(stepsResult)) {
      sendEvent('error', { error: 'AI 返回的不是步骤数组' });
      sendEvent('done', {});
      return res.end();
    }

    // ---- Validate ----
    sendEvent('progress', { stage: 'validating', message: '正在验证流程数据...' });

    const hasStart = stepsResult.some(s => s.step_type === 'start');
    const hasEnd = stepsResult.some(s => s.step_type === 'end');
    if (!hasStart || !hasEnd) {
      sendEvent('error', { error: 'AI 返回的流程缺少开始或结束节点' });
      sendEvent('done', {});
      return res.end();
    }

    const stepCount = stepsResult.length;
    sendEvent('progress', { stage: 'complete', message: `转换完成！共 ${stepCount} 个步骤` });
    sendEvent('result', { steps: stepsResult });
    sendEvent('done', {});
    return res.end();

  } catch (error) {
    console.error('AI workflow design stream error:', error?.response?.data || error?.message || error);
    if (!aborted) {
      const errData = error?.response?.data;
      const status = error?.response?.status;
      let errMsg;
      if (status === 401 || status === 403) {
        errMsg = 'AI API 密钥无效或权限不足，请检查 WORKFLOW_AI_API_KEY';
      } else if (status === 429) {
        errMsg = 'AI API 请求频率超限，请稍后重试';
      } else {
        errMsg = errData?.error?.message || error?.message || 'AI 服务调用失败';
      }
      sendEvent('error', { error: errMsg });
      sendEvent('done', {});
    }
    return res.end();
  }
});

// ---- System prompt (same as non-streaming endpoint) ----
function buildSystemPrompt() {
  return `你是一个专业的审批流程设计师。你的任务是根据用户的自然语言描述，修改审批流程的步骤定义（JSON 格式）。

## 步骤数据结构

每个步骤(step)是一个 JSON 对象，必须包含以下字段：
- _id: 字符串，唯一标识。已有步骤保留原 _id，新步骤使用 "step-" + 小写字母数字随机串（如 "step-a1b2c3d4"）
- name: 字符串，步骤名称
- step_type: 字符串，类型，只能是以下之一：
  - "start" — 开始节点（流程必须有且仅有一个）
  - "end" — 结束节点（流程必须有且仅有一个）
  - "submit" — 填写步骤
  - "sign" — 审批步骤
  - "counterSign" — 会签步骤（多人同时审批）
  - "condition" — 条件分支节点
- deal_type: 字符串, 处理人身份（submit/sign/counterSign 必填），可选值：
  - "pickupAtRuntime" — 审批时指定
  - "specifyUser" — 指定人员
  - "applicantSuperior" — 申请人的上级
  - "applicant" — 申请人
  - "hrRole" — 指定角色
  - "applicantRole" — 指定审批岗位
- posx, posy: 数字，节点位置。纵向从上到下排列，开始节点 posx=200,posy=40，之后每步 y 增加约120
- timeout_hours: 数字，超时小时数，默认 168
- lines: 数组，从当前步骤出发的连线，每条连线包含：
  - _id: 字符串，连线唯一标识，用 "line-" + 随机串
  - name: 字符串，连线名称（可空）
  - to_step: 字符串，目标步骤的 _id
  - state: 字符串，"submitted"（提交）/ "approved"（核准）/ "rejected"（驳回）
  - condition: 字符串，条件表达式（仅条件节点的出线需要），格式如 {字段编码} > 10000

## 规则
1. 流程必须有且仅有一个 start 和一个 end 节点
2. 所有步骤必须通过 lines 互相连通，不能有孤立节点
3. 从 start 开始必须能最终到达 end
4. 审批步骤(sign)通常有两条出线：approved（核准）到下一步，rejected（驳回）回到填写或上一步
5. 条件节点(condition)有多条出线，每条带不同的 condition 表达式
6. 不要添加 notify、concurrentStart、concurrentEnd 类型的步骤
7. 修改时尽可能保留用户已有步骤的 _id 和设置，只改需要改的部分
8. 返回完整的 steps 数组

## 输出格式
直接返回 JSON 数组，不要有任何其他文字、解释、markdown 标记。`;
}

exports.default = router;
