/**
 * components/voice.jsx
 *
 * Action-first voice panel:
 * - If ACTION_SCHEMA matches: call REST endpoint directly (no LLM)
 * - Otherwise: call LLM with tool_calling and dispatch tool calls to REST
 */

// ACTION SCHEMA
const ACTION_SCHEMA = [
  {
    patterns: [/uruchom\s+vscode/i, /start\s+vscode/i, /włącz\s+vscode/i, /otwórz\s+vscode/i, /open\s+vscode/i],
    action: { method: 'POST', path: '/api/v1/vscode/start' },
    confirm: false,
    label: 'Uruchamiam VS Code Web…',
    success: (d) => `VS Code uruchomiony. ${d.url ? 'Otwieramy: ' + d.url : 'Dostępny na porcie 8080.'}`,
    then: (d) => { if (d.url) window.open(d.url, '_blank'); },
  },
  {
    patterns: [/zatrzymaj\s+vscode/i, /stop\s+vscode/i, /wyłącz\s+vscode/i],
    action: { method: 'POST', path: '/api/v1/vscode/stop' },
    label: 'Zatrzymuję VS Code Web…',
    success: () => 'VS Code zatrzymany.',
  },
  {
    patterns: [/status\s+vscode/i, /czy\s+vscode\s+działa/i],
    action: { method: 'GET', path: '/api/v1/vscode/status' },
    label: 'Sprawdzam VS Code…',
    success: (d) => `VS Code: ${d.running ? `działa na porcie ${d.port}` : 'zatrzymany'}.`,
  },
  {
    patterns: [/pokaż\s+(vm|maszyn)/i, /lista\s+(vm|maszyn)/i, /jakie\s+maszyny/i, /show\s+vms?/i, /list\s+vms?/i],
    action: { method: 'GET', path: '/api/v1/vms' },
    label: 'Pobieram listę VM-ów…',
    success: (d) => {
      const vms = d.vms || d || [];
      if (!vms.length) return 'Brak VM-ów. Utwórz przez: proxym vm create.';
      const running = vms.filter(v => v.state === 'running');
      return `Masz ${vms.length} VM-ów, ${running.length} uruchomionych: ${running.map(v => v.vm_id || v.id).join(', ') || 'żaden'}.`;
    },
  },
  {
    patterns: [/uruchom\s+vm\s+([a-z0-9_-]+)/i, /start\s+vm\s+([a-z0-9_-]+)/i, /włącz\s+([a-z0-9_-]+)/i],
    captureGroup: 1,
    action: { method: 'POST', path: '/api/v1/vms/{0}/start' },
    label: (id) => `Uruchamiam VM ${id}…`,
    success: (d) => `VM ${d.vm_id || ''} uruchomiony.`,
  },
  {
    patterns: [/zatrzymaj\s+vm\s+([a-z0-9_-]+)/i, /stop\s+vm\s+([a-z0-9_-]+)/i, /wyłącz\s+([a-z0-9_-]+)/i],
    captureGroup: 1,
    action: { method: 'POST', path: '/api/v1/vms/{0}/stop' },
    label: (id) => `Zatrzymuję VM ${id}…`,
    success: (d) => `VM ${d.vm_id || ''} zatrzymany.`,
  },
  {
    patterns: [/snapshot\s+([a-z0-9_-]+)/i, /zapisz\s+stan\s+([a-z0-9_-]+)/i],
    captureGroup: 1,
    action: { method: 'POST', path: '/api/v1/vms/{0}/snapshot' },
    label: (id) => `Tworzę snapshot VM ${id}…`,
    success: () => 'Snapshot utworzony.',
  },
  {
    patterns: [/ile\s+wydałem/i, /koszty/i, /budżet/i, /costs?/i, /how\s+much/i],
    action: { method: 'GET', path: '/api/v1/dashboard/costs/summary' },
    label: 'Sprawdzam koszty…',
    success: (d) => {
      const daily = (d.daily_usd || 0).toFixed(3);
      const monthly = (d.monthly_usd || 0).toFixed(2);
      return `Dziś: $${daily}. Miesiąc: $${monthly}.`;
    },
  },
  {
    patterns: [/^status$/i, /jak\s+idzie/i, /co\s+działa/i, /pokaż\s+stan/i, /system\s+status/i],
    action: { method: 'GET', path: '/health' },
    label: 'Sprawdzam status…',
    success: (d) => `proxym ${d.status === 'ok' ? 'działa poprawnie' : 'ma problemy'}. Wersja: ${d.version || '?'}.`,
  },
  {
    patterns: [/logi?/i, /błędy?/i, /co\s+się\s+stało/i, /logs?/i, /errors?/i],
    action: { method: 'GET', path: '/api/v1/logs?lines=5&level=error' },
    label: 'Pobieramy logi…',
    success: (d) => {
      const logs = d.logs || [];
      if (!logs.length) return 'Brak błędów.';
      return `${logs.length} błędów. Ostatni: ${logs[0].message?.slice(0, 80) || '?'}`;
    },
  },
  {
    patterns: [/jakie\s+modele/i, /dostępne\s+modele/i, /list\s+models/i, /show\s+models/i],
    action: { method: 'GET', path: '/v1/models' },
    label: 'Pobieramy modele…',
    success: (d) => {
      const ids = (d.data || []).map(m => m.id).join(', ');
      return `Dostępne modele: ${ids}.`;
    },
  },
  {
    patterns: [/test\s+routing/i, /sprawdź\s+routing/i, /czy\s+działa\s+ollama/i, /test\s+ollama/i],
    action: { method: 'GET', path: '/api/v1/routing/test' },
    label: 'Testuję routing…',
    success: (d) => {
      const routes = d.routing || {};
      const ok = Object.entries(routes).filter(([, v]) => v.status === 'ok').map(([k]) => k);
      const fail = Object.entries(routes).filter(([, v]) => v.status !== 'ok').map(([k]) => k);
      return `Działa: ${ok.join(', ') || 'żaden'}. Niedostępne: ${fail.join(', ') || 'żaden'}.`;
    },
  },
  {
    patterns: [/pokaż\s+konta/i, /lista\s+kont/i, /jakie\s+konta/i, /list\s+accounts/i],
    action: { method: 'GET', path: '/api/v1/accounts' },
    label: 'Pobieramy konta…',
    success: (d) => {
      const accounts = d.accounts || d || [];
      return `Masz ${accounts.length} kont API: ${accounts.map(a => a.label || a.id).join(', ')}.`;
    },
  },
];

