import express from 'express';
import fs from 'fs';
import path from 'path';
import {
  createSession,
  listSessions,
  getSession,
  getSessionMessages,
  deleteSession,
  sendPromptAsync,
  abortSession,
  respondPermission,
  replyQuestion,
  updateDirectory,
  getAIConfig,
  isClientReady,
  getLspStatus,
  findSymbols,
  findText,
  findFiles,
  getMcpStatus,
  addMcpServer,
  connectMcpServer,
  disconnectMcpServer,
  startMcpAuth,
  removeMcpAuth,
  getConfig,
  updateConfig,
  getSkills,
  toggleSkill,
  listProviders,
  listAgents,
  readModelConfig,
  writeModelConfig,
  updateModelRecent,
  sendPrompt,
  extractTextFromParts,
  summarizeSession,
  exportSession
} from '../services/aiService.js';

import { getInitPrompt } from '../prompts/index.js'

/**
 * 使用 XML 结构构建中文提示词。
 * 不读取任何文件内容，仅使用 filePath 和 snippet 字段。
 * @param {Array<{type: string, label: string, filePath?: string, snippet?: string}>} contexts
 * @returns {string|null}
 */
function buildContextPrompt(contexts) {
  const hasFilePath = contexts.some((ctx) => ctx.filePath);
  if (!hasFilePath) return null;

  let xml = '<project_context>\n';

  for (const ctx of contexts) {
    const filePath = (ctx.filePath || '').replace(/\\/g, '/');
    if (!filePath) continue;

    if (ctx.type === 'file') {
      xml += `  <file path="${filePath}"/>\n`;
    } else if (ctx.type === 'folder') {
      xml += `  <folder path="${filePath}"/>\n`;
    } else if (ctx.type === 'code') {
      const snippet = (ctx.snippet || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
      xml += `  <code path="${filePath}">\n    ${snippet}\n  </code>\n`;
    }
  }

  xml += '</project_context>';

  return `用户提供了以下项目上下文信息，请基于这些信息使用中文回答用户的问题：\n\n${xml}`;
}

const router = express.Router();

router.use((req, res, next) => {
  if (!isClientReady()) {
    return res.status(503).json({ error: 'AI client not ready, please wait...' });
  }
  next();
});

router.post('/session', async (req, res) => {
  try {
    const session = await createSession(req.body.title);
    res.json(session);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/session', async (req, res) => {
  try {
    const sessions = await listSessions();
    res.json(sessions);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/session/:id', async (req, res) => {
  try {
    const session = await getSession(req.params.id);
    res.json(session);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/session/:id/message', async (req, res) => {
  try {
    const messages = await getSessionMessages(req.params.id);
    res.json(messages);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.delete('/session/:id', async (req, res) => {
  try {
    await deleteSession(req.params.id);
    res.status(204).end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/session/:id/prompt_async', async (req, res) => {
  try {
    const { parts, agent, model } = req.body;
    const options = { agent, model };

    if (!parts || !Array.isArray(parts) || parts.length === 0) {
      return res.status(400).json({ error: 'parts is required and must be a non-empty array' });
    }

    // const finalParts = [...parts];

    // if (contexts && Array.isArray(contexts) && contexts.length > 0) {
    //   const contextPrompt = buildContextPrompt(contexts);
    //   if (contextPrompt) {
    //     finalParts.unshift({ type: 'text', text: contextPrompt });
    //   }
    // }
    // console.log(finalParts)
    await sendPromptAsync(req.params.id, parts, options);
    res.status(204).end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/session/:id/abort', async (req, res) => {
  try {
    await abortSession(req.params.id);
    res.status(204).end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/session/:id/permissions/:permissionId', async (req, res) => {
  try {
    await respondPermission(req.params.id, req.params.permissionId, req.body.response);
    res.status(204).end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/question/:id/reply', async (req, res) => {
  try {
    const { answers } = req.body;
    if (!answers || !Array.isArray(answers)) {
      return res.status(400).json({ error: 'answers is required and must be an array' });
    }
    const { directory, workspace } = req.query;
    await replyQuestion(req.params.id, answers, { directory, workspace });
    res.status(204).end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/config/directory', async (req, res) => {
  try {
    const { directory } = req.body;
    if (!directory || typeof directory !== 'string') {
      return res.status(400).json({ error: 'directory is required and must be a string' });
    }
    await updateDirectory(directory);
    const config = getAIConfig();
    res.json({ directory: config.directory, baseUrl: config.baseUrl });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/config', async (req, res) => {
  try {
    const config = getAIConfig();
    res.json({
      directory: config.directory,
      baseUrl: config.baseUrl,
      apiKeyConfigured: config.apiKeyConfigured,
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/lsp', async (req, res) => {
  try {
    const status = await getLspStatus();
    res.json(status);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/find/symbol', async (req, res) => {
  try {
    const { query, directory, workspace } = req.query;
    if (!query) {
      return res.status(400).json({ error: 'query is required' });
    }
    const symbols = await findSymbols(query, { directory, workspace });
    res.json(symbols);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/find/text', async (req, res) => {
  try {
    const { pattern, directory, workspace } = req.query;
    if (!pattern) {
      return res.status(400).json({ error: 'pattern is required' });
    }
    const results = await findText(pattern, { directory, workspace });
    res.json(results);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/find/file', async (req, res) => {
  try {
    const { query, directory, workspace, dirs, type, limit } = req.query;
    if (!query) {
      return res.status(400).json({ error: 'query is required' });
    }
    const options = { directory, workspace };
    if (dirs) options.dirs = dirs;
    if (type) options.type = type;
    if (limit) options.limit = parseInt(limit, 10);
    const files = await findFiles(query, options);
    res.json(files);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/skill', async (req, res) => {
  try {
    const skills = await getSkills();
    res.json(skills);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/skill/:name/toggle', async (req, res) => {
  try {
    const { name } = req.params;
    const { enabled, scope } = req.body;
    if (typeof enabled !== 'boolean') {
      return res.status(400).json({ error: 'enabled is required and must be a boolean' });
    }
    if (!scope || (scope !== 'global' && scope !== 'project')) {
      return res.status(400).json({ error: 'scope is required and must be "global" or "project"' });
    }
    const result = await toggleSkill(name, scope, enabled);
    res.json({ success: result });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/mcp', async (req, res) => {
  try {
    const status = await getMcpStatus();
    const config = await getConfig();
    const mcpConfig = config.mcp || {};
    const servers = Object.entries(mcpConfig).map(([name, cfg]) => ({
      name,
      config: cfg,
      status: status[name] || { status: 'disabled' },
    }));
    res.json(servers);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/mcp', async (req, res) => {
  try {
    const { name, config } = req.body;
    if (!name || !config) {
      return res.status(400).json({ error: 'name and config are required' });
    }
    const status = await addMcpServer(name, config);
    res.json(status);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/mcp/:name/connect', async (req, res) => {
  try {
    const result = await connectMcpServer(req.params.name);
    res.json({ success: result !== false });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/mcp/:name/disconnect', async (req, res) => {
  try {
    const result = await disconnectMcpServer(req.params.name);
    res.json({ success: result !== false });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/mcp/:name/auth', async (req, res) => {
  try {
    const result = await startMcpAuth(req.params.name);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.delete('/mcp/:name/auth', async (req, res) => {
  try {
    const result = await removeMcpAuth(req.params.name);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.delete('/mcp/:name', async (req, res) => {
  try {
    const name = req.params.name;
    try {
      await disconnectMcpServer(name);
    } catch (e) {
      console.warn(`[MCP] Failed to disconnect "${name}" before delete:`, e.message);
    }
    const config = await getConfig();
    const mcpConfig = config.mcp || {};
    if (!mcpConfig[name]) {
      return res.status(404).json({ error: `MCP server "${name}" not found` });
    }
    delete mcpConfig[name];
    config.mcp = mcpConfig;
    await updateConfig(config);
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/provider', async (req, res) => {
  try {
    const providers = await listProviders();
    res.json(providers);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/agent', async (req, res) => {
  try {
    const agents = await listAgents();
    res.json(agents);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.get('/model', (req, res) => {
  try {
    const config = readModelConfig();
    res.json(config);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/model', (req, res) => {
  try {
    const config = req.body;
    if (!config || typeof config !== 'object') {
      return res.status(400).json({ error: 'config is required and must be an object' });
    }
    const result = writeModelConfig(config);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/model/recent', (req, res) => {
  try {
    const { providerID, modelID } = req.body;
    if (!providerID || !modelID) {
      return res.status(400).json({ error: 'providerID and modelID are required' });
    }
    const result = updateModelRecent(providerID, modelID);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

const SHORTCUT_HANDLERS = {
  init: async ({ sessionId }) => {
    const url = process.env.CODEFREE_DIRECTORY || ''
    const prompt = getInitPrompt(url);
    await sendPromptAsync(sessionId, [{ type: 'text', text: prompt }]);
  },
  compact: async ({ sessionId, providerID, modelID }) => {
    const model = {};
    if (providerID) model.providerID = providerID;
    if (modelID) model.modelID = modelID;
    await summarizeSession(sessionId, model);
  },
  export: async ({ sessionId }) => {
    return await exportSession(sessionId);
  },
};

router.post('/shortcut', async (req, res) => {
  try {
    const { commandName, sessionId, providerID, modelID } = req.body;
    if (!commandName || typeof commandName !== 'string') {
      return res.status(400).json({ error: 'commandName is required and must be a string' });
    }
    const name = commandName.startsWith('/') ? commandName.slice(1) : commandName;
    const handler = SHORTCUT_HANDLERS[name];
    if (!handler) {
      return res.status(404).json({ error: `Unknown shortcut command: /${name}` });
    }
    const result = await handler({ sessionId, providerID, modelID });
    if (result !== undefined) {
      res.json(result);
    } else {
      res.status(204).end();
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

router.post('/optimize', async (req, res) => {
  let sessionId = null;
  try {
    const { content } = req.body;
    if (!content || typeof content !== 'string') {
      return res.status(400).json({ error: 'content is required and must be a string' });
    }
    const session = await createSession('Optimize Prompt');
    sessionId = session.id;

    const systemPrompt = `你是一个专业的提示词优化助手。请帮助用户优化他们的输入内容，使其更加清晰、具体和有效。
  
  优化原则：
  1. 保持用户原始意图不变
  2. 使表达更加清晰、结构化
  3. 补充必要的上下文和细节
  4. 使用更专业的术语和表达方式
  5. 如果是编程相关的问题，确保技术术语准确
  
  请直接输出优化后的内容，不要添加任何解释或前缀。`;

    const parts = [
      { type: 'text', text: systemPrompt },
      { type: 'text', text: `请优化以下内容：\n\n${content}` },
    ];

    const result = await sendPrompt(sessionId, parts);

    const text = extractTextFromParts(result.parts || []);

    res.json({ optimized: text.trim() });
  } catch (error) {
    res.status(500).json({ error: error.message });
  } finally {
    if (sessionId) {
      try {
        await deleteSession(sessionId);
      } catch (e) {
        console.error('[Git] Failed to delete temp session:', e.message);
      }
    }
  }
});

export { router as codefreeApiRouter };
