const TATER_PROMPT = `You are Tater, a concise, warm, human-centered live workshop cofacilitator.
Your avatar is a potato, so light potato-based humor is welcome, but only sparingly.

Core guardrails:
- Do not respond automatically to ambient conversation.
- Only respond when explicitly requested by typed prompt, quick action, or the Ask Tater button.
- Do not greet, banter, check in, or fill silence.
- Keep responses brief, facilitative, and concrete.
- Default to summaries, options, prompts, and next steps rather than casual conversation.
- If context is limited, say so plainly.

Tool rules:
- Use create_poll only when explicitly requested or clearly needed for a decision.
- Use generate_word_cloud only when explicitly requested.
- Use generate_slide_deck only when explicitly requested.
- After a tool succeeds, explain what was created in 1–2 short lines.`;

const TOOL_DEFS = [
  {
    type: 'function',
    name: 'create_poll',
    description: 'Create a poll that participants can vote on during the live session.',
    parameters: {
      type: 'object',
      properties: {
        question: { type: 'string' },
        options: { type: 'array', items: { type: 'string' } },
        multiSelect: { type: 'boolean' }
      },
      required: ['question', 'options']
    }
  },
  {
    type: 'function',
    name: 'generate_word_cloud',
    description: 'Create a word cloud from live discussion themes.',
    parameters: {
      type: 'object',
      properties: {
        title: { type: 'string' },
        seed_terms: { type: 'array', items: { type: 'string' } }
      },
      required: ['title', 'seed_terms']
    }
  },
  {
    type: 'function',
    name: 'generate_slide_deck',
    description: 'Generate a short downloadable PowerPoint deck summarizing discussion, decisions, or next steps.',
    parameters: {
      type: 'object',
      properties: {
        title: { type: 'string' },
        audience: { type: 'string' },
        objective: { type: 'string' },
        slide_count: { type: 'number' },
        key_points: { type: 'array', items: { type: 'string' } }
      },
      required: ['title', 'audience', 'objective', 'slide_count', 'key_points']
    }
  }
];

const urlParams = new URLSearchParams(window.location.search);
const SESSION_CODE = urlParams.get('sessionCode') || localStorage.getItem('tater_session_code') || 'TATER1';
localStorage.setItem('tater_session_code', SESSION_CODE);
const FACILITATOR_DEVICE_LABEL = localStorage.getItem('tater_facilitator_device_label') || `facilitator_${Math.random().toString(36).slice(2, 10)}`;
localStorage.setItem('tater_facilitator_device_label', FACILITATOR_DEVICE_LABEL);

const els = {
  audioSource: document.getElementById('audioSource'),
  connectBtn: document.getElementById('connectBtn'),
  disconnectBtn: document.getElementById('disconnectBtn'),
  sendBtn: document.getElementById('sendBtn'),
  clearBtn: document.getElementById('clearBtn'),
  messageInput: document.getElementById('messageInput'),
  chatLog: document.getElementById('chatLog'),
  statusPill: document.getElementById('statusPill'),
  statusText: document.getElementById('statusText'),
  pollList: document.getElementById('pollList'),
  wordCloudWrap: document.getElementById('wordCloudWrap'),
  deckList: document.getElementById('deckList'),
  askTaterBtn: document.getElementById('askTaterBtn'),
  transcriptLog: document.getElementById('transcriptLog')
};

let pc = null;
let dataChannel = null;
let remoteAudioEl = null;
let localStream = null;
let refreshHandle = null;
let transcriptRefreshHandle = null;
let isVoiceArmed = false;
let transcriptCacheKey = '';

function ensureSupplementalUi() {
  if (!els.askTaterBtn) {
    const button = document.createElement('button');
    button.id = 'askTaterBtn';
    button.type = 'button';
    button.textContent = 'Ask Tater';
    button.style.marginLeft = '0.5rem';

    if (els.disconnectBtn?.parentElement) {
      els.disconnectBtn.parentElement.insertBefore(button, els.disconnectBtn.nextSibling);
    } else if (els.connectBtn?.parentElement) {
      els.connectBtn.parentElement.appendChild(button);
    } else {
      document.body.appendChild(button);
    }
    els.askTaterBtn = button;
  }

  if (!els.transcriptLog) {
    const wrap = document.createElement('div');
    wrap.id = 'transcriptLog';
    wrap.style.marginTop = '1rem';
    wrap.style.maxHeight = '220px';
    wrap.style.overflowY = 'auto';
    wrap.style.padding = '0.75rem';
    wrap.style.borderRadius = '12px';
    wrap.style.background = 'rgba(111,63,20,0.08)';
    wrap.style.border = '1px solid rgba(111,63,20,0.16)';
    wrap.innerHTML = '<div style="font-weight:600;margin-bottom:0.5rem;">Live transcript</div>';

    if (els.chatLog?.parentElement) {
      els.chatLog.parentElement.insertBefore(wrap, els.chatLog.nextSibling);
    } else {
      document.body.appendChild(wrap);
    }
    els.transcriptLog = wrap;
  }
}