// TOOL SCHEMA (LLM tool_calling)
const TOOL_SCHEMA = [
  {
    type: 'function',
    function: {
      name: 'vscode_start',
      description: 'Uruchom usługę VS Code Web',
      parameters: { type: 'object', properties: {}, required: [] },
    },
  },
  {
    type: 'function',
    function: {
      name: 'vm_action',
      description: 'Zarządzaj maszyną wirtualną (start/stop/pause/snapshot)',
      parameters: {
        type: 'object',
        properties: {
          vm_id: { type: 'string', description: 'ID maszyny wirtualnej' },
          action: { type: 'string', enum: ['start', 'stop', 'pause', 'snapshot', 'list'] },
        },
        required: ['action'],
      },
    },
  },
  {
    type: 'function',
    function: {
      name: 'get_costs',
      description: 'Pobierz podsumowanie kosztów API',
      parameters: { type: 'object', properties: {}, required: [] },
    },
  },
  {
    type: 'function',
    function: {
      name: 'get_status',
      description: 'Sprawdź status systemu proxym',
      parameters: { type: 'object', properties: {}, required: [] },
    },
  },
  {
    type: 'function',
    function: {
      name: 'get_logs',
      description: 'Pobierz ostatnie logi błędów',
      parameters: {
        type: 'object',
        properties: {
          level: { type: 'string', enum: ['all', 'error', 'warning', 'info'], default: 'error' },
          lines: { type: 'integer', default: 10 },
        },
        required: [],
      },
    },
  },
];

