/**
 * components/voice_engine.jsx
 *
 * Low-level helpers for VoiceChat: apiCall, matchDSL, executeDSL, formatToolResult.
 * Depends on globals from voice_schemas.jsx (ACTION_SCHEMA, TOOL_DISPATCH).
 */

let _proxymBase = '', _apiKey = '';

async function apiCall(method, path, body = null) {
  const opts = {
    method,
    headers: {
      'Authorization': `Bearer ${_apiKey}`,
      'Content-Type': 'application/json',
    },
  };
  if (body) opts.body = JSON.stringify(body);
  const r = await fetch(`${_proxymBase}${path}`, opts);
  if (!r.ok) {
    const err = await r.json().catch(() => ({ detail: r.statusText }));
    throw new Error(err.detail || `HTTP ${r.status}`);
  }
  return r.json().catch(() => ({}));
}

function matchDSL(text) {
  const t = text.trim();
  for (const rule of ACTION_SCHEMA) {
    for (const pattern of rule.patterns) {
      const m = t.match(pattern);
      if (m) {
        const captured = rule.captureGroup != null ? m[rule.captureGroup] : null;
        return { rule, captured };
      }
    }
  }
  return null;
}

async function executeDSL(rule, captured) {
  let { method, path } = rule.action;
  if (captured) path = path.replace('{0}', captured);
  const data = await apiCall(method, path);
  if (rule.then) rule.then(data);
  return typeof rule.success === 'function' ? rule.success(data) : 'Gotowe.';
}

function setVoiceCredentials(base, key) {
  _proxymBase = base;
  _apiKey = key;
}

// ── formatToolResult — map tool name → human-readable string ─

const _TOOL_FORMATTERS = {
  vscode_start: (d) => {
    if (d.url) window.open(d.url, '_blank');
    return `VS Code ${d.running ? 'uruchomiony' : 'jest już uruchomiony'}. ${d.url || ''}`;
  },
  vm_action: (d) => {
    const vms = d.vms || d || [];
    if (Array.isArray(vms)) return `Masz ${vms.length} VM-ów, ${vms.filter(v => v.state === 'running').length} aktywnych.`;
    return 'Operacja na VM zakończona.';
  },
  get_costs: (d) => `Dziś: $${(d.daily_usd || 0).toFixed(3)}. Miesiąc: $${(d.monthly_usd || 0).toFixed(2)}.`,
  get_status: (d) => `proxym ${d.status === 'ok' ? 'działa' : 'ma problem'}.`,
  get_logs: (d) => {
    const logs = d.logs || [];
    return logs.length ? `${logs.length} błędów. Ostatni: ${logs[0].message?.slice(0, 60)}.` : 'Brak błędów.';
  },
};

function formatToolResult(name, data) {
  const fn = _TOOL_FORMATTERS[name];
  return fn ? fn(data) : JSON.stringify(data).slice(0, 100);
}

// ── processVoiceInput — split into DSL / LLM / tool phases ──

async function _handleDSL(text, onStatus) {
  const dsl = matchDSL(text);
  if (!dsl) return null;
  const label = typeof dsl.rule.label === 'function' ? dsl.rule.label(dsl.captured) : (dsl.rule.label || 'Wykonuję…');
  if (onStatus) onStatus(label);
  const reply = await executeDSL(dsl.rule, dsl.captured);
  if (onStatus) onStatus('');
  return reply;
}

function _buildMessages(text, history, lang) {
  const langHint = lang === 'pl-PL' ? 'polsku' : 'angielsku';
  return [
    {
      role: 'system',
      content:
        'Jesteś asystentem zarządzającym proxym. ' +
        'ZAWSZE używaj dostępnych narzędzi zamiast opisywać jak coś zrobić. ' +
        'Gdy użytkownik pyta o status, koszty, VM-y lub VS Code — wywołaj odpowiednie narzędzie. ' +
        'Odpowiadaj maksymalnie 1–2 zdaniami po ' + langHint + '. ' +
        'Nie pisz instrukcji krok po kroku — działaj.',
    },
    ...history,
    { role: 'user', content: text },
  ];
}

async function _callLLM(messages, model, signal) {
  const res = await fetch(`${_proxymBase}/v1/chat/completions`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${_apiKey}` },
    signal,
    body: JSON.stringify({ model, messages, tools: TOOL_SCHEMA, tool_choice: 'auto', stream: false, max_tokens: 300 }),
  });
  const data = await res.json();
  return data.choices?.[0]?.message;
}

async function _handleToolCalls(toolCalls, onStatus) {
  for (const tc of toolCalls) {
    const name = tc.function.name;
    let args = {};
    try { args = JSON.parse(tc.function.arguments || '{}'); } catch (_) { /* parse error */ }
    if (!TOOL_DISPATCH[name]) continue;
    if (onStatus) onStatus(`Wywołuję: ${name}…`);
    const result = await TOOL_DISPATCH[name](args);
    if (onStatus) onStatus('');
    return formatToolResult(name, result);
  }
  return null;
}

async function processVoiceInput(text, { history, model, lang, signal, onStatus }) {
  if (!text.trim()) return null;

  const dslReply = await _handleDSL(text, onStatus);
  if (dslReply) return dslReply;

  if (onStatus) onStatus('Pytam model…');
  const messages = _buildMessages(text, history, lang);
  const msg = await _callLLM(messages, model, signal);

  if (msg?.tool_calls?.length) {
    const toolReply = await _handleToolCalls(msg.tool_calls, onStatus);
    if (toolReply) return toolReply;
  }

  if (onStatus) onStatus('');
  return msg?.content || 'Brak odpowiedzi.';
}