function setStatus(text, kind = 'default') {
  if (!els.statusText || !els.statusPill) return;
  els.statusText.textContent = text;
  els.statusPill.className = `status ${kind === 'live' ? 'live' : kind === 'warn' ? 'warn' : ''}`.trim();
}

function addMessage(role, text) {
  if (!els.chatLog) return null;
  const div = document.createElement('div');
  div.className = `msg ${role}`;
  div.textContent = text;
  els.chatLog.appendChild(div);
  els.chatLog.scrollTop = els.chatLog.scrollHeight;
  return div;
}

function renderTranscriptEvents(transcriptEvents) {
  ensureSupplementalUi();
  if (!els.transcriptLog) return;

  const nextKey = JSON.stringify((transcriptEvents || []).map((event) => [event.id, event.transcriptText, event.eventType]));
  if (nextKey === transcriptCacheKey) return;
  transcriptCacheKey = nextKey;

  els.transcriptLog.innerHTML = '<div style="font-weight:600;margin-bottom:0.5rem;">Live transcript</div>';

  if (!transcriptEvents?.length) {
    const empty = document.createElement('div');
    empty.textContent = 'No transcript captured yet.';
    empty.style.opacity = '0.7';
    els.transcriptLog.appendChild(empty);
    return;
  }

  transcriptEvents.forEach((event) => {
    const row = document.createElement('div');
    row.style.padding = '0.35rem 0';
    row.style.borderTop = '1px solid rgba(0,0,0,0.06)';
    const who = event.displayName || event.speakerLabel || event.deviceLabel || event.tableName || 'Speaker';
    row.innerHTML = `<strong>${who}</strong>: ${escapeHtml(event.transcriptText || '')}`;
    if (event.eventType === 'armed') {
      row.style.background = 'rgba(207,124,38,0.08)';
      row.style.paddingLeft = '0.35rem';
      row.style.paddingRight = '0.35rem';
      row.style.borderRadius = '6px';
    }
    els.transcriptLog.appendChild(row);
  });

  els.transcriptLog.scrollTop = els.transcriptLog.scrollHeight;
}

function escapeHtml(text) {
  return String(text || '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function extractMessageTextFromResponse(response) {
  const output = response?.output || [];
  const parts = [];

  for (const item of output) {
    if (item.type !== 'message' || !Array.isArray(item.content)) continue;
    for (const content of item.content) {
      if (typeof content?.text === 'string' && content.text.trim()) {
        parts.push(content.text.trim());
        continue;
      }
      if (content?.type === 'output_text' && typeof content?.text === 'string' && content.text.trim()) {
        parts.push(content.text.trim());
        continue;
      }
      if (typeof content?.transcript === 'string' && content.transcript.trim()) {
        parts.push(content.transcript.trim());
      }
    }
  }

  return parts.join('\n').trim();
}

async function getAudioStream(mode) {
  if (mode === 'none') return null;

  if (mode === 'display') {
    const display = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true });
    const audioTracks = display.getAudioTracks();
    if (!audioTracks.length) {
      throw new Error('No system or tab audio track was provided by the browser.');
    }
    return new MediaStream([audioTracks[0]]);
  }

  return navigator.mediaDevices.getUserMedia({ audio: true });
}

function configureSession() {
  if (!dataChannel || dataChannel.readyState !== 'open') return;

  dataChannel.send(JSON.stringify({
    type: 'session.update',
    session: {
      type: 'realtime',
      instructions: TATER_PROMPT,
      tools: TOOL_DEFS,
      output_modalities: ['text'],
      audio: {
        input: {
          noise_reduction: { type: 'near_field' },
          turn_detection: {
            type: 'server_vad',
            create_response: false,
            interrupt_response: false,
            silence_duration_ms: 700,
            prefix_padding_ms: 400,
            threshold: 0.55
          },
          transcription: {
            model: 'gpt-4o-mini-transcribe',
            language: 'en'
          }
        }
      }
    }
  }));
}

