function createSkillsService({ db, memory }) {

  // Search skills by semantic similarity to a query
  // Option B with smart fallback: if < 2 results from current division, force-include core skill
  async function searchSkills(queryText, settings, divisionId = null, topK = 5) {
    const queryVec = await memory.embedText(queryText, settings);

    // FALLBACK: embedding offline → load current division's core skill + its sub-skills
    if (!queryVec) {
      if (divisionId) {
        const core = db.prepare('SELECT * FROM skills WHERE division_id = ? AND parent_id IS NULL LIMIT 1').get(divisionId);
        const subs = core ? db.prepare('SELECT * FROM skills WHERE parent_id = ?').all(core.id) : [];
        return [core, ...subs].filter(Boolean).map(s => ({ ...s, score: 0, fallback: true }));
      }
      return db.prepare('SELECT * FROM skills WHERE parent_id IS NULL ORDER BY use_count DESC LIMIT ?').all(topK)
        .map(s => ({ ...s, score: 0, fallback: true }));
    }

    // Load all skills that have been embedded
    const allSkills = db.prepare('SELECT * FROM skills WHERE embedding IS NOT NULL').all();

    if (allSkills.length === 0) {
      // Nothing embedded — recency/usage fallback
      if (divisionId) {
        const core = db.prepare('SELECT * FROM skills WHERE division_id = ? AND parent_id IS NULL LIMIT 1').get(divisionId);
        const subs = core ? db.prepare('SELECT * FROM skills WHERE parent_id = ?').all(core.id) : [];
        return [core, ...subs].filter(Boolean).map(s => ({ ...s, score: 0, fallback: true }));
      }
      return [];
    }

    // Score every skill by cosine similarity
    const scored = allSkills.map(skill => {
      const vec = JSON.parse(skill.embedding);
      return { ...skill, score: memory.cosineSimilarity(queryVec, vec) };
    });

    scored.sort((a, b) => b.score - a.score);

    let results = scored.slice(0, topK);

    // Smart fallback rule: if fewer than 2 results are from the current division,
    // force-include the division's core skill
    if (divisionId) {
      const fromDiv = results.filter(s => s.division_id === divisionId).length;
      if (fromDiv < 2) {
        const core = db.prepare('SELECT * FROM skills WHERE division_id = ? AND parent_id IS NULL LIMIT 1').get(divisionId);
        if (core && !results.find(r => r.id === core.id)) {
          const coreVec = core.embedding ? JSON.parse(core.embedding) : null;
          const coreScore = coreVec ? memory.cosineSimilarity(queryVec, coreVec) : 0;
          results.push({ ...core, score: coreScore, forced: true });
        }
      }
    }

    return results;
  }

  // Build the skills context string for injection into AI prompts
  async function buildSkillsContext(queryText, settings, divisionId = null) {
    const results = await searchSkills(queryText, settings, divisionId);

    if (results.length === 0) return '';

    const isFallback = results[0]?.fallback;

    // Increment use_count for all retrieved skills
    const updateUse = db.prepare('UPDATE skills SET use_count = use_count + 1, last_used = ? WHERE id = ?');
    const now = new Date().toISOString();
    results.forEach(s => updateUse.run(now, s.id));

    let ctx = '\n\n[LOADED SKILLS]\n';
    ctx += isFallback
      ? '(Embedding offline — loaded division default skills)\n'
      : `(${results.length} skills loaded by semantic relevance)\n`;

    results.forEach((s, i) => {
      const scoreStr = s.score > 0 ? ` [${(s.score * 100).toFixed(0)}% match]` : '';
      const forcedStr = s.forced ? ' [division safety-net]' : '';
      ctx += `\n── Skill: ${s.name}${scoreStr}${forcedStr} ──\n${s.content}\n`;
    });

    return ctx;
  }

  // Embed all skills that don't have vectors yet
  async function backfillSkillEmbeddings() {
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const pending = db.prepare('SELECT id, name, content, tags FROM skills WHERE embedding IS NULL').all();
    if (!pending.length) return { done: 0, total: 0 };

    let done = 0;
    for (const skill of pending) {
      // Embed name + content + tags together for best semantic match
      const text = `${skill.name}. ${skill.content}. Tags: ${skill.tags}`;
      const vec = await memory.embedText(text, settings);
      if (vec) {
        db.prepare('UPDATE skills SET embedding = ? WHERE id = ?').run(JSON.stringify(vec), skill.id);
        done++;
      }
    }
    return { done, total: pending.length };
  }

  return {
    searchSkills,
    buildSkillsContext,
    backfillSkillEmbeddings
  };
}

module.exports = { createSkillsService };
