#!/usr/bin/env node
'use strict';

/**
 * VoiceInput Bridge — JSON 事件输出模式
 *
 * 用法: node bridge.js
 * 从 stdin 读取 JSON 指令，通过 stdout 输出 JSON 事件。
 *
 * 输入格式（stdin，每行一个 JSON）:
 *   {"action":"chat","message":"用户问题","workspace":"/path/to/dir"}
 *
 * 输出格式（stdout，每行一个 JSON）:
 *   {"type":"tool_start","tool":"read_file","args":{"path":"..."}}
 *   {"type":"tool_end","tool":"read_file","lines":120}
 *   {"type":"assistant_chunk","content":"..."}
 *   {"type":"done","elapsed_ms":12345}
 *   {"type":"error","message":"..."}
 */

const fs = require('fs');
const path = require('path');
const readline = require('readline');

const { ensureConfig } = require('./config');
const { createState } = require('./core/state');
const { buildSystemPrompt } = require('./core/context');
const { streamChat } = require('./model/client');
const { executeTool } = require('./tools/dispatcher');
const { applyUsage, normalizeTokenUsage, normalizeUsagePayload } = require('./utils/token_usage');
const { buildStartLine, buildFinalLine, formatResultLines } = require('./ui/tool_display');

// --- 配置 ---
// 优先使用 --config 参数指定的路径（用户目录），回退到 Agent 目录
let configPath = path.join(path.dirname(__dirname), 'models.json');
const configArgIdx = process.argv.indexOf('--config');
if (configArgIdx >= 0 && configArgIdx + 1 < process.argv.length) {
  configPath = path.resolve(process.argv[configArgIdx + 1]);
}
process.stderr.write(`[bridge] starting, configPath=${configPath}\n`);
const config = ensureConfig({ path: configPath });
process.stderr.write(`[bridge] config loaded, validModels=${config.valid_models?.length || 0}\n`);

if (!config.valid_models || config.valid_models.length === 0) {
  process.stderr.write(`[bridge] no valid models, exiting\n`);
  process.stdout.write(JSON.stringify({ type: 'error', message: '未找到可用模型配置' }) + '\n');
  process.exit(1);
}

const PROMPTS_DIR = path.join(__dirname, '..', 'prompts');
const TOOLS_JSON = path.join(__dirname, '..', 'doc', 'tools.json');
const systemPrompt = fs.readFileSync(path.join(PROMPTS_DIR, 'system.txt'), 'utf8');
const tools = JSON.parse(fs.readFileSync(TOOLS_JSON, 'utf8'));

// --- 输出辅助 ---
function emit(obj) {
  process.stdout.write(JSON.stringify(obj) + '\n');
}

// --- 工作区解析 ---
function resolveWorkspace(raw) {
  if (!raw) return process.cwd();
  const p = path.resolve(raw);
  return fs.existsSync(p) ? p : process.cwd();
}

// --- 构建消息 ---
function buildApiMessages(state, workspace) {
  const system = buildSystemPrompt(systemPrompt, {
    workspace,
    allowMode: state.allowMode,
    modelId: state.modelId || state.modelKey,
  });
  const messages = [{ role: 'system', content: system }];
  for (const msg of state.messages) {
    const m = { role: msg.role };
    if (msg.role === 'tool') {
      m.tool_call_id = msg.tool_call_id;
      m.content = msg.content;
    } else if (msg.role === 'assistant' && Array.isArray(msg.tool_calls)) {
      m.content = msg.content || null;
      m.tool_calls = msg.tool_calls;
      if (Object.prototype.hasOwnProperty.call(msg, 'reasoning_content')) {
        m.reasoning_content = msg.reasoning_content;
      } else if (state.thinkingMode) {
        m.reasoning_content = '';
      }
    } else {
      m.content = msg.content;
      if (msg.role === 'assistant' && Object.prototype.hasOwnProperty.call(msg, 'reasoning_content')) {
        m.reasoning_content = msg.reasoning_content;
      }
    }
    messages.push(m);
  }
  return messages;
}

// --- 持久化会话状态（跨多轮对话） ---
let persistentState = null;
let persistentWorkspace = null;
let persistentThinkingMode = false;

