import { createOpencodeClient, createOpencodeServer } from '@srdcloud/codefree-o-sdk';
import { findSymbols as scanFindSymbols } from './symbolScanner.js';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { exec } from 'child_process';

const MODEL_JSON_PATH = path.join(os.homedir(), '.codefree-o', '.local', 'state', 'model.json');

const DEFAULT_MODEL_CONFIG = {
  recent: [],
  favorite: [],
  variant: {},
};

function getModelJsonPath() {
  return MODEL_JSON_PATH;
}

export function readModelConfig() {
  try {
    if (fs.existsSync(MODEL_JSON_PATH)) {
      const content = fs.readFileSync(MODEL_JSON_PATH, 'utf-8');
      return { ...DEFAULT_MODEL_CONFIG, ...JSON.parse(content) };
    }
  } catch (e) {
    console.warn('[AI] Failed to read model.json:', e.message);
  }
  return { ...DEFAULT_MODEL_CONFIG };
}

export function writeModelConfig(config) {
  const dir = path.dirname(MODEL_JSON_PATH);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  fs.writeFileSync(MODEL_JSON_PATH, JSON.stringify(config, null, 2), 'utf-8');
  return config;
}

export function updateModelRecent(providerID, modelID) {
  const config = readModelConfig();
  const newEntry = { providerID, modelID };
  const recent = config.recent.filter(
    (item) => !(item.providerID === providerID && item.modelID === modelID)
  );
  recent.unshift(newEntry);
  config.recent = recent.slice(0, 10);
  writeModelConfig(config);
  return config;
}

let client = null;
let eventStream = null;
let eventAbortController = null;
let opencodeServer = null;
let currentDirectory = process.env.CODEFREE_DIRECTORY || '';

/**
 * 获取 CodeFree-O 服务基础 URL
 * @returns {string} 服务地址
 */
function getBaseUrl() {
  return process.env.CODEFREE_BASE_URL || '';
}

/**
 * 获取项目目录
 * @returns {string} 项目目录路径
 */
function getDirectory() {
  return currentDirectory;
}

/**
 * 更新项目目录并重新初始化客户端
 * @param {string} directory - 新的项目目录路径
 * @returns {Promise<object>} 重新初始化后的 OpencodeClient 实例
 */
export async function updateDirectory(directory) {
  currentDirectory = directory;
  process.env.CODEFREE_DIRECTORY = directory;
  await shutdownClient();
  return initializeClient();
}

/**
 * 获取 API Key
 * @returns {string} API Key
 */
function getApiKey() {
  return process.env.CODEFREE_API_KEY || '';
}

/**
 * 启动 CodeFree-O 后端服务并初始化客户端
 * 如果 CODEFREE_BASE_URL 已配置且不是自身地址，则直接连接远程服务
 * 否则自动启动本地 codefree-o 进程
 * @returns {Promise<object>} OpencodeClient 实例
 */
export async function initializeClient() {
  if (client) {
    return client;
  }

  const baseUrl = getBaseUrl();
  const serverPort = process.env.SERVER_PORT || '3001';

  if (baseUrl && !baseUrl.includes(`127.0.0.1:${serverPort}`) && !baseUrl.includes(`localhost:${serverPort}`)) {
    console.log('[AI] Connecting to remote CodeFree-O service:', baseUrl);
    const config = { baseUrl };
    const dir = getDirectory();
    if (dir) {
      config.directory = dir;
    }
    const apiKey = getApiKey();
    if (apiKey) {
      config.headers = {
        ...config.headers,
        'Authorization': `Bearer ${apiKey}`,
      };
    }

    client = createOpencodeClient(config);
    return client;
  }

  console.log('[AI] Starting local CodeFree-O server...');
  const serverOptions = {
    hostname: '127.0.0.1',
    port: 4096,
    timeout: 30000,
  };

  const dir = getDirectory();
  if (dir) {
    serverOptions.config = { ...serverOptions.config };
  }

  opencodeServer = await createOpencodeServer(serverOptions);
  console.log('[AI] CodeFree-O server started at:', opencodeServer.url);

  const clientConfig = { baseUrl: opencodeServer.url };
  if (dir) {
    clientConfig.directory = dir;
  }
  client = createOpencodeClient(clientConfig);
  return client;
}