function deleteConversationItem(itemId) {
  if (!dataChannel || dataChannel.readyState !== 'open' || !itemId) return;
  dataChannel.send(JSON.stringify({
    type: 'conversation.item.delete',
    item_id: itemId
  }));
}

async function fetchTranscriptContext(limit = 40) {
  try {
    const response = await fetch(`/api/transcripts?sessionCode=${encodeURIComponent(SESSION_CODE)}&limit=${limit}`);
    const data = await response.json();
    const items = data.transcriptEvents || [];
    if (!items.length) return 'No transcript context has been captured yet.';
    return items
      .slice(-limit)
      .map((event) => {
        const who = event.displayName || event.speakerLabel || event.deviceLabel || event.tableName || 'Speaker';
        return `${who}: ${event.transcriptText}`;
      })
      .join('\n');
  } catch (error) {
    console.error('fetchTranscriptContext failed', error);
    return 'Transcript context is currently unavailable.';
  }
}

async function requestModelResponse(instructions = null) {
  if (!dataChannel || dataChannel.readyState !== 'open') return;

  const transcriptContext = await fetchTranscriptContext();
  const payload = {
    type: 'response.create',
    response: {
      output_modalities: ['text'],
      instructions: [
        'Use the current workshop transcript context below when it is relevant.',
        'Be brief and concrete.',
        '',
        'Workshop transcript context:',
        transcriptContext,
        instructions ? `\nAdditional instruction:\n${instructions}` : ''
      ].filter(Boolean).join('\n')
    }
  };

  dataChannel.send(JSON.stringify(payload));
}

async function logTranscriptEvent({ transcriptText, eventType = 'passive', itemId = null, speakerLabel = 'Facilitator' }) {
  try {
    const response = await fetch('/api/transcript-events', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        sessionCode: SESSION_CODE,
        transcriptText,
        eventType,
        itemId,
        speakerLabel,
        participantDeviceLabel: FACILITATOR_DEVICE_LABEL
      })
    });

    if (!response.ok) {
      const payload = await response.json().catch(() => ({}));
      throw new Error(payload.error || `HTTP ${response.status}`);
    }
  } catch (error) {
    console.error('Transcript logging failed', error);
    addMessage('system', `Transcript logging failed: ${error.message}`);
  }
}

async function connect() {
  if (pc) return;
  setStatus('Connecting…', 'warn');

  pc = new RTCPeerConnection();
  remoteAudioEl = null;

  try {
    const audioMode = els.audioSource?.value || 'mic';

    if (audioMode === 'none') {
      pc.addTransceiver('audio', { direction: 'recvonly' });
    } else {
      localStream = await getAudioStream(audioMode);
      if (localStream) {
        for (const track of localStream.getTracks()) {
          pc.addTrack(track, localStream);
        }
      }
    }

    dataChannel = pc.createDataChannel('oai-events');

    dataChannel.addEventListener('open', () => {
      setStatus('Connected', 'live');
      if (els.connectBtn) els.connectBtn.disabled = true;
      if (els.disconnectBtn) els.disconnectBtn.disabled = false;
      configureSession();
      addMessage('system', 'Tater is connected.');
    });

    dataChannel.addEventListener('message', handleServerEvent);

    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);

    const response = await fetch('/session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/sdp' },
      body: offer.sdp
    });

    if (!response.ok) {
      throw new Error(await response.text());
    }

    await pc.setRemoteDescription({
      type: 'answer',
      sdp: await response.text()
    });
  } catch (error) {
    console.error(error);
    addMessage('system', `Connection failed: ${error.message}`);
    disconnect();
    setStatus('Connection failed', 'warn');
  }
}

