function createMemoryService({ db, embeddedRuntime = null }) {
  function cosineSimilarity(a, b) {
    if (!a || !b || a.length !== b.length) return 0;
    let dot = 0;
    let normA = 0;
    let normB = 0;
    for (let i = 0; i < a.length; i++) {
      dot += a[i] * b[i];
      normA += a[i] * a[i];
      normB += b[i] * b[i];
    }
    const denominator = Math.sqrt(normA) * Math.sqrt(normB);
    return denominator === 0 ? 0 : dot / denominator;
  }

  async function embedText(text, settings) {
    const provider = settings.embedding_provider || (settings.local_runtime === 'embedded' ? 'embedded' : 'ollama');
    const model = settings.embedding_model || (provider === 'embedded' ? (settings.local_runtime_model || settings.ai_model || 'qwen2.5-3b-instruct-4bit') : 'nomic-embed-text');

    try {
      if (provider === 'embedded') {
        if (!embeddedRuntime) throw new Error('Embedded runtime is not available');
        return await embeddedRuntime.embed({
          text,
          modelSlug: model
        });
      }

      if (provider === 'ollama') {
        const res = await fetch('http://127.0.0.1:11434/api/embeddings', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ model, prompt: text })
        });
        const data = await res.json();
        if (!data.embedding) throw new Error(data.error || 'No embedding returned');
        return data.embedding;
      }

      if (provider === 'openai') {
        const res = await fetch('https://api.openai.com/v1/embeddings', {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${settings.ai_api_key}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ model, input: text })
        });
        const data = await res.json();
        if (data.error) throw new Error(data.error.message);
        return data.data[0].embedding;
      }
    } catch (error) {
      console.log(`[RAG] embedText failed (${provider}/${model}):`, error.message);
      return null;
    }

    return null;
  }

  async function searchMemory(queryText, settings, divisionId = null, topK = 8) {
    const queryVector = await embedText(queryText, settings);
    if (!queryVector) {
      return db.prepare('SELECT * FROM memory_logs ORDER BY id DESC LIMIT ?').all(topK).map((row) => ({ ...row, score: 0, fallback: true }));
    }

    const logs = db.prepare('SELECT * FROM memory_logs WHERE embedding IS NOT NULL').all();
    if (logs.length === 0) {
      return db.prepare('SELECT * FROM memory_logs ORDER BY id DESC LIMIT ?').all(topK).map((row) => ({ ...row, score: 0, fallback: true }));
    }

    const scored = logs.map((log) => ({
      ...log,
      score: cosineSimilarity(queryVector, JSON.parse(log.embedding))
    }));

    scored.sort((a, b) => {
      const boost = divisionId ? (b.division_id === divisionId ? 0.05 : 0) - (a.division_id === divisionId ? 0.05 : 0) : 0;
      return (b.score + boost) - (a.score + boost);
    });

    return scored.slice(0, topK);
  }

  function logMemory(divisionId, type, content) {
    const timestamp = new Date().toISOString();
    const result = db.prepare('INSERT INTO memory_logs (division_id, type, content, timestamp) VALUES (?, ?, ?, ?)').run(divisionId, type, content, timestamp);
    const logId = result.lastInsertRowid;
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();

    embedText(content, settings)
      .then((vector) => {
        if (vector) {
          db.prepare('UPDATE memory_logs SET embedding = ? WHERE id = ?').run(JSON.stringify(vector), logId);
        }
      })
      .catch(() => {});
  }

  async function backfillEmbeddings() {
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const pending = db.prepare('SELECT id, content FROM memory_logs WHERE embedding IS NULL').all();

    if (!pending.length) {
      return { done: 0, total: 0 };
    }

    let done = 0;
    for (const log of pending) {
      const vector = await embedText(log.content, settings);
      if (vector) {
        db.prepare('UPDATE memory_logs SET embedding = ? WHERE id = ?').run(JSON.stringify(vector), log.id);
        done++;
      }
    }

    return { done, total: pending.length };
  }

  function getMemoryStats() {
    const total = db.prepare('SELECT COUNT(*) as c FROM memory_logs').get().c;
    const embedded = db.prepare('SELECT COUNT(*) as c FROM memory_logs WHERE embedding IS NOT NULL').get().c;
    return { total, embedded, unembedded: total - embedded };
  }

  return {
    embedText,
    cosineSimilarity,
    searchMemory,
    logMemory,
    backfillEmbeddings,
    getMemoryStats
  };
}

module.exports = { createMemoryService };
