// lib/ai.js
// =========================================================================
// Client-side helper that routes AI calls through the worker's unified
// provider router instead of going direct to Google/Anthropic/etc.
//
// Use this in NEW code. Existing gemini.js calls keep working unchanged.
//
// Benefits:
//   - User can swap providers (Gemini → Groq → Claude) without code change
//   - Trial mode "just works" — no key needed for first 50 calls
//   - All calls are logged centrally for analytics
//   - Auto-fallback on provider failure
// =========================================================================

import { SYNC } from './config.js';
import { getSession } from './auth.js';
import { obLog } from './logger.js';

const ENDPOINT = `${SYNC.endpoint}/api/v1/ai/generate`;
const TRIAL_ENDPOINT = `${SYNC.endpoint}/api/v1/ai/trial`;

/**
 * Generate text/JSON via the user's configured AI provider (or trial).
 * @param {string} prompt
 * @param {object} opts
 * @param {'text'|'vision'} [opts.kind='text']
 * @param {string} [opts.imageDataUrl] — for vision
 * @param {string} [opts.feature] — analytics tag, e.g. 'li_comment', 'profile_audit'
 * @param {string} [opts.model] — override provider's default model
 * @param {string} [opts.providerId] — pin to a specific instance (else default)
 * @param {number} [opts.temperature=0.5]
 * @param {number} [opts.maxTokens=1024]
 * @param {'json'|undefined} [opts.responseFormat]
 * @returns {Promise<{ok, text, provider, model, isTrial, latencyMs, error?, trialExhausted?}>}
 */
export async function aiGenerate(prompt, opts = {}) {
  try {
    const { token } = await getSession();
    if (!token) return { ok: false, error: 'not signed in' };
    const res = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-Session-Token': token },
      body: JSON.stringify({
        prompt,
        kind: opts.kind || 'text',
        imageDataUrl: opts.imageDataUrl,
        feature: opts.feature || 'extension',
        model: opts.model,
        providerId: opts.providerId,
        temperature: typeof opts.temperature === 'number' ? opts.temperature : 0.5,
        maxTokens: opts.maxTokens || 1024,
        responseFormat: opts.responseFormat,
      }),
    });
    if (res.status === 402) {
      const j = await res.json().catch(() => ({}));
      obLog.warn('ai_trial_exhausted', { context: { feature: opts.feature } });
      return { ok: false, trialExhausted: true, error: 'trial_exhausted', message: j.message, trial: j.trial };
    }
    if (!res.ok) {
      const text = await res.text();
      obLog.error('ai_generate_http_error', { context: { status: res.status, feature: opts.feature, body: text.slice(0, 200) } });
      return { ok: false, error: `HTTP ${res.status}` };
    }
    const json = await res.json();
    return json;
  } catch (e) {
    obLog.error('ai_generate_exception', { context: { msg: String(e?.message || e), feature: opts.feature } });
    return { ok: false, error: String(e?.message || e) };
  }
}

/**
 * Convenience wrapper that expects a JSON response and parses it.
 * @returns {Promise<{ok, data?, raw?, error?}>}
 */
export async function aiGenerateJson(prompt, opts = {}) {
  const r = await aiGenerate(prompt, { ...opts, responseFormat: 'json' });
  if (!r.ok) return r;
  try {
    let txt = (r.text || '').trim().replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/i, '');
    const firstBrace = txt.indexOf('{');
    if (firstBrace > 0) txt = txt.slice(firstBrace);
    return { ok: true, data: JSON.parse(txt), raw: r.text, provider: r.provider, model: r.model };
  } catch (e) {
    return { ok: false, error: 'non-JSON response', raw: r.text };
  }
}

/**
 * Get current trial status (for UI badges).
 */
export async function getAiTrialStatus() {
  try {
    const { token } = await getSession();
    if (!token) return null;
    const r = await fetch(TRIAL_ENDPOINT, { headers: { 'X-Session-Token': token } });
    if (!r.ok) return null;
    return await r.json();
  } catch { return null; }
}