function handleServerEvent(event) {
  let serverEvent;

  try {
    serverEvent = JSON.parse(event.data);
  } catch (error) {
    console.log('Non-JSON dataChannel message:', event.data);
    return;
  }

  console.log('Realtime server event:', serverEvent.type, serverEvent);

  if (serverEvent.type === 'conversation.item.input_audio_transcription.completed') {
    const transcriptText = String(serverEvent.transcript || '').trim();
    const itemId = serverEvent.item_id || null;
    if (!transcriptText) {
      deleteConversationItem(itemId);
      return;
    }

    const eventType = isVoiceArmed ? 'armed' : 'passive';
    logTranscriptEvent({ transcriptText, eventType, itemId, speakerLabel: 'Facilitator' });

    if (!isVoiceArmed) {
      deleteConversationItem(itemId);
      return;
    }

    isVoiceArmed = false;
    if (els.askTaterBtn) {
      els.askTaterBtn.disabled = false;
      els.askTaterBtn.textContent = 'Ask Tater';
    }

    addMessage('user', `[voice] ${transcriptText}`);

    dataChannel.send(JSON.stringify({
      type: 'conversation.item.create',
      item: {
        type: 'message',
        role: 'user',
        content: [{ type: 'input_text', text: transcriptText }]
      }
    }));

    deleteConversationItem(itemId);
    requestModelResponse('Respond only to the most recent spoken facilitator request. Be brief and concrete.');
    return;
  }

  if (serverEvent.type === 'response.done') {
    const text = extractMessageTextFromResponse(serverEvent.response);
    if (text) {
      addMessage('assistant', text);
    }

    executeFunctionCalls(serverEvent).catch((error) => {
      addMessage('system', `Tool execution failed: ${error.message}`);
    });
  }
}

function disconnect() {
  try {
    if (dataChannel) dataChannel.close();
  } catch {}

  try {
    if (localStream) localStream.getTracks().forEach((track) => track.stop());
  } catch {}

  try {
    if (pc) {
      pc.getSenders().forEach((sender) => {
        try { sender.track?.stop(); } catch {}
      });
      pc.close();
    }
  } catch {}

  pc = null;
  dataChannel = null;
  localStream = null;
  isVoiceArmed = false;

  if (els.connectBtn) els.connectBtn.disabled = false;
  if (els.disconnectBtn) els.disconnectBtn.disabled = true;
  if (els.askTaterBtn) {
    els.askTaterBtn.disabled = false;
    els.askTaterBtn.textContent = 'Ask Tater';
  }
  setStatus('Disconnected');
}

async function sendTextMessage(text) {
  if (!dataChannel || dataChannel.readyState !== 'open') {
    addMessage('system', 'Connect Tater before sending a message.');
    return;
  }

  const clean = String(text || '').trim();
  if (!clean) return;

  addMessage('user', clean);
  await logTranscriptEvent({ transcriptText: clean, eventType: 'typed_chat', speakerLabel: 'Facilitator' });

  dataChannel.send(JSON.stringify({
    type: 'conversation.item.create',
    item: {
      type: 'message',
      role: 'user',
      content: [{ type: 'input_text', text: clean }]
    }
  }));

  requestModelResponse();
}

async function executeFunctionCalls(serverEvent) {
  const output = serverEvent?.response?.output || [];
  const functionCalls = output.filter((item) => item.type === 'function_call');

  for (const call of functionCalls) {
    const args = (() => {
      try { return JSON.parse(call.arguments || '{}'); } catch { return {}; }
    })();

    const toolResponse = await fetch('/api/tool', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: call.name, arguments: args, sessionCode: SESSION_CODE })
    }).then((res) => res.json());

    dataChannel.send(JSON.stringify({
      type: 'conversation.item.create',
      item: {
        type: 'function_call_output',
        call_id: call.call_id,
        output: JSON.stringify(toolResponse)
      }
    }));

    await requestModelResponse('A tool just ran. Briefly explain what was created and how to use it.');
  }
}

async function refreshState() {
  try {
    const data = await fetch(`/api/state?sessionCode=${encodeURIComponent(SESSION_CODE)}`).then((res) => res.json());
    renderPolls(data.polls || []);
    renderWordCloud(data.wordClouds?.[0] || null);
    renderDecks(data.decks || []);
  } catch (error) {
    console.error('refreshState failed', error);
  }
}

async function refreshTranscripts() {
  try {
    const data = await fetch(`/api/transcripts?sessionCode=${encodeURIComponent(SESSION_CODE)}&limit=120`).then((res) => res.json());
    renderTranscriptEvents(data.transcriptEvents || []);
  } catch (error) {
    console.error('refreshTranscripts failed', error);
  }
}

