function createAIService({ db, embeddedRuntime = null }) {
  const DEFAULT_MODELS = {
    openrouter: 'google/gemini-2.5-flash',
    ollama: 'llama3.1:8b',
    embedded: 'qwen2.5-3b-instruct-4bit'
  };
  const LANGUAGE_META = {
    en: { label: 'English', code: 'en' },
    it: { label: 'Italian', code: 'it' },
    ro: { label: 'Romanian', code: 'ro' }
  };
  const CURRENCY_LABELS = {
    EUR: 'EUR',
    GBP: 'GBP',
    USD: 'USD',
    CAD: 'CAD'
  };

  function buildServicesContext() {
    const services = db.prepare('SELECT * FROM services').all();
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const company = settings.company_name || 'Agency';
    const owner = settings.owner_name || 'Director';
    if (services.length === 0) return '';

    let context = `\n\n[${company.toUpperCase()} — SERVICES CATALOG]\nOwner: ${owner}\n`;
    services.forEach((service) => {
      const currency = CURRENCY_LABELS[service.currency] || 'EUR';
      context += `\nService: ${service.name}\n  Price: ${currency} ${service.price}\n  Description: ${service.description}\n  Deliverables: ${service.deliverables}\n  Ideal Client: ${service.ideal_client}\n`;
    });
    return context;
  }

  function buildBusinessContext() {
    const services = db.prepare('SELECT * FROM services').all();
    const settings = db.prepare('SELECT * FROM settings WHERE id = 1').get();
    const company = settings.company_name || 'the business';
    const owner = settings.owner_name || 'the owner';
    const city = settings.city || 'the current market';
    const serviceNames = services.map((service) => service.name).filter(Boolean);
    const serviceLine = serviceNames.length > 0
      ? `Core offers: ${serviceNames.join(', ')}.`
      : 'Core offers should be inferred from the configured services and division VFPs.';

    return `Business: ${company}. Owner: ${owner}. Market: ${city}. ${serviceLine} Personalize all recommendations to this specific business instead of assuming a generic agency profile.`;
  }

  function buildOperatingContext() {
    return `

[ORG BOARD OPERATING MODEL]
This platform runs as a connected division workflow, not as isolated tabs.
- ED / Division 8: command, executive orders, battle plans, stats, bottlenecks, finance visibility, delivery visibility.
- Division 7: policy, admin control, compliance, org structure, execution control.
- Division 1: communication lines, routing, templates, dispatches, follow-ups.
- Division 2: dissemination, campaign briefs, outreach direction, handoffs.
- Division 3: treasury, invoice maker, approvals, collections, reserves.
- Division 4: production, build queue, client assets, staging, blockers, delivery readiness, scope control.
- Division 5: QA, zero-bug protocol, release gates, readiness verification.
- Division 6: client success, reviews, support, referrals, continuity.

[WORKFLOW CONNECTIONS]
- Executive orders create linked todos in the target division.
- Live client work moves through the board and downstream divisions should act on the current state.
- Division 3 invoices propagate into treasury cases and treasury summaries.
- Division 4 readiness feeds Division 5 QA.
- Division 5 release gates feed Division 6 client-success readiness.
- Division 6 sends feedback, review, referral, and support signals back into executive visibility.
- General agents are visible in every division and can run cross-division control work.
- Division agents belong to one division, can read memory, create tasks, create battle plans, and post updates into that division's AI Advisor thread.
- Scheduled agents run from their saved timetable and manual agents can be run on demand from the Agents tab.

[HOW TO RESPOND]
- Ground suggestions in the current operating model above.
- Prefer actions that fit the existing division tabs, persistent records, and handoff flow.
- Do not invent a workflow that bypasses the actual board unless the user explicitly asks for a new design.
- If a recommendation depends on another division, say which division should receive the handoff or order next.
`;
  }

  function getLanguageMeta(settings = {}) {
    const language = 'en';
    return { language, ...LANGUAGE_META[language] };
  }

  function buildLanguageInstruction(settings = {}, options = {}) {
    const meta = getLanguageMeta(settings);
    const rules = [`IMPORTANT: Use ${meta.label} for the entire response.`];
    if (options.json) rules.push(`If you return JSON, every user-facing string inside the JSON must be in ${meta.label}.`);
    if (options.plainText) rules.push(`Return plain text only in ${meta.label}.`);
    if (options.html) rules.push(`Any visible text inside the HTML must be in ${meta.label}.`);
    rules.push('Do not switch languages unless the user explicitly asks for a different one.');
    return rules.join(' ');
  }

  function buildMessages(prompt, system = '') {
    const fullSystem = system + buildServicesContext() + '\n' + buildBusinessContext() + '\n' + buildOperatingContext();
    return fullSystem
      ? [{ role: 'system', content: fullSystem }, { role: 'user', content: prompt }]
      : [{ role: 'user', content: prompt }];
  }

  function normalizeSettings(settings = {}) {
    const provider = ['embedded', 'ollama', 'openrouter'].includes(settings.ai_provider)
      ? settings.ai_provider
      : 'openrouter';
    return {
      ...settings,
      ai_provider: provider,
      ai_model: settings.ai_model || DEFAULT_MODELS[provider],
      ai_api_key: settings.ai_api_key || ''
    };
  }

  async function callProvider(provider, prompt, settings, system = '') {
    const normalized = normalizeSettings({ ...settings, ai_provider: provider });
    const messages = buildMessages(prompt, system);

    if (provider === 'openrouter') {
      if (!normalized.ai_api_key) throw new Error('OpenRouter API key is missing');
      const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${normalized.ai_api_key}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ model: normalized.ai_model, messages })
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data?.error?.message || `OpenRouter request failed (${res.status})`);
      if (data.error) throw new Error(data.error.message);
      return data.choices?.[0]?.message?.content || '';
    }

    if (provider === 'ollama') {
      const res = await fetch('http://127.0.0.1:11434/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ model: normalized.ai_model, messages, stream: false })
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data?.error || `Ollama request failed (${res.status})`);
      if (data.error) throw new Error(data.error);
      return data?.message?.content || '';
    }

    if (provider === 'embedded') {
      if (!embeddedRuntime) throw new Error('Embedded runtime is not available');
      return embeddedRuntime.chat({
        messages,
        modelSlug: normalized.local_runtime_model || normalized.ai_model || DEFAULT_MODELS.embedded,
        maxTokens: 700,
        temperature: 0.2
      });
    }

    throw new Error('No AI provider configured');
  }

  async function callAI(prompt, settings, system = '') {
    const normalized = normalizeSettings(settings);
    return callProvider(normalized.ai_provider, prompt, normalized, system);
  }

  async function callAIWithFallback(prompt, settings, system = '') {
    const normalized = normalizeSettings(settings);
    const primaryProvider = normalized.ai_provider;
    const attempts = [];
    const fallbackProviders = [];

    if (primaryProvider === 'embedded') {
      fallbackProviders.push('ollama', 'openrouter');
    } else if (primaryProvider === 'ollama') {
      fallbackProviders.push(normalized.local_runtime === 'embedded' ? 'embedded' : null, 'openrouter');
    } else {
      fallbackProviders.push(normalized.local_runtime === 'embedded' ? 'embedded' : 'ollama');
    }

    const primarySettings = {
      ...normalized,
      ai_provider: primaryProvider,
      ai_model: normalized.ai_model || DEFAULT_MODELS[primaryProvider]
    };

    try {
      const content = await callProvider(primaryProvider, prompt, primarySettings, system);
      return { content, provider: primaryProvider, fallbackUsed: false, attempts };
    } catch (error) {
      attempts.push({ provider: primaryProvider, error: error.message });
    }

    for (const provider of fallbackProviders.filter(Boolean)) {
      if (provider === primaryProvider) continue;
      const fallbackSettings = {
        ...normalized,
        ai_provider: provider,
        ai_model: provider === 'openrouter'
          ? DEFAULT_MODELS.openrouter
          : provider === 'embedded'
            ? (normalized.local_runtime_model || DEFAULT_MODELS.embedded)
            : DEFAULT_MODELS.ollama
      };

      try {
        const content = await callProvider(provider, prompt, fallbackSettings, system);
        return { content, provider, fallbackUsed: true, attempts };
      } catch (error) {
        attempts.push({ provider, error: error.message });
      }
    }

    const attemptSummary = attempts.map((attempt) => `${attempt.provider}: ${attempt.error}`).join(' | ');
    throw new Error(`All AI providers failed. ${attemptSummary}`);
  }

  // Extracts a string from an object when the AI returns [{goal:"..."}, ...] instead of ["...", ...]
  function normaliseItem(item) {
    if (typeof item === 'string') return item.trim();
    if (typeof item === 'object' && item !== null) {
      const val = item.goal || item.title || item.text || item.task || item.content
        || item.plan || item.action || item.objective || item.item
        || Object.values(item)[0];
      return String(val || '').trim();
    }
    return String(item).trim();
  }

  function parseJsonResponse(text) {
    if (!text) return [];
    const clean = text.replace(/```json/gi, '').replace(/```/g, '').trim();

    let parsed = null;

    try { parsed = JSON.parse(clean); } catch (e) {}

    if (!parsed) {
      const arrayMatch = clean.match(/\[[\s\S]*?\]/);
      if (arrayMatch) { try { parsed = JSON.parse(arrayMatch[0]); } catch (e) {} }
    }

    if (!parsed) {
      const objMatch = clean.match(/\{[\s\S]*\}/);
      if (objMatch) { try { parsed = JSON.parse(objMatch[0]); } catch (e) {} }
    }

    if (!parsed) {
      // Last resort: newline split
      return clean
        .split('\n')
        .map((line) => line.replace(/^[-\d.*"[\]{}]+/, '').replace(/[",[\]{}]/g, '').trim())
        .filter((line) => line.length > 5)
        .slice(0, 5);
    }

    // If it's an array, normalise every element to a string
    if (Array.isArray(parsed)) {
      return parsed.map(normaliseItem).filter((s) => s.length > 3);
    }

    // If it's a plain object (e.g. VFP map {"2":"...", "8":"..."}) return as-is
    return parsed;
  }

  return {
    buildServicesContext,
    buildBusinessContext,
    buildOperatingContext,
    getLanguageMeta,
    buildLanguageInstruction,
    callAI,
    callAIWithFallback,
    parseJsonResponse
  };
}

module.exports = { createAIService };