// --- 主循环 ---
async function runAgent(input) {
  // 首次调用或 state 丢失时重建
  if (!persistentState) {
    persistentState = createState(config, persistentWorkspace || process.cwd());
    persistentState.thinkingMode = persistentThinkingMode;
  }

  const state = persistentState;
  process.stderr.write(`[bridge] runAgent start, msgs=${state.messages.length}, thinkingMode=${state.thinkingMode}\n`);

  // 用户消息
  state.messages.push({ role: 'user', content: input });
  const startTime = Date.now();

  let continueLoop = true;
  let lastAssistantContent = '';

  while (continueLoop) {
    let assistantContent = '';
    let fullThinkingBuffer = '';
    let toolCalls = {};
    let usageTotal = null;

    emit({ type: 'status', mode: 'thinking' });

    const streamController = new AbortController();
    const messages = buildApiMessages(state, persistentWorkspace || process.cwd());

    try {
      for await (const chunk of streamChat({
        config,
        modelKey: state.modelKey,
        messages,
        tools,
        thinkingMode: state.thinkingMode,
        currentContextTokens: normalizeTokenUsage(state.tokenUsage).total || 0,
        abortSignal: streamController.signal,
      })) {
        const choice = chunk.choices?.[0];
        if (!choice) continue;

        const delta = choice.delta || {};

        // Tool calls
        if (delta.tool_calls) {
          delta.tool_calls.forEach((tc) => {
            const idx = tc.index;
            if (!toolCalls[idx]) toolCalls[idx] = { id: tc.id, type: tc.type, function: { name: '', arguments: '' } };
            if (tc.function?.name) toolCalls[idx].function.name = tc.function.name;
            if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;
          });
        }

        // Content
        if (delta.content) {
          assistantContent += delta.content;
          emit({ type: 'assistant_chunk', content: delta.content });
        }

        // Reasoning（思考模式）
        if (delta.reasoning_content || delta.reasoning_details || choice.reasoning_details) {
          let rc = '';
          if (delta.reasoning_content) {
            rc = delta.reasoning_content;
          } else if (delta.reasoning_details) {
            if (Array.isArray(delta.reasoning_details)) {
              rc = delta.reasoning_details.map((d) => d.text || '').join('');
            } else if (typeof delta.reasoning_details === 'string') {
              rc = delta.reasoning_details;
            } else if (delta.reasoning_details && typeof delta.reasoning_details.text === 'string') {
              rc = delta.reasoning_details.text;
            }
          } else if (choice.reasoning_details) {
            if (Array.isArray(choice.reasoning_details)) {
              rc = choice.reasoning_details.map((d) => d.text || '').join('');
            } else if (typeof choice.reasoning_details === 'string') {
              rc = choice.reasoning_details;
            } else if (choice.reasoning_details && typeof choice.reasoning_details.text === 'string') {
              rc = choice.reasoning_details.text;
            }
          }
          fullThinkingBuffer += rc;
        }

        // Usage
        const usage = chunk.usage || choice.usage || delta.usage;
        const normalized = normalizeUsagePayload(usage);
        if (normalized && Number.isFinite(normalized.total_tokens)) {
          usageTotal = normalized.total_tokens;
        }
      }
    } catch (err) {
      process.stderr.write(`[bridge] streamChat error: ${err.message || err}\n`);
      emit({ type: 'error', message: err.message || '流式请求失败' });
      return;
    }

    // Tool calls
    const toolCallList = Object.keys(toolCalls)
      .sort((a, b) => a - b)
      .map((k) => toolCalls[k]);

    if (toolCallList.length) {
      const assistantMsg = {
        role: 'assistant',
        content: assistantContent || null,
        tool_calls: toolCallList,
      };
      if (state.thinkingMode) assistantMsg.reasoning_content = fullThinkingBuffer || '';
      state.messages.push(assistantMsg);

      for (const call of toolCallList) {
        let args = {};
        try { args = JSON.parse(call.function.arguments || '{}'); } catch (_) {}

        emit({
          type: 'tool_start',
          tool: call.function.name,
          args: args,
          display: buildStartLine(call.function.name, args),
        });

        const controller = new AbortController();
        let result;
        try {
          result = await executeTool({
            workspace: persistentWorkspace || process.cwd(),
            config,
            allowMode: state.allowMode,
            toolCall: call,
            abortSignal: controller.signal,
          });
        } catch (err) {
          result = {
            success: false,
            tool: call.function.name,
            raw: { success: false, error: err.message || String(err) },
            formatted: `失败: ${err.message || err}`,
            tool_content: null,
          };
        }

        const resultLines = formatResultLines(call.function.name, args, result.raw || { success: result.success });

        emit({
          type: 'tool_end',
          tool: call.function.name,
          result: resultLines,
          success: result.success,
        });

        state.messages.push({
          role: 'tool',
          tool_call_id: call.id,
          content: result.tool_content || result.formatted,
          tool_raw: result.raw,
        });
      }

      continueLoop = true;
      continue;
    }

    // Final
    if (assistantContent) {
      const msg = { role: 'assistant', content: assistantContent };
      if (state.thinkingMode) msg.reasoning_content = fullThinkingBuffer || '';
      state.messages.push(msg);
    }
    lastAssistantContent = assistantContent;
    continueLoop = false;
  }

  const elapsed = Date.now() - startTime;
  emit({ type: 'done', content: lastAssistantContent, elapsed_ms: elapsed });
  // 不退出，等待下一轮输入继续对话
}

// --- 主入口 ---
const rl = readline.createInterface({ input: process.stdin });

rl.on('line', (line) => {
  (async () => {
    try {
      const msg = JSON.parse(line.trim());
      if (msg.action === 'chat') {
        // 首轮：设置工作区和模型
        if (!persistentState) {
          persistentWorkspace = resolveWorkspace(msg.workspace);
          persistentState = createState(config, persistentWorkspace);
          persistentState.allowMode = msg.allowMode || 'full_access';
          if (msg.modelKey && config.model_map.has(msg.modelKey)) {
            const model = config.model_map.get(msg.modelKey);
            persistentState.modelKey = model.key;
            persistentState.modelId = model.model_id || model.name || model.key;
            // 优先使用消息中指定的 thinkingMode，否则用模型默认
            if (msg.thinkingMode !== undefined) {
              persistentThinkingMode = !!msg.thinkingMode;
            } else {
              persistentThinkingMode = model.modes === 'thinking' || model.modes.includes('思考');
            }
            persistentState.thinkingMode = persistentThinkingMode;
          }
        }
        await runAgent(msg.message);
      }
    } catch (err) {
      emit({ type: 'error', message: err.message });
    }
  })();
});

rl.on('close', () => {
  process.stderr.write(`[bridge] stdin closed, exiting\n`);
  process.exit(0);
});

// 优雅处理 SIGTERM：Swift 端 stop() 会发 SIGTERM，这里转为 code 0 退出
process.on('SIGTERM', () => {
  process.stderr.write(`[bridge] received SIGTERM, exiting gracefully\n`);
  process.exit(0);
});