const TOOL_DISPATCH = {
  vscode_start: () => apiCall('POST', '/api/v1/vscode/start'),
  vm_action: ({ action, vm_id }) => {
    if (action === 'list') return apiCall('GET', '/api/v1/vms');
    if (!vm_id) return apiCall('GET', '/api/v1/vms');
    return apiCall('POST', `/api/v1/vms/${vm_id}/${action}`);
  },
  get_costs: () => apiCall('GET', '/api/v1/dashboard/costs/summary'),
  get_status: () => apiCall('GET', '/health'),
  get_logs: ({ level = 'error', lines = 10 } = {}) => apiCall('GET', `/api/v1/logs?lines=${lines}&level=${level}`),
};

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 VoiceChat({ proxymBase, apiKey, model: initialModel = 'cheap' }) {
  _proxymBase = proxymBase;
  _apiKey = apiKey;

  const h = React.createElement;
  const [state, setState] = React.useState('idle');
  const [transcript, setTranscript] = React.useState('');
  const [response, setResponse] = React.useState('');
  const [statusMsg, setStatusMsg] = React.useState('');
  const [history, setHistory] = React.useState([]);
  const [error, setError] = React.useState('');
  const [ttsEnabled, setTtsEnabled] = React.useState(true);
  const [model, setModel] = React.useState(initialModel);
  const [lang, setLang] = React.useState('pl-PL');

  const recRef = React.useRef(null);
  const abortRef = React.useRef(null);

  const hasSR = !!(window.SpeechRecognition || window.webkitSpeechRecognition);
  const hasTTS = !!(window.speechSynthesis);

  function startListening() {
    if (state !== 'idle') return;
    setError('');
    setTranscript('');
    setResponse('');
    setStatusMsg('');
    if (window.speechSynthesis?.speaking) window.speechSynthesis.cancel();

    if (!hasSR) {
      setError('Brak Web Speech API. Użyj Chrome lub Edge.');
      return;
    }
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    const r = new SR();
    r.lang = lang;
    r.continuous = false;
    r.interimResults = true;

    r.onstart = () => setState('listening');
    r.onerror = (e) => { setError(`Mikrofon: ${e.error}`); setState('idle'); };
    r.onend = () => { if (state === 'listening') setState('idle'); };
    r.onresult = (e) => {
      const text = Array.from(e.results).map(r => r[0].transcript).join('');
      setTranscript(text);
      if (e.results[e.results.length - 1].isFinal) {
        r.stop();
        processInput(text);
      }
    };
    recRef.current = r;
    r.start();
  }

  function stopListening() {
    recRef.current?.abort();
    setState('idle');
  }

  function stopSpeaking() {
    window.speechSynthesis?.cancel();
    setState('idle');
  }

  function stopAll() {
    stopListening();
    stopSpeaking();
    abortRef.current?.abort();
    setState('idle');
  }

  async function sendMessage(text) {
    return processInput(text);
  }

  async function processInput(text) {
    if (!text.trim()) return;
    setState('thinking');

    const dsl = matchDSL(text);
    if (dsl) {
      const label = typeof dsl.rule.label === 'function' ? dsl.rule.label(dsl.captured) : (dsl.rule.label || 'Wykonuję…');
      setStatusMsg(label);
      try {
        const reply = await executeDSL(dsl.rule, dsl.captured);
        setResponse(reply);
        setStatusMsg('');
        finish(reply, text);
      } catch (e) {
        setError(`Błąd akcji: ${e.message}`);
        setState('idle');
      }
      return;
    }

    setStatusMsg('Pytam model…');
    const messages = [
      {
        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 ' + (lang === 'pl-PL' ? 'polsku' : 'angielsku') + '. ' +
          'Nie pisz instrukcji krok po kroku — działaj.',
      },
      ...history,
      { role: 'user', content: text },
    ];

    const ctrl = new AbortController();
    abortRef.current = ctrl;

    try {
      const res = await fetch(`${proxymBase}/v1/chat/completions`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
        signal: ctrl.signal,
        body: JSON.stringify({
          model,
          messages,
          tools: TOOL_SCHEMA,
          tool_choice: 'auto',
          stream: false,
          max_tokens: 300,
        }),
      });

      const data = await res.json();
      const msg = data.choices?.[0]?.message;

      if (msg?.tool_calls?.length) {
        for (const tc of msg.tool_calls) {
          const name = tc.function.name;
          let args = {};
          try { args = JSON.parse(tc.function.arguments || '{}'); } catch {}

          setStatusMsg(`Wywołuję: ${name}…`);
          if (TOOL_DISPATCH[name]) {
            const toolResult = await TOOL_DISPATCH[name](args);
            const toolReply = formatToolResult(name, toolResult);
            setResponse(toolReply);
            setStatusMsg('');
            finish(toolReply, text);
            return;
          }
        }
      }

      const textReply = msg?.content || 'Brak odpowiedzi.';
      setResponse(textReply);
      setStatusMsg('');
      finish(textReply, text);
    } catch (e) {
      if (e.name !== 'AbortError') {
        setError(`LLM: ${e.message}`);
        setState('idle');
      }
    }
  }

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

  function finish(text, userText) {
    setHistory(h => [
      ...h.slice(-10),
      { role: 'user', content: userText },
      { role: 'assistant', content: text },
    ]);
    setState(ttsEnabled && hasTTS ? 'speaking' : 'idle');
    if (ttsEnabled && hasTTS && text) {
      const u = new SpeechSynthesisUtterance(text);
      u.lang = lang;
      u.rate = 1.05;
      u.onend = () => setState('idle');
      u.onerror = () => setState('idle');
      window.speechSynthesis.speak(u);
    }
  }

  React.useEffect(() => {
    const onKey = (e) => {
      if (e.code === 'Space' && e.target === document.body) {
        e.preventDefault();
        state === 'idle' ? startListening() : stopAll();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [state]);

  const STATE_CONFIG = {
    idle: { label: '🎤 Naciśnij aby mówić', color: '#444', icon: '🎤' },
    listening: { label: '🔴 Słucham…', color: '#e74c3c', icon: '⏹' },
    thinking: { label: statusMsg || '⏳ Działam…', color: '#f39c12', icon: '⏳' },
    speaking: { label: '🔊 Mówię…', color: '#27ae60', icon: '🔊' },
  };
  const sc = STATE_CONFIG[state];

  const styles = {
    container: { padding: '12px', fontFamily: '"JetBrains Mono", monospace', maxWidth: '600px' },
    header: { display: 'flex', alignItems: 'center', gap: '6px', marginBottom: '10px', flexWrap: 'wrap' },
    title: { fontWeight: 'bold', fontSize: '13px', color: '#111' },
    select: { fontSize: '11px', padding: '2px 4px', background: '#fff', color: '#111', border: '1px solid #ddd', borderRadius: '3px' },
    toggleLabel: { fontSize: '11px', display: 'flex', gap: '4px', alignItems: 'center', color: '#555', cursor: 'pointer' },
    clearBtn: { fontSize: '11px', padding: '2px 8px', background: '#fff', color: '#555', border: '1px solid #ddd', borderRadius: '3px', cursor: 'pointer', marginLeft: 'auto' },
    micBtn: { display: 'block', margin: '0 auto 8px', width: '72px', height: '72px', borderRadius: '50%', border: 'none', cursor: 'pointer', fontSize: '28px', color: '#fff', transition: 'background 0.3s, transform 0.1s' },
    stateLabel: { textAlign: 'center', fontSize: '12px', marginBottom: '4px' },
    hint: { textAlign: 'center', fontSize: '10px', color: '#666', marginBottom: '10px' },
    bubble: (role) => ({
      background: role === 'user' ? '#eef2ff' : '#f3f4f6',
      border: `1px solid ${role === 'user' ? '#c7d2fe' : '#e5e7eb'}`,
      borderRadius: '10px',
      padding: '10px 12px',
      fontSize: '13px',
      marginBottom: '6px',
      color: '#111827',
      lineHeight: '1.5',
    }),
    roleLabel: (role) => ({ color: role === 'user' ? '#4338ca' : '#065f46', fontWeight: 'bold' }),
    error: { background: '#fee2e2', border: '1px solid #fecaca', borderRadius: '10px', padding: '8px 10px', color: '#b91c1c', fontSize: '12px', marginBottom: '6px' },
    chipBtn: { fontSize: '10px', padding: '3px 8px', background: '#fff', color: '#555', border: '1px solid #ddd', borderRadius: '12px', cursor: 'pointer' },
    historyBox: { marginTop: '8px', maxHeight: '120px', overflowY: 'auto', background: '#fff', border: '1px solid #eee', borderRadius: '10px', padding: '6px 8px' },
  };

  return h('div', { style: styles.container },
    h('div', { style: styles.header },
      h('span', { style: styles.title }, '🎙 Głos AI'),
      h('span', { style: { fontSize: '10px', color: '#555', marginRight: 'auto' } },
        'Web Speech API • STT ' + (hasSR ? '✓' : '✗') + ' • TTS ' + (hasTTS ? '✓' : '✗'),
      ),
      h('select', { value: model, onChange: e => setModel(e.target.value), style: styles.select },
        [['cheap','cheap (Haiku)'],['balanced','balanced (Sonnet)'],['free','free (Gemini)'],['local','local (Ollama)'],['premium','premium (Opus)']]
          .map(([v,l]) => h('option', { key: v, value: v }, l))
      ),
      h('select', { value: lang, onChange: e => setLang(e.target.value), style: styles.select },
        [['pl-PL','Polski'],['en-US','English'],['de-DE','Deutsch']]
          .map(([v,l]) => h('option', { key: v, value: v }, l))
      ),
      h('label', { style: styles.toggleLabel },
        h('input', { type: 'checkbox', checked: ttsEnabled, onChange: e => setTtsEnabled(e.target.checked) }),
        '🔊 TTS',
      ),
      h('button', { onClick: () => { setHistory([]); setResponse(''); setTranscript(''); setError(''); }, style: styles.clearBtn }, 'Wyczyść'),
    ),

    h('button', { onClick: state === 'idle' ? startListening : stopAll, style: { ...styles.micBtn, background: sc.color }, title: 'Spacja = nasłuchuj' }, sc.icon),
    h('div', { style: { ...styles.stateLabel, color: sc.color } }, sc.label),
    h('div', { style: styles.hint }, 'Spacja = nasłuchuj / zatrzymaj'),
    transcript && h('div', { style: styles.bubble('user') }, h('span', { style: styles.roleLabel('user') }, 'Ty: '), transcript),
    response && h('div', { style: styles.bubble('ai') }, h('span', { style: styles.roleLabel('ai') }, 'proxym: '), response),
    error && h('div', { style: styles.error }, '⚠ ' + error),
    h('details', { style: { marginTop: '8px' } },
      h('summary', { style: { fontSize: '11px', color: '#555', cursor: 'pointer' } }, 'Dostępne komendy'),
      h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '4px', marginTop: '6px' } },
        ['uruchom vscode','zatrzymaj vscode','status vscode','pokaż VM-y','ile wydałem','logi','status','test routing','jakie modele','pokaż konta']
          .map(cmd => h('button', { key: cmd, onClick: () => processInput(cmd), style: styles.chipBtn }, cmd))
      )
    ),
    history.length > 0 && h('div', { style: styles.historyBox },
      history.slice(-8).map((m, i) =>
        h('div', { key: i, style: { color: m.role === 'user' ? '#4338ca' : '#065f46', fontSize: '11px', padding: '1px 0' } },
          (m.role === 'user' ? 'Ty: ' : 'AI: ') + m.content
        )
      )
    ),
  );
}
