const PARTICIPANT_PROMPT = `You are Tater, a concise and supportive workshop cofacilitator helping a live participant in an in-person session.

Core guardrails:
- Respond only when explicitly asked by typed message.
- Do not greet, banter, or interrupt.
- Keep replies brief, useful, and grounded in the current workshop context.
- If a participant asks what to do next, offer one practical next step.
- If context is limited, say so plainly.`;

const urlParams = new URLSearchParams(window.location.search);
const SESSION_CODE = urlParams.get('sessionCode') || localStorage.getItem('tater_session_code') || 'TATER1';
const SESSION_PARTICIPANT_ID = localStorage.getItem('tater_session_participant_id') || null;
const TABLE_CODE = localStorage.getItem('tater_session_table_code') || urlParams.get('tableCode') || '';
const PARTICIPANT_DEVICE_LABEL = localStorage.getItem('tater_participant_device_label') || `participant_${Math.random().toString(36).slice(2, 10)}`;
localStorage.setItem('tater_session_code', SESSION_CODE);
localStorage.setItem('tater_participant_device_label', PARTICIPANT_DEVICE_LABEL);

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

const voterId = localStorage.getItem('tater_voter_id') || `voter_${Math.random().toString(36).slice(2, 10)}`;
localStorage.setItem('tater_voter_id', voterId);

let pc = null;
let dataChannel = null;
let refreshHandle = null;
let transcriptRefreshHandle = null;
let transcriptCacheKey = '';

function ensureTranscriptUi() {
  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;">Table transcript</div>';

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

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

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;
  const div = document.createElement('div');
  div.className = `msg ${role}`;
  div.textContent = text;
  els.chatLog.appendChild(div);
  els.chatLog.scrollTop = els.chatLog.scrollHeight;
}

function renderTranscriptEvents(transcriptEvents) {
  ensureTranscriptUi();
  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;">Table transcript</div>';

  if (!transcriptEvents?.length) {
    const empty = document.createElement('div');
    empty.textContent = 'No table 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 || '')}`;
    els.transcriptLog.appendChild(row);
  });

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

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());
      }
    }
  }

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

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

  dataChannel.send(JSON.stringify({
    type: 'session.update',
    session: {
      type: 'realtime',
      instructions: PARTICIPANT_PROMPT,
      output_modalities: ['text'],
      audio: {
        input: {
          turn_detection: null
        }
      }
    }
  }));
}

async function getTranscriptContext(limit = 25) {
  try {
    const qs = new URLSearchParams({ sessionCode: SESSION_CODE, limit: String(limit) });
    if (TABLE_CODE) qs.set('tableCode', TABLE_CODE);
    const data = await fetch(`/api/transcripts?${qs.toString()}`).then((res) => res.json());
    const items = data.transcriptEvents || [];
    if (!items.length) return 'No table transcript context has been captured yet.';
    return items.map((event) => `${event.displayName || event.speakerLabel || event.deviceLabel || 'Speaker'}: ${event.transcriptText}`).join('\n');
  } catch (error) {
    console.error('getTranscriptContext failed', error);
    return 'Transcript context is currently unavailable.';
  }
}

async function logTranscriptEvent({ transcriptText, eventType = 'typed_chat', speakerLabel = 'Participant' }) {
  try {
    const response = await fetch('/api/transcript-events', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        sessionCode: SESSION_CODE,
        transcriptText,
        eventType,
        speakerLabel,
        participantDeviceLabel: PARTICIPANT_DEVICE_LABEL,
        sessionParticipantId: SESSION_PARTICIPANT_ID,
        tableCode: TABLE_CODE || null
      })
    });

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

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

  dataChannel.send(JSON.stringify({
    type: 'response.create',
    response: {
      output_modalities: ['text'],
      instructions: [
        'Use the current table transcript context below when it is relevant.',
        'Be brief and practical.',
        '',
        'Table transcript context:',
        transcriptContext,
        instructions ? `\nAdditional instruction:\n${instructions}` : ''
      ].filter(Boolean).join('\n')
    }
  }));
}

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

  pc = new RTCPeerConnection();

  try {
    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', 'Connected to Tater. Chat is text-only.');
    });

    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 disconnect() {
  if (dataChannel) {
    try { dataChannel.close(); } catch {}
  }
  if (pc) {
    try { pc.close(); } catch {}
  }

  pc = null;
  dataChannel = null;
  if (els.connectBtn) els.connectBtn.disabled = false;
  if (els.disconnectBtn) els.disconnectBtn.disabled = true;
  setStatus('Disconnected');
}

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

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

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

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

  requestModelResponse();
}

function handleServerEvent(event) {
  let serverEvent;

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

  console.log('Participant realtime event:', serverEvent.type, serverEvent);

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

async function refreshPolls() {
  try {
    const data = await fetch(`/api/state?sessionCode=${encodeURIComponent(SESSION_CODE)}`).then((r) => r.json());
    renderPolls(data.polls || []);
  } catch (error) {
    console.error('refreshPolls failed', error);
  }
}

async function refreshTranscripts() {
  try {
    const qs = new URLSearchParams({ sessionCode: SESSION_CODE, limit: '80' });
    if (TABLE_CODE) qs.set('tableCode', TABLE_CODE);
    const data = await fetch(`/api/transcripts?${qs.toString()}`).then((r) => r.json());
    renderTranscriptEvents(data.transcriptEvents || []);
  } catch (error) {
    console.error('refreshTranscripts failed', error);
  }
}

async function submitVote(pollId, optionIds) {
  const response = await fetch(`/api/polls/${pollId}/vote`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ voterId, optionIds })
  }).then((r) => r.json());

  if (!response.ok) {
    addMessage('system', response.error || 'Vote failed.');
  }

  refreshPolls();
}

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

  for (const poll of polls) {
    const card = document.createElement('div');
    card.className = 'small-card';

    const title = document.createElement('h4');
    title.textContent = poll.question;
    card.appendChild(title);

    const note = document.createElement('p');
    note.textContent = poll.multiSelect ? 'Choose one or more.' : 'Choose one.';
    card.appendChild(note);

    const selectedIds = poll.votesByVoter?.[voterId] || [];

    poll.options.forEach((option) => {
      const row = document.createElement('label');
      row.className = 'poll-option';
      row.style.marginTop = '0.55rem';

      const input = document.createElement('input');
      input.type = poll.multiSelect ? 'checkbox' : 'radio';
      input.name = poll.id;
      input.value = option.id;
      input.checked = selectedIds.includes(option.id);
      input.addEventListener('change', () => {
        const optionIds = [...card.querySelectorAll('input:checked')].map((el) => el.value);
        submitVote(poll.id, optionIds);
      });

      const span = document.createElement('span');
      span.textContent = option.label;

      row.appendChild(input);
      row.appendChild(span);
      card.appendChild(row);
    });

    els.pollList.appendChild(card);
  }
}

ensureTranscriptUi();
els.connectBtn?.addEventListener('click', connect);
els.disconnectBtn?.addEventListener('click', disconnect);
els.sendBtn?.addEventListener('click', async () => {
  await sendTextMessage(els.messageInput?.value || '');
  if (els.messageInput) els.messageInput.value = '';
});
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 to start chatting with Tater. Session: ${SESSION_CODE}`);
refreshPolls();
refreshTranscripts();
refreshHandle = setInterval(refreshPolls, 3000);
transcriptRefreshHandle = setInterval(refreshTranscripts, 3000);
window.addEventListener('beforeunload', () => {
  clearInterval(refreshHandle);
  clearInterval(transcriptRefreshHandle);
});