/**
 * 关闭 CodeFree-O 后端服务
 */
export async function shutdownClient() {
  if (opencodeServer) {
    console.log('[AI] Shutting down CodeFree-O server...');
    opencodeServer.close();
    opencodeServer = null;
  }
  client = null;
}

/**
 * 获取已初始化的 SDK 客户端（需先调用 initializeClient）
 * @returns {object} OpencodeClient 实例
 */
export function getClient() {
  if (!client) {
    throw new Error('AI client not initialized. Call initializeClient() first.');
  }
  return client;
}

/**
 * 检查 AI 客户端是否已初始化就绪
 * @returns {boolean} 是否已就绪
 */
export function isClientReady() {
  return client !== null;
}

/**
 * 回复问题请求
 * @param {string} requestId - 问题请求 ID
 * @param {Array<Array<string>>} answers - 用户答案（按问题顺序，每个内层数组为选中的 label 列表）
 * @param {object} [options] - 选项
 * @param {string} [options.directory] - 项目目录
 * @param {string} [options.workspace] - 工作区
 * @returns {Promise<boolean>} 是否回复成功
 */
export async function replyQuestion(requestId, answers, options = {}) {
  const sdk = getClient();
  const result = await sdk.session._client.post({
    url: `/question/${encodeURIComponent(requestId)}/reply`,
    body: { answers },
    headers: {
      'Content-Type': 'application/json',
    },
  });
  if (result.error) {
    throw new Error(`Failed to reply question: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 获取 AI 配置信息
 * @returns {object} 配置对象
 */
export function getAIConfig() {
  const baseUrl = opencodeServer?.url || getBaseUrl() || '(auto-start on 127.0.0.1:4096)';
  return {
    baseUrl,
    directory: getDirectory(),
    apiKeyConfigured: !!getApiKey(),
  };
}

/**
 * 订阅 SSE 事件流，用于接收实时消息更新
 * @param {function} onEvent - 事件回调函数
 * @param {object} [options] - 订阅选项
 * @param {AbortSignal} [options.signal] - 取消信号
 * @returns {Promise<void>}
 */
export async function subscribeEvents(onEvent, options = {}) {
  const sdk = getClient();

  if (eventStream) {
    await unsubscribeEvents();
  }

  eventAbortController = new AbortController();
  const signal = options.signal
    ? AbortSignal.any([eventAbortController.signal, options.signal])
    : eventAbortController.signal;

  try {
    const result = await sdk.event.subscribe({
      signal,
    });

    eventStream = result.stream;

    for await (const event of eventStream) {
      if (signal.aborted) break;
      console.log(event)
      await onEvent(event);
    }
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error('[AI] SSE stream error:', error.message);
    }
  } finally {
    eventStream = null;
    eventAbortController = null;
  }
}

/**
 * 取消订阅 SSE 事件流
 */
export async function unsubscribeEvents() {
  if (eventAbortController) {
    eventAbortController.abort();
    eventAbortController = null;
  }
  eventStream = null;
}

/**
 * 创建新的会话
 * @param {string} [title] - 会话标题
 * @returns {Promise<object>} 创建的会话对象
 */
export async function createSession(title) {
  const sdk = getClient();
  const body = {};
  if (title) {
    body.title = title;
  }
  const result = await sdk.session.create({ body });
  console.log(result)
  if (result.error) {
    throw new Error(`Failed to create session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 获取会话列表
 * @returns {Promise<Array>} 会话列表
 */
export async function listSessions() {
  const sdk = getClient();
  const result = await sdk.session.list();
  if (result.error) {
    throw new Error(`Failed to list sessions: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 获取指定会话
 * @param {string} sessionId - 会话 ID
 * @returns {Promise<object>} 会话对象
 */
export async function getSession(sessionId) {
  const sdk = getClient();
  const result = await sdk.session.get({ path: { id: sessionId } });
  if (result.error) {
    throw new Error(`Failed to get session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 获取会话消息列表
 * @param {string} sessionId - 会话 ID
 * @param {object} [options] - 查询选项
 * @param {number} [options.limit] - 消息数量限制
 * @returns {Promise<Array>} 消息列表
 */
export async function getSessionMessages(sessionId, options = {}) {
  const sdk = getClient();
  const query = {};
  if (options.limit) {
    query.limit = options.limit;
  }
  const result = await sdk.session.messages({ path: { id: sessionId }, query });
  if (result.error) {
    throw new Error(`Failed to get messages: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 删除会话
 * @param {string} sessionId - 会话 ID
 * @returns {Promise<boolean>} 是否删除成功
 */
export async function deleteSession(sessionId) {
  const sdk = getClient();
  const result = await sdk.session.delete({ path: { id: sessionId } });
  if (result.error) {
    throw new Error(`Failed to delete session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 更新会话标题
 * @param {string} sessionId - 会话 ID
 * @param {string} title - 新标题
 * @returns {Promise<object>} 更新后的会话对象
 */
export async function updateSessionTitle(sessionId, title) {
  const sdk = getClient();
  const result = await sdk.session.update({
    path: { id: sessionId },
    body: { title },
  });
  if (result.error) {
    throw new Error(`Failed to update session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 发送用户消息（异步模式），通过 SSE 事件流接收响应
 * @param {string} sessionId - 会话 ID
 * @param {Array} parts - 消息 parts 数组（TextPartInput / FilePartInput 等）
 * @param {object} [options] - 发送选项
 * @param {string} [options.agent] - 指定 Agent
 * @param {object} [options.model] - 指定模型 { providerID, modelID }
 * @returns {Promise<void>} promptAsync 返回 204，实际响应通过 SSE 事件流获取
 */
export async function sendPromptAsync(sessionId, parts, options = {}) {
  const sdk = getClient();
  const body = { parts };
  if (options.agent) {
    body.agent = options.agent;
  }
  if (options.model) {
    body.model = options.model;
  }
  const result = await sdk.session.promptAsync({
    path: { id: sessionId },
    body,
  });

  if (result.error) {
    throw new Error(`Failed to send prompt: ${JSON.stringify(result.error)}`);
  }
}

/**
 * 发送用户消息（同步阻塞模式），等待完整响应
 * @param {string} sessionId - 会话 ID
 * @param {Array} parts - 消息 parts 数组（TextPartInput / FilePartInput 等）
 * @param {object} [options] - 发送选项
 * @param {string} [options.agent] - 指定 Agent
 * @param {object} [options.model] - 指定模型 { providerID, modelID }
 * @returns {Promise<object>} { info: AssistantMessage, parts: Array<Part> }
 */
export async function sendPrompt(sessionId, parts, options = {}) {
  const sdk = getClient();
  const body = { parts };
  if (options.agent) {
    body.agent = options.agent;
  }
  if (options.model) {
    body.model = options.model;
  }
  const result = await sdk.session.prompt({
    path: { id: sessionId },
    body,
  });
  if (result.error) {
    throw new Error(`Failed to send prompt: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 中止当前会话的 AI 响应
 * @param {string} sessionId - 会话 ID
 * @returns {Promise<boolean>} 是否中止成功
 */
export async function abortSession(sessionId) {
  const sdk = getClient();
  const result = await sdk.session.abort({ path: { id: sessionId } });
  if (result.error) {
    throw new Error(`Failed to abort session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 响应权限请求
 * @param {string} sessionId - 会话 ID
 * @param {string} permissionId - 权限 ID
 * @param {'once'|'always'|'reject'} response - 响应类型
 * @returns {Promise<boolean>} 是否响应成功
 */
export async function respondPermission(sessionId, permissionId, response) {
  const sdk = getClient();
  const result = await sdk.postSessionIdPermissionsPermissionId({
    path: { id: sessionId, permissionID: permissionId },
    body: { response },
  });
  if (result.error) {
    throw new Error(`Failed to respond permission: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 获取 LSP 服务器状态
 * @returns {Promise<Array<{id: string, name: string, root: string, status: string}>>} LSP 服务器状态列表
 */
export async function getLspStatus() {
  const sdk = getClient();
  const result = await sdk.lsp.status();
  if (result.error) {
    throw new Error(`Failed to get LSP status: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 使用 Node.js 原生功能扫描函数名和类名
 * @param {string} query - 搜索关键词
 * @param {object} [options] - 搜索选项
 * @param {string} [options.directory] - 项目目录
 * @returns {Promise<Array<{name: string, kind: number, filePath: string, line: number, snippet: string}>>} 符号列表
 */
export async function findSymbols(query, options = {}) {
  return scanFindSymbols(query, options);
}

/**
 * 使用 ripgrep 搜索文本内容
 * @param {string} pattern - 搜索模式
 * @param {object} [options] - 搜索选项
 * @param {string} [options.directory] - 项目目录
 * @param {string} [options.workspace] - 工作区
 * @returns {Promise<Array>} 匹配结果列表
 */
export async function findText(pattern, options = {}) {
  const sdk = getClient();
  const params = { pattern };
  if (options.directory) params.directory = options.directory;
  if (options.workspace) params.workspace = options.workspace;
  const result = await sdk.find.text(params);
  if (result.error) {
    throw new Error(`Failed to find text: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 搜索文件或目录
 * @param {string} query - 搜索关键词
 * @param {object} [options] - 搜索选项
 * @param {string} [options.directory] - 项目目录
 * @param {string} [options.workspace] - 工作区
 * @param {string} [options.dirs] - 是否包含目录 "true"|"false"
 * @param {string} [options.type] - 类型 "file"|"directory"
 * @param {number} [options.limit] - 结果数量限制
 * @returns {Promise<Array<string>>} 文件路径列表
 */
export async function findFiles(query, options = {}) {
  const sdk = getClient();
  const params = { query };
  if (options.directory) params.directory = options.directory;
  if (options.workspace) params.workspace = options.workspace;
  if (options.dirs) params.dirs = options.dirs;
  if (options.type) params.type = options.type;
  if (options.limit) params.limit = options.limit;
  const result = await sdk.find.files(params);
  if (result.error) {
    throw new Error(`Failed to find files: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 获取 MCP 服务器状态列表
 * @returns {Promise<object>} MCP 服务器状态映射 { [name]: McpStatus }
 */
export async function getMcpStatus() {
  const sdk = getClient();
  const result = await sdk.mcp.status();
  if (result.error) {
    throw new Error(`Failed to get MCP status: ${JSON.stringify(result.error)}`);
  }
  return result.data || {};
}

/**
 * 动态添加 MCP 服务器
 * @param {string} name - MCP 服务器名称
 * @param {object} config - MCP 服务器配置 (McpLocalConfig | McpRemoteConfig)
 * @returns {Promise<object>} 更新后的 MCP 服务器状态映射
 */
export async function addMcpServer(name, config) {
  const sdk = getClient();
  const result = await sdk.mcp.add({
    body: { name, config },
  });
  if (result.error) {
    throw new Error(`Failed to add MCP server: ${JSON.stringify(result.error)}`);
  }
  try {
    const currentConfig = await getConfig();
    console.log('[AI] Current config mcp keys:', Object.keys(currentConfig.mcp || {}));
    const mcpConfig = currentConfig.mcp || {};
    mcpConfig[name] = config;
    currentConfig.mcp = mcpConfig;
    const updateResult = await updateConfig(currentConfig);
    console.log('[AI] Config update result:', JSON.stringify(updateResult).substring(0, 200));
  } catch (e) {
    console.warn('[AI] Failed to persist MCP server config:', e.message, e.stack);
  }
  return result.data || {};
}

/**
 * 连接 MCP 服务器
 * @param {string} name - MCP 服务器名称
 * @returns {Promise<boolean>} 是否连接成功
 */
export async function connectMcpServer(name) {
  const sdk = getClient();
  const result = await sdk.mcp.connect({
    path: { name },
  });
  if (result.error) {
    throw new Error(`Failed to connect MCP server: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 断开 MCP 服务器
 * @param {string} name - MCP 服务器名称
 * @returns {Promise<boolean>} 是否断开成功
 */
export async function disconnectMcpServer(name) {
  const sdk = getClient();
  const result = await sdk.mcp.disconnect({
    path: { name },
  });
  if (result.error) {
    throw new Error(`Failed to disconnect MCP server: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 启动 MCP 服务器的 OAuth 认证流程
 * @param {string} name - MCP 服务器名称
 * @returns {Promise<object>} 包含 authorizationUrl 的对象
 */
export async function startMcpAuth(name) {
  const sdk = getClient();
  const result = await sdk.mcp.auth.start({
    path: { name },
  });
  if (result.error) {
    throw new Error(`Failed to start MCP auth: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 移除 MCP 服务器的 OAuth 凭证
 * @param {string} name - MCP 服务器名称
 * @returns {Promise<object>} 操作结果
 */
export async function removeMcpAuth(name) {
  const sdk = getClient();
  const result = await sdk.mcp.auth.remove({
    path: { name },
  });
  if (result.error) {
    throw new Error(`Failed to remove MCP auth: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 设置 MCP 服务器的认证凭证
 * @param {string} name - MCP 服务器名称
 * @param {object} auth - 认证信息
 * @returns {Promise<object>} 操作结果
 */
export async function setMcpAuth(name, auth) {
  const sdk = getClient();
  const result = await sdk.mcp.auth.set({
    body: { name, auth },
  });
  if (result.error) {
    throw new Error(`Failed to set MCP auth: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

/**
 * 获取 CodeFree-O 配置（包含 MCP 配置）
 * @returns {Promise<object>} 完整配置对象
 */
export async function getConfig() {
  const sdk = getClient();
  const result = await sdk.config.get();
  if (result.error) {
    throw new Error(`Failed to get config: ${JSON.stringify(result.error)}`);
  }
  return result.data || {};
}

/**
 * 从 SKILL.md 文件解析 frontmatter 中的元数据
 * @param {string} filePath - SKILL.md 文件路径
 * @returns {{name: string, description: string}} 解析出的元数据
 */
function parseSkillMeta(filePath) {
  try {
    const content = fs.readFileSync(filePath, 'utf-8');
    const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
    if (!frontmatterMatch) return { name: '', description: '' };
    const meta = {};
    for (const line of frontmatterMatch[1].split('\n')) {
      const idx = line.indexOf(':');
      if (idx === -1) continue;
      const key = line.slice(0, idx).trim();
      let value = line.slice(idx + 1).trim();
      if (value.startsWith('"') && value.endsWith('"')) {
        value = value.slice(1, -1);
      }
      meta[key] = value;
    }
    return { name: meta.name || '', description: meta.description || '' };
  } catch {
    return { name: '', description: '' };
  }
}

/**
 * 扫描指定 skills 目录，返回 Skill 列表
 * @param {string} skillsDir - skills 目录路径
 * @param {'global' | 'project'} scope - 作用域
 * @param {object} disabledMap - 禁用状态映射
 * @returns {Array<{name: string, description: string, scope: string, enabled: boolean}>} Skill 列表
 */
function scanSkillsDir(skillsDir, scope, disabledMap) {
  if (!fs.existsSync(skillsDir)) return [];
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
  return entries
    .filter((e) => e.isDirectory())
    .map((e) => {
      const skillMdPath = path.join(skillsDir, e.name, 'SKILL.md');
      const meta = fs.existsSync(skillMdPath) ? parseSkillMeta(skillMdPath) : { name: e.name, description: '' };
      return {
        name: meta.name || e.name,
        dirname: e.name,
        description: meta.description || '',
        scope,
        enabled: disabledMap[e.name] !== true,
      };
    });
}

/**
 * 获取全局 Skill 禁用状态的持久化文件路径
 * @returns {string} 禁用状态文件路径
 */
function getGlobalSkillStatePath() {
  return path.join(os.homedir(), '.codefree-o', '.local', 'share', 'skill-state.json');
}

/**
 * 获取项目 Skill 禁用状态的持久化文件路径
 * @returns {string} 禁用状态文件路径
 */
function getProjectSkillStatePath() {
  return path.join(currentDirectory, '.codefree-o', 'skill-state.json');
}

/**
 * 读取指定路径的 Skill 禁用状态映射
 * @param {string} statePath - 禁用状态文件路径
 * @returns {object} 禁用状态映射
 */
function readSkillStateFile(statePath) {
  try {
    if (fs.existsSync(statePath)) {
      return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
    }
  } catch { }
  return {};
}

/**
 * 写入 Skill 禁用状态映射到指定路径
 * @param {string} statePath - 禁用状态文件路径
 * @param {object} state - 禁用状态映射
 */
function writeSkillStateFile(statePath, state) {
  const dir = path.dirname(statePath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf-8');
}

/**
 * 获取 Skill 列表（全局 + 项目级别）
 * @returns {Promise<Array<{name: string, description: string, scope: string, enabled: boolean}>>} Skill 列表
 */
export async function getSkills() {
  const globalDisabledMap = readSkillStateFile(getGlobalSkillStatePath());
  const homeDir = os.homedir();
  const globalSkillsDir = path.join(homeDir, '.codefree-o', '.config', 'skills');
  const globalSkills = scanSkillsDir(globalSkillsDir, 'global', globalDisabledMap);

  let projectSkills = [];
  if (currentDirectory) {
    const projectDisabledMap = readSkillStateFile(getProjectSkillStatePath());
    const projectSkillsDir = path.join(currentDirectory, '.codefree-o', 'skills');
    projectSkills = scanSkillsDir(projectSkillsDir, 'project', projectDisabledMap);
  }

  return [...globalSkills, ...projectSkills];
}

/**
 * 切换 Skill 的启用/禁用状态
 * @param {string} name - Skill 名称
 * @param {boolean} enabled - 是否启用
 * @returns {Promise<boolean>} 是否切换成功
 */
export async function toggleSkill(name, scope, enabled) {
  const skills = await getSkills();
  const skill = skills.find((s) => s.name === name && s.scope === scope);
  if (!skill) {
    throw new Error(`Skill "${name}" with scope "${scope}" not found`);
  }
  const statePath = skill.scope === 'global' ? getGlobalSkillStatePath() : getProjectSkillStatePath();
  const state = readSkillStateFile(statePath);
  if (enabled) {
    delete state[skill.dirname];
  } else {
    state[skill.dirname] = true;
  }
  writeSkillStateFile(statePath, state);
  return true;
}

/**
 * 更新 CodeFree-O 配置
 * @param {object} config - 完整配置对象
 * @returns {Promise<object>} 更新后的配置
 */
export async function updateConfig(config) {
  const sdk = getClient();
  const result = await sdk.config.update({
    body: config,
  });
  if (result.error) {
    throw new Error(`Failed to update config: ${JSON.stringify(result.error)}`);
  }
  return result.data || {};
}

/**
 * 将 SDK 的 Part 列表转换为纯文本内容
 * @param {Array} parts - SDK Part 列表
 * @returns {string} 拼接后的文本内容
 */
export function extractTextFromParts(parts) {
  if (!parts || !Array.isArray(parts)) return '';
  return parts
    .filter((part) => part.type === 'text')
    .map((part) => part.text || '')
    .join('');
}

/**
 * 从 SDK Part 列表中提取文件附件
 * @param {Array} parts - SDK Part 列表
 * @returns {Array} FileAttachment 格式列表
 */
export function extractAttachmentsFromParts(parts) {
  if (!parts || !Array.isArray(parts)) return [];
  return parts
    .filter((part) => part.type === 'file')
    .map((part) => ({
      id: part.id,
      mime: part.mime || 'application/octet-stream',
      filename: part.filename,
      url: part.url,
      source: part.source,
    }));
}

/**
 * 获取可用的 AI Provider 和 Model 列表
 * @returns {Promise<{all: Array, default: Object, connected: Array<string>}>} Provider 列表信息
 */
export async function listProviders() {
  const sdk = getClient();
  const result = await sdk.provider.list();
  if (result.error) {
    throw new Error(`Failed to list providers: ${JSON.stringify(result.error)}`);
  }
  return result.data || { all: [], default: {}, connected: [] };
}

/**
 * 获取可用的 Agent 列表
 * @returns {Promise<Array>} Agent 列表
 */
export async function listAgents() {
  const sdk = getClient();
  const result = await sdk.app.agents();
  if (result.error) {
    throw new Error(`Failed to list agents: ${JSON.stringify(result.error)}`);
  }
  return result.data || [];
}

/**
 * 将 SDK 的 Message + Parts 转换为前端 ChatMessage 格式
 * @param {object} messageInfo - SDK Message 对象
 * @param {Array} parts - SDK Part 列表
 * @returns {object} ChatMessage 格式 { id, role, content, timestamp, attachments }
 */
export function convertToChatMessage(messageInfo, parts) {
  return {
    id: messageInfo.id,
    role: messageInfo.role,
    content: extractTextFromParts(parts),
    timestamp: new Date(messageInfo.time.created * 1000).toISOString(),
    attachments: extractAttachmentsFromParts(parts),
  };
}

export async function summarizeSession(sessionId, model = {}) {
  const sdk = getClient();
  const body = {};
  if (model.providerID) body.providerID = model.providerID;
  if (model.modelID) body.modelID = model.modelID;
  const result = await sdk.session.summarize({
    path: { id: sessionId },
    body,
  });
  if (result.error) {
    throw new Error(`Failed to summarize session: ${JSON.stringify(result.error)}`);
  }
  return result.data;
}

function formatTimestamp(unixSeconds) {
  if (!unixSeconds) return '';
  const d = new Date(unixSeconds * 1000);
  return d.toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });
}

function escapeMarkdown(text) {
  if (!text) return '';
  return text;
}

function convertMessagesToMarkdown(session, messages) {
  const title = session.title || '未命名会话';
  let md = `# ${escapeMarkdown(title)}\n\n`;

  if (session.id) {
    md += `> Session ID: ${session.id}\n`;
  }
  md += '\n---\n\n';

  for (const msg of messages) {
    if (msg.role === 'user') {
      md += `## 👤 用户\n\n`;
      const time = formatTimestamp(msg.time?.created);
      if (time) {
        md += `*${time}*\n\n`;
      }
    } else if (msg.role === 'assistant') {
      md += `## 🤖 助手\n\n`;
      const time = formatTimestamp(msg.time?.created);
      if (time) {
        md += `*${time}*\n\n`;
      }
    }

    if (msg.parts && Array.isArray(msg.parts)) {
      for (const part of msg.parts) {
        if (part.type === 'text' && part.text) {
          md += `${escapeMarkdown(part.text)}\n\n`;
        } else if (part.type === 'reasoning' && part.text) {
          md += `<details>\n<summary>💭 思考过程</summary>\n\n${escapeMarkdown(part.text)}\n\n</details>\n\n`;
        } else if (part.type === 'tool') {
          const state = part.state || {};
          const toolName = part.tool || 'unknown';
          if (state.status === 'completed') {
            md += `<details>\n<summary>🔧 工具调用: ${toolName}${state.title ? ' - ' + state.title : ''}</summary>\n\n`;
            if (state.input && Object.keys(state.input).length > 0) {
              md += `**输入:**\n\`\`\`json\n${JSON.stringify(state.input, null, 2)}\n\`\`\`\n\n`;
            }
            if (state.output) {
              md += `**输出:**\n\`\`\`\n${state.output}\n\`\`\`\n\n`;
            }
            md += `</details>\n\n`;
          } else if (state.status === 'error') {
            md += `<details>\n<summary>🔧 工具调用: ${toolName} (❌ 失败)</summary>\n\n`;
            md += `**错误:** ${state.error || 'Unknown error'}\n\n</details>\n\n`;
          }
        } else if (part.type === 'file') {
          const filename = part.filename || part.url || 'file';
          md += `📎 *附件: ${filename}*\n\n`;
        }
      }
    }

    md += '---\n\n';
  }

  return md;
}

function openInBrowser(filePath) {
  const fileUrl = `file:///${filePath.replace(/\\/g, '/')}`;
  const platform = os.platform();
  return new Promise((resolve, reject) => {
    let cmd;
    if (platform === 'win32') {
      cmd = `start "" "${fileUrl}"`;
    } else if (platform === 'darwin') {
      cmd = `open "${fileUrl}"`;
    } else {
      cmd = `xdg-open "${fileUrl}"`;
    }
    exec(cmd, (error) => {
      if (error) {
        reject(new Error(`Failed to open browser: ${error.message}`));
      } else {
        resolve();
      }
    });
  });
}

export async function exportSession(sessionId) {
  const session = await getSession(sessionId);
  const messages = await getSessionMessages(sessionId);

  const markdown = convertMessagesToMarkdown(session, messages);

  const tmpDir = path.join(os.tmpdir(), 'codefree-o-export');
  if (!fs.existsSync(tmpDir)) {
    fs.mkdirSync(tmpDir, { recursive: true });
  }

  const safeTitle = (session.title || 'session').replace(/[\\/:*?"<>|]/g, '_');
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const fileName = `${safeTitle}_${timestamp}.md`;
  const filePath = path.join(tmpDir, fileName);

  fs.writeFileSync(filePath, markdown, 'utf-8');

  await openInBrowser(filePath);

  return { path: filePath, title: session.title };
}