function renderPolls(polls) {
  if (!els.pollList) return;
  els.pollList.innerHTML = '';

  if (!polls.length) {
    els.pollList.innerHTML = '<div class="small-card"><p>No polls yet.</p></div>';
    return;
  }

  for (const poll of polls) {
    const totalVotes = poll.options.reduce((sum, option) => sum + option.votes, 0);
    const safeTotal = Math.max(totalVotes, 1);
    const wrap = document.createElement('div');
    wrap.className = 'small-card';
    wrap.innerHTML = `<h4>${escapeHtml(poll.question)}</h4><p>${poll.multiSelect ? 'Multi-select' : 'Single choice'} · ${totalVotes} votes</p>`;

    for (const option of poll.options) {
      const pct = Math.round((option.votes / safeTotal) * 100);
      const row = document.createElement('div');
      row.style.marginTop = '0.6rem';
      row.innerHTML = `
        <div class="poll-option">
          <strong>${escapeHtml(option.label)}</strong>
          <span>${option.votes}</span>
        </div>
        <div class="bar"><div style="width:${pct}%"></div></div>
      `;
      wrap.appendChild(row);
    }

    els.pollList.appendChild(wrap);
  }
}

function renderWordCloud(cloud) {
  if (!els.wordCloudWrap) return;
  if (!cloud) {
    els.wordCloudWrap.innerHTML = '<p>No word cloud yet.</p>';
    return;
  }

  const terms = cloud.terms || [];
  const max = Math.max(...terms.map((term) => term.value), 1);
  const cloudHtml = terms.map((term) => {
    const size = 0.85 + (term.value / max) * 1.2;
    return `<span style="font-size:${size}rem">${escapeHtml(term.text)}</span>`;
  }).join('');

  els.wordCloudWrap.innerHTML = `<h4>${escapeHtml(cloud.title)}</h4><div class="word-cloud">${cloudHtml}</div>`;
}

function renderDecks(decks) {
  if (!els.deckList) return;
  els.deckList.innerHTML = '';

  if (!decks.length) {
    els.deckList.innerHTML = '<div class="small-card"><p>No decks yet.</p></div>';
    return;
  }

  for (const deck of decks) {
    const item = document.createElement('div');
    item.className = 'small-card';
    item.innerHTML = `
      <h4>${escapeHtml(deck.title)}</h4>
      <p>${escapeHtml(deck.subtitle || '')}</p>
      <p><a href="${deck.url}" target="_blank" rel="noreferrer">Open artifact</a></p>
    `;
    els.deckList.appendChild(item);
  }
}

const quickPrompts = {
  summary: 'Summarize the live discussion so far in 5 bullets, then give one tension and one recommended next move for the facilitator.',
  poll: 'Create a short poll that would help the facilitator take the pulse of the room right now. Use the create_poll tool.',
  wordcloud: 'Create a live word cloud that captures the strongest themes in the room right now. Use the generate_word_cloud tool.',
  slides: 'Create a 5-slide discussion snapshot deck for the facilitator. Use the generate_slide_deck tool.',
  design: 'Act as a design-thinking cofacilitator. Propose a focused 7-minute exercise the facilitator can run next, including purpose, prompt, timebox, and debrief question.',
  nextmove: 'Based on the discussion so far, recommend the best next facilitation move in under 6 bullets.'
};

ensureSupplementalUi();

document.querySelectorAll('[data-quick]').forEach((button) => {
  button.addEventListener('click', async () => {
    await sendTextMessage(quickPrompts[button.dataset.quick]);
  });
});

els.connectBtn?.addEventListener('click', connect);
els.disconnectBtn?.addEventListener('click', disconnect);
els.askTaterBtn?.addEventListener('click', () => {
  isVoiceArmed = true;
  els.askTaterBtn.disabled = true;
  els.askTaterBtn.textContent = 'Listening…';
  addMessage('system', 'Listening for the next spoken question…');
});

els.sendBtn?.addEventListener('click', async () => {
  await sendTextMessage(els.messageInput?.value || '');
  if (els.messageInput) els.messageInput.value = '';
});

els.clearBtn?.addEventListener('click', () => {
  if (els.chatLog) els.chatLog.innerHTML = '';
  addMessage('system', 'Chat cleared locally.');
});

els.messageInput?.addEventListener('keydown', async (event) => {
  if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
    event.preventDefault();
    await sendTextMessage(els.messageInput?.value || '');
    if (els.messageInput) els.messageInput.value = '';
  }
});

addMessage('system', `Connect Tater to begin. Session: ${SESSION_CODE}`);
refreshState();
refreshTranscripts();
refreshHandle = setInterval(refreshState, 3000);
transcriptRefreshHandle = setInterval(refreshTranscripts, 3000);
window.addEventListener('beforeunload', () => {
  clearInterval(refreshHandle);
  clearInterval(transcriptRefreshHandle);
});
