const crypto = require('crypto');

function registerAIRoutes(app, { db, ai, memory, skills, briefing }) {
  app.get('/api/ai/conversations/:division_id', (req, res) => {
    const divisionId = parseInt(req.params.division_id, 10);
    const conversations = db.prepare(`
      SELECT c.id, c.division_id, c.title, c.created_at, c.updated_at,
             COUNT(m.id) as message_count
      FROM ai_conversations c
      LEFT JOIN ai_messages m ON m.conversation_id = c.id
      WHERE c.division_id = ?
      GROUP BY c.id
      ORDER BY datetime(c.updated_at) DESC, c.id DESC
    `).all(divisionId);
    res.json({ conversations });
  });

  app.get('/api/ai/conversations/thread/:id', (req, res) => {
    const conversation = db.prepare('SELECT * FROM ai_conversations WHERE id = ?').get(req.params.id);
    if (!conversation) return res.status(404).json({ error: 'Conversation not found' });
    const messages = db.prepare('SELECT id, role, content, created_at FROM ai_messages WHERE conversation_id = ? ORDER BY id ASC').all(req.params.id);
    res.json({ conversation, messages });
  });

  app.post('/api/ai/conversations', (req, res) => {
    const divisionId = parseInt(req.body?.division_id, 10);
    if (!divisionId) return res.status(400).json({ error: 'division_id required' });
    const now = new Date().toISOString();
    const title = String(req.body?.title || 'New conversation').trim().substring(0, 120) || 'New conversation';
    const result = db.prepare('INSERT INTO ai_conversations (division_id, title, created_at, updated_at) VALUES (?, ?, ?, ?)').run(divisionId, title, now, now);
    res.json({ id: result.lastInsertRowid, title, created_at: now, updated_at: now });
  });

  app.delete('/api/ai/conversations/:id', (req, res) => {
    db.prepare('DELETE FROM ai_messages WHERE conversation_id = ?').run(req.params.id);
    db.prepare('DELETE FROM ai_conversations WHERE id = ?').run(req.params.id);
    res.json({ success: true });
  });

  app.post('/api/daily-briefing', async (req, res) => {
    const data = await briefing.generateBriefing(req.body && req.body.force);
    res.json(data);
  });

  app.post('/api/ai/guided-tasks', async (req, res) => {
    const { item_id: itemId, answers } = req.body;
    const item = db.prepare('SELECT * FROM items WHERE id = ?').get(itemId);
    const division = db.prepare('SELECT * FROM divisions WHERE id = ?').get(item.division_id);
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const memories = await memory.searchMemory(`tasks for ${division.name} ${item.title}`, settings, item.division_id, 5);

    const memoryContext = memories.length > 0 ? memories.map((entry) => `- ${entry.content}`).join('\n') : 'No relevant history yet.';
    const answersContext = Object.entries(answers || {})
      .filter(([, value]) => value && value.trim())
      .map(([key, value]) => `${key}: ${value}`)
      .join('\n') || 'No additional context provided.';

    const prompt = `Generate exactly 5 highly specific, actionable tasks for client "${item.title}" currently in "${division.name}".
Division VFP: ${division.vfp}
User-provided context:
${answersContext}
Relevant agency memory:
${memoryContext}
Tasks must be SPECIFIC to this client and context — not generic.
Return ONLY a raw JSON array of 5 task strings (max 12 words each).`;

    try {
      const skillsCtx = await skills.buildSkillsContext(`tasks for ${item.title} in ${division.name}`, settings, item.division_id);
      const aiText = await ai.callAI(prompt, settings, `You are the expert director for "${division.name}". Be surgical and specific. ${ai.buildBusinessContext()} ${ai.buildLanguageInstruction(settings, { json: true })}${skillsCtx}`);
      const rawTasks = ai.parseJsonResponse(aiText);
      if (!Array.isArray(rawTasks)) throw new Error('Expected array');

      const tasks = rawTasks
        .map((t) => {
          if (typeof t === 'string') return t.trim();
          if (typeof t === 'object' && t !== null) {
            const val = t.task || t.text || t.title || t.action || t.content || Object.values(t)[0];
            return String(val || '').trim();
          }
          return String(t).trim();
        })
        .filter((t) => t.length > 3)
        .slice(0, 5);

      const insertTodo = db.prepare('INSERT INTO todos (item_id, division_id, text, done) VALUES (?, ?, ?, 0)');
      tasks.forEach((task) => insertTodo.run(item.id, item.division_id, task.substring(0, 100)));
      memory.logMemory(item.division_id, 'ai_tasks_generated', `AI generated ${tasks.length} guided tasks for '${item.title}'`);
      res.json({ success: true, tasks });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  });

  app.post('/api/ai/guided-battle-plan', async (req, res) => {
    const { division_id: divisionId, period, answers } = req.body;
    const division = db.prepare('SELECT * FROM divisions WHERE id = ?').get(divisionId);
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const existing = db.prepare('SELECT period, title FROM battle_plans WHERE division_id = ? AND period != ?').all(divisionId, period);
    const memories = await memory.searchMemory(`${period} goals ${division.name}`, settings, divisionId, 4);

    const memoryContext = memories.filter((entry) => entry.score > 0 || entry.fallback).map((entry) => `- ${entry.content}`).join('\n') || '';
    const existingContext = existing.length > 0 ? existing.map((plan) => `[${plan.period.toUpperCase()}] ${plan.title}`).join('\n') : '';
    const answersContext = Object.entries(answers || {}).filter(([, value]) => value && value.trim()).map(([key, value]) => `${key}: ${value}`).join('\n') || '';

    const prompt = `Generate exactly 3 highly specific '${period}' strategic goals for division "${division.name}".
Division VFP: ${division.vfp}
${existingContext ? `Higher-level strategy already in place:\n${existingContext}` : ''}
${answersContext ? `Director's context for this ${period}:\n${answersContext}` : ''}
${memoryContext ? `Relevant agency memory:\n${memoryContext}` : ''}
Return ONLY a raw JSON array of 3 strategic goal strings (max 15 words each). Be specific and tactical.`;

    try {
      const skillsCtx = await skills.buildSkillsContext(`${period} strategy for ${division.name}`, settings, divisionId);
      const aiText = await ai.callAI(prompt, settings, `You are the Expert AI Director for "${division.name}". ${ai.buildBusinessContext()} ${ai.buildLanguageInstruction(settings, { json: true })}${skillsCtx}`);
      const rawPlans = ai.parseJsonResponse(aiText);
      if (!Array.isArray(rawPlans)) throw new Error('Expected array');

      // Normalise: AI sometimes returns [{goal:"..."},{title:"..."}] instead of ["...","..."]
      const plans = rawPlans
        .map((p) => {
          if (typeof p === 'string') return p.trim();
          if (typeof p === 'object' && p !== null) {
            const val = p.goal || p.title || p.text || p.content || p.plan || p.objective || Object.values(p)[0];
            return String(val || '').trim();
          }
          return String(p).trim();
        })
        .filter((p) => p.length > 3)
        .slice(0, 3);

      const insertPlan = db.prepare('INSERT INTO battle_plans (division_id, period, title, created_at) VALUES (?, ?, ?, ?)');
      const now = new Date().toISOString();
      plans.forEach((plan) => insertPlan.run(divisionId, period, plan.substring(0, 200), now));

      if (period === 'daily') {
        const dateStr = now.split('T')[0];
        const cardTitle = `🔥 Daily Execution (${dateStr})`;
        let executionCard = db.prepare('SELECT id FROM items WHERE division_id = ? AND title = ?').get(divisionId, cardTitle);
        if (!executionCard) {
          const insertCard = db.prepare('INSERT INTO items (division_id, title, magic_link) VALUES (?, ?, ?)').run(divisionId, cardTitle, crypto.randomUUID());
          executionCard = { id: insertCard.lastInsertRowid };
        }
        const insertTodo = db.prepare('INSERT INTO todos (item_id, division_id, text, done) VALUES (?, ?, ?, 0)');
        plans.forEach((plan) => insertTodo.run(executionCard.id, divisionId, `[BP] ${plan.substring(0, 100)}`));
      }

      memory.logMemory(divisionId, 'battle_plan_generated', `AI generated ${plans.length} ${period} goals for ${division.name}`);
      res.json({ success: true, plans });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  });

  app.post('/api/ai/chat', async (req, res) => {
    const { messages, division_id: divisionId, conversation_id: conversationId } = req.body;
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const userQuery = [...messages].reverse().find((message) => message.role === 'user')?.content || '';
    const memories = await memory.searchMemory(userQuery, settings, divisionId, 8);
    const snapshot = db.prepare('SELECT d.name, COUNT(i.id) as count FROM divisions d LEFT JOIN items i ON d.id = i.division_id GROUP BY d.id HAVING count > 0').all();
    const wins = db.prepare('SELECT title FROM battle_plans WHERE done = 1 ORDER BY id DESC LIMIT 5').all();

    const isFallback = memories.length > 0 && memories[0].fallback;
    let memoryBlock = '\n\n[SENTIENT AGENCY MEMORY]\n';
    memoryBlock += isFallback ? 'Mode: recent (embedding offline)\n' : `Mode: semantic RAG — top ${memories.length} relevant\n`;
    memories.forEach((entry, index) => {
      const score = entry.score > 0 ? ` (${(entry.score * 100).toFixed(0)}%)` : '';
      memoryBlock += `${index + 1}. [${entry.type}]${score} ${entry.content}\n`;
    });
    if (snapshot.length) {
      memoryBlock += '\nPipeline: ';
      snapshot.forEach((entry) => { memoryBlock += `${entry.name}:${entry.count} `; });
    }
    if (wins.length) {
      memoryBlock += '\nRecent wins: ' + wins.map((win) => win.title).join(' | ');
    }
    memoryBlock += '\nUse the memory above to give specific, personalized advice.';

    // Inject skills context (Option B semantic retrieval)
    const skillsCtx = await skills.buildSkillsContext(userQuery, settings, divisionId);

    if (messages.length > 0 && messages[0].role === 'system') {
      messages[0].content += skillsCtx + memoryBlock;
    }

    try {
      const systemContext = [messages[0]?.content || '', ai.buildBusinessContext(), ai.buildLanguageInstruction(settings, { html: true })].filter(Boolean).join(' ');
      const { content: reply, provider, fallbackUsed } = await ai.callAIWithFallback(messages[messages.length - 1].content, settings, systemContext);
      if (conversationId) {
        const now = new Date().toISOString();
        const lastUserMessage = [...messages].reverse().find((message) => message.role === 'user')?.content || '';
        if (lastUserMessage) {
          db.prepare('INSERT INTO ai_messages (conversation_id, role, content, created_at) VALUES (?, ?, ?, ?)').run(conversationId, 'user', lastUserMessage, now);
        }
        db.prepare('INSERT INTO ai_messages (conversation_id, role, content, created_at) VALUES (?, ?, ?, ?)').run(conversationId, 'assistant', reply, now);
        const inferredTitle = lastUserMessage.substring(0, 72) || 'New conversation';
        db.prepare(`UPDATE ai_conversations
          SET title = CASE WHEN title IS NULL OR title = '' OR title = 'New conversation' THEN ? ELSE title END,
              updated_at = ?
          WHERE id = ?`).run(inferredTitle, now, conversationId);
      }
      res.json({ reply, provider, provider_fallback_used: fallbackUsed, rag_memories_used: memories.length, rag_fallback: isFallback });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  });

  app.post('/api/items/:id/ai-tasks', async (req, res) => {
    const item = db.prepare('SELECT * FROM items WHERE id = ?').get(req.params.id);
    const division = db.prepare('SELECT * FROM divisions WHERE id = ?').get(item.division_id);
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();

    try {
      const aiText = await ai.callAI(
        `Client: "${item.title}". Return a raw JSON array of 3 highly actionable tasks (max 10 words each).`,
        settings,
        `Expert director for "${division.name}". VFP: "${division.vfp}". ${ai.buildBusinessContext()} ${ai.buildLanguageInstruction(settings, { json: true })}`
      );
      const tasks = ai.parseJsonResponse(aiText);
      const insertTodo = db.prepare('INSERT INTO todos (item_id, division_id, text, done) VALUES (?, ?, ?, 0)');
      (Array.isArray(tasks) ? tasks : []).slice(0, 3).forEach((task) => insertTodo.run(item.id, item.division_id, String(task).substring(0, 100)));
      res.json({ success: true, tasks });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  });

  app.post('/api/i18n/translate', async (req, res) => {
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const targetLanguage = ['en', 'it', 'ro'].includes(req.body?.target_language) ? req.body.target_language : (settings.app_language || 'en');
    const texts = Array.isArray(req.body?.texts) ? req.body.texts.map((text) => String(text || '').trim()).filter(Boolean) : [];
    if (!texts.length || targetLanguage === 'en') {
      return res.json({ translations: texts.map((text) => ({ source: text, translated: text })) });
    }

    const cached = db.prepare('SELECT source_text, translated_text FROM translation_cache WHERE target_language = ? AND source_text IN (' + texts.map(() => '?').join(',') + ')')
      .all(targetLanguage, ...texts);
    const cacheMap = new Map(cached.map((row) => [row.source_text, row.translated_text]));
    const missing = texts.filter((text) => !cacheMap.has(text));

    if (missing.length > 0) {
      try {
        const prompt = `Translate every string in this JSON array into ${ai.getLanguageMeta({ app_language: targetLanguage }).label}. Preserve meaning, tone, names, formatting, HTML tags, and product names. Return ONLY valid JSON array of translated strings in the same order.\n${JSON.stringify(missing)}`;
        const raw = await ai.callAI(prompt, settings, ai.buildLanguageInstruction({ app_language: targetLanguage }, { json: true }));
        const parsed = ai.parseJsonResponse(raw);
        const translations = Array.isArray(parsed) ? parsed : missing;
        const insert = db.prepare('INSERT OR REPLACE INTO translation_cache (source_text, target_language, translated_text, created_at) VALUES (?, ?, ?, ?)');
        missing.forEach((source, index) => {
          const translated = String(translations[index] || source).trim() || source;
          cacheMap.set(source, translated);
          insert.run(source, targetLanguage, translated, new Date().toISOString());
        });
      } catch (error) {
        missing.forEach((source) => cacheMap.set(source, source));
      }
    }

    res.json({
      translations: texts.map((source) => ({
        source,
        translated: cacheMap.get(source) || source
      }))
    });
  });
}

module.exports = { registerAIRoutes };
