(function() {
'use strict';

var TURNSTILE_KEY = '0x4AAAAAACLI9vyJZYGLg9lS';
var API = '/api';

var S = {
  bal: 0,
  hasVoice: false,
  hasAvatar: false,
  mood: 'calm',
  fmt: 'audio',
  busy: false,
  previewId: null,
  videoId: null,
  videoPoll: null,
  rec: null,
  recChunks: [],
  recBlob: null,
  recStart: 0,
  recTimer: null,
  avatarFile: null,
  tsWidgets: {},
  toastTimer: null,
  journal: []
};

var audio = new Audio();

function $(id) { return document.getElementById(id); }
function $$(sel, root) { return (root || document).querySelectorAll(sel); }
function on(el, ev, fn) { if (el) el.addEventListener(ev, fn); }
function setText(el, t) { if (el) el.textContent = t; }
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function pad2(n) { return n < 10 ? '0' + n : '' + n; }
function jsonParse(s, fb) { try { return JSON.parse(s) || fb; } catch(e) { return fb; } }

function delegate(root, ev, sel, fn) {
  if (!root) return;
  root.addEventListener(ev, function(e) {
    var t = e.target.closest(sel);
    if (t && root.contains(t)) fn.call(t, e);
  });
}

function btnLoad(id, on) {
  var btn = $(id); if (!btn) return;
  var t = btn.querySelector('.bm-text');
  var l = btn.querySelector('.bm-spin');
  if (on) {
    if (t) t.classList.add('hidden');
    if (l) l.classList.remove('hidden');
    btn.disabled = true;
  } else {
    if (t) t.classList.remove('hidden');
    if (l) l.classList.add('hidden');
    btn.disabled = false;
  }
}

function showErr(id, msg) {
  var el = $(id); if (!el) return;
  el.textContent = msg;
  el.classList.remove('hidden');
}

function hideErr(id) {
  var el = $(id); if (el) el.classList.add('hidden');
}

function toast(msg) {
  var t = $('toast');
  var m = $('toast-msg');
  if (!t || !m) return;
  m.textContent = msg;
  t.classList.remove('hidden');
  clearTimeout(S.toastTimer);
  S.toastTimer = setTimeout(function() { t.classList.add('hidden'); }, 3000);
}

function showOverlay(title, sub) {
  setText($('ov-title'), title);
  setText($('ov-sub'), sub || '');
  $('overlay').classList.remove('hidden');
}

function hideOverlay() {
  $('overlay').classList.add('hidden');
}

function fmtTime(s) {
  if (!s || isNaN(s)) return '0:00';
  var m = Math.floor(s / 60);
  var sc = Math.floor(s % 60);
  return m + ':' + pad2(sc);
}

// ─── Turnstile ───
function renderTurnstile(containerId) {
  var el = $(containerId);
  if (!el || typeof turnstile === 'undefined') return;
  if (S.tsWidgets[containerId] != null) {
    try { turnstile.remove(S.tsWidgets[containerId]); } catch(e) {}
  }
  el.innerHTML = '';
  S.tsWidgets[containerId] = turnstile.render(el, {
    sitekey: TURNSTILE_KEY,
    theme: 'dark',
    callback: function() {}
  });
}

function getTurnstileToken(containerId) {
  if (typeof turnstile === 'undefined' || S.tsWidgets[containerId] == null) return null;
  return turnstile.getResponse(S.tsWidgets[containerId]);
}

function resetTurnstile(containerId) {
  if (typeof turnstile !== 'undefined' && S.tsWidgets[containerId] != null) {
    try { turnstile.reset(S.tsWidgets[containerId]); } catch(e) {}
  }
}

// ─── API ───
function apiCall(path, opts) {
  opts = opts || {};
  var headers = opts.headers || {};
  var isForm = opts.body instanceof FormData;
  if (!isForm) headers['Content-Type'] = 'application/json';
  return fetch(API + path, {
    method: opts.method || 'GET',
    headers: headers,
    body: opts.body || null,
    credentials: 'include'
  }).then(function(r) {
    return r.json().then(function(d) {
      if (!r.ok) throw Object.assign({ status: r.status }, d);
      return d;
    });
  });
}

// ─── User sync ───
function syncUser() {
  apiCall('/balance').then(function(d) {
    S.bal = typeof d.balance === 'number' ? d.balance : 0;
    S.hasVoice = !!d.hasVoice;
    S.hasAvatar = !!d.hasAvatar;
    updateUI();
  }).catch(function() { updateUI(); });
}

function updateUI() {
  var f = '$' + S.bal.toFixed(2);
  setText($('bal-hdr'), f);
  setText($('bal-main'), f);

  var badge = $('scard-badge');
  if (badge) {
    if (S.bal <= 0) { setText(badge, '0 credits'); badge.style.color = '#FF5252'; }
    else if (S.bal < 10) { setText(badge, 'Low'); badge.style.color = '#FFD54F'; }
    else { setText(badge, 'Healthy'); badge.style.color = '#00BFA5'; }
  }

  if (S.hasVoice) {
    var sv = $('si-voice'); if (sv) sv.classList.add('done');
    setText($('si-voice-sub'), 'Voice cloned ✓');
    setText($('sdot-voice-t'), 'Voice ready');
    $$('.sdot-voice').forEach(function(d) { d.classList.add('on'); });
  }
  if (S.hasAvatar) {
    var sa = $('si-avatar'); if (sa) sa.classList.add('done');
    setText($('si-avatar-sub'), 'Avatar uploaded ✓');
    setText($('sdot-avatar-t'), 'Avatar ready');
    $$('.sdot-avatar').forEach(function(d) { d.classList.add('on'); });
  }
}

// ─── Navigation ───
function navigate(target) {
  if (target === 'topup') { openTopUp(); return; }
  $$('.layer').forEach(function(l) { l.classList.remove('active'); });
  var el = $('layer-' + target);
  if (el) {
    el.classList.add('active');
    var sc = el.querySelector('.lscroll,.home-scroll');
    if (sc) sc.scrollTop = 0;
  }
  $$('.tab').forEach(function(t) {
    t.classList.toggle('active', t.getAttribute('data-tab') === target);
  });
  if (target === 'record') renderTurnstile('ts-voice');
  if (target === 'avatar') renderTurnstile('ts-avatar');
  if (target === 'generate') renderTurnstile('ts-generate');
  if (target === 'journal') renderJournal();
}

// ─── Top Up ───
function openTopUp() {
  $('modal-topup').classList.remove('hidden');
  $('tu-amount').value = '';
  $('tu-slider').value = 10;
  hideErr('err-topup');
  updateTuLabel(10);
}

function closeModal() {
  $$('.modal').forEach(function(m) { m.classList.add('hidden'); });
}

function updateTuLabel(v) {
  v = clamp(v || 10, 10, 500);
  setText($('tu-btn-text'), 'Continue \u2014 $' + v.toFixed(2));
}

function processTopUp() {
  var amt = parseInt($('tu-amount').value) || parseInt($('tu-slider').value) || 10;
  if (amt < 10 || amt > 500) { showErr('err-topup', 'Enter $10 \u2013 $500'); return; }
  hideErr('err-topup');
  btnLoad('btn-topup', true);
  apiCall('/topup', {
    method: 'POST',
    body: JSON.stringify({ amount: amt })
  }).then(function(d) {
    if (d.url) window.location.href = d.url;
    else { closeModal(); syncUser(); toast('Balance updated!'); btnLoad('btn-topup', false); }
  }).catch(function(e) {
    showErr('err-topup', e.error || 'Payment failed');
    btnLoad('btn-topup', false);
  });
}

// ─── Voice Recording ───
function toggleRecord() {
  if (S.rec && S.rec.state === 'recording') { S.rec.stop(); return; }
  navigator.mediaDevices.getUserMedia({ audio: true }).then(function(stream) {
    S.recChunks = [];
    S.rec = new MediaRecorder(stream);
    S.rec.ondataavailable = function(e) { if (e.data.size > 0) S.recChunks.push(e.data); };
    S.rec.onstop = function() {
      stream.getTracks().forEach(function(t) { t.stop(); });
      S.recBlob = new Blob(S.recChunks, { type: 'audio/webm' });
      var ra = $('rec-audio');
      if (ra) ra.src = URL.createObjectURL(S.recBlob);
      var rp = $('rec-playback'); if (rp) rp.classList.remove('hidden');
      setText($('rec-btn-text'), 'Re-record');
      var rr = $('rec-ring'); if (rr) rr.classList.remove('on');
      clearInterval(S.recTimer);
    };
    S.rec.start();
    S.recStart = Date.now();
    var rr = $('rec-ring'); if (rr) rr.classList.add('on');
    setText($('rec-btn-text'), 'Stop Recording');
    var rp = $('rec-playback'); if (rp) rp.classList.add('hidden');
    hideErr('err-voice');
    S.recTimer = setInterval(function() {
      var s = Math.floor((Date.now() - S.recStart) / 1000);
      setText($('rec-timer'), Math.floor(s / 60) + ':' + pad2(s % 60));
    }, 250);
  }).catch(function() {
    showErr('err-voice', 'Microphone access denied');
  });
}

function submitVoice() {
  if (S.busy || !S.recBlob) return;
  var tk = getTurnstileToken('ts-voice');
  if (!tk) { showErr('err-voice', 'Complete the verification'); return; }
  S.busy = true;
  hideErr('err-voice');
  btnLoad('btn-submit-voice', true);
  var fd = new FormData();
  fd.append('audio', S.recBlob, 'voice.webm');
  apiCall('/voice/clone', {
    method: 'POST',
    headers: { 'X-Turnstile-Token': tk },
    body: fd
  }).then(function() {
    S.hasVoice = true;
    updateUI();
    navigate('home');
    toast('Voice cloned!');
  }).catch(function(e) {
    showErr('err-voice', e.error || 'Clone failed');
    resetTurnstile('ts-voice');
  }).finally(function() {
    S.busy = false;
    btnLoad('btn-submit-voice', false);
  });
}

// ─── Avatar ───
function onAvatarFile(e) {
  var file = e.target.files && e.target.files[0];
  if (!file) return;
  S.avatarFile = file;
  var reader = new FileReader();
  reader.onload = function(ev) {
    var img = $('av-preview-img'); if (img) img.src = ev.target.result;
    var box = $('av-preview-box'); if (box) box.classList.remove('hidden');
    var ph = $('av-placeholder'); if (ph) ph.classList.add('hidden');
  };
  reader.readAsDataURL(file);
  var btn = $('btn-submit-avatar'); if (btn) btn.disabled = false;
}

function submitAvatar() {
  if (S.busy || !S.avatarFile) return;
  var tk = getTurnstileToken('ts-avatar');
  if (!tk) { showErr('err-avatar', 'Complete the verification'); return; }
  S.busy = true;
  hideErr('err-avatar');
  btnLoad('btn-submit-avatar', true);
  var fd = new FormData();
  fd.append('image', S.avatarFile);
  apiCall('/avatar/create', {
    method: 'POST',
    headers: { 'X-Turnstile-Token': tk },
    body: fd
  }).then(function() {
    S.hasAvatar = true;
    updateUI();
    navigate('home');
    toast('Avatar uploaded!');
  }).catch(function(e) {
    showErr('err-avatar', e.error || 'Upload failed');
    resetTurnstile('ts-avatar');
  }).finally(function() {
    S.busy = false;
    btnLoad('btn-submit-avatar', false);
  });
}

// ─── Generate ───
function generateStory() {
  if (S.busy) return;
  var prompt = ($('gen-prompt') ? $('gen-prompt').value : '').trim();
  if (!prompt) { showErr('err-generate', 'Enter a story prompt'); return; }
  if (S.fmt === 'audio' && !S.hasVoice) { showErr('err-generate', 'Record your voice first'); return; }
  if (S.fmt === 'video' && !S.hasAvatar) { showErr('err-generate', 'Upload an avatar first'); return; }

  var tk = getTurnstileToken('ts-generate');
  if (!tk) { showErr('err-generate', 'Complete the verification'); return; }

  hideErr('err-generate');
  S.busy = true;
  showOverlay('Generating preview\u2026', 'Building your story');

  apiCall('/generate', {
    method: 'POST',
    headers: { 'X-Turnstile-Token': tk },
    body: JSON.stringify({ prompt: prompt, mood: S.mood, format: S.fmt })
  }).then(function(d) {
    hideOverlay();
    S.previewId = d.storyId || null;
    setText($('pv-prompt-echo'), prompt);
    var pvAud = $('pv-audio');
    if (pvAud && d.previewUrl) pvAud.src = d.previewUrl;
    navigate('preview');
  }).catch(function(e) {
    hideOverlay();
    showErr('err-generate', e.error || 'Generation failed');
    resetTurnstile('ts-generate');
  }).finally(function() {
    S.busy = false;
  });
}

// ─── Unlock ───
function unlockStory(type) {
  if (S.busy || !S.previewId) return;
  if (S.bal <= 0) { openTopUp(); toast('Add balance first'); return; }
  S.busy = true;
  hideErr('err-preview');
  var endpoint = type === 'video' ? '/generate' : '/generate';
  showOverlay(type === 'video' ? 'Creating video\u2026' : 'Creating audio\u2026', 'This may take a moment');

  apiCall('/generate', {
    method: 'POST',
    body: JSON.stringify({ storyId: S.previewId, unlockType: type })
  }).then(function(d) {
    hideOverlay();
    syncUser();

    var entry = {
      id: d.storyId || S.previewId,
      type: type,
      prompt: $('pv-prompt-echo') ? $('pv-prompt-echo').textContent : '',
      date: new Date().toISOString(),
      audioUrl: d.audioUrl || null,
      videoId: d.videoId || null,
      videoUrl: d.videoUrl || null
    };
    S.journal.unshift(entry);
    localStorage.setItem('rt_journal', JSON.stringify(S.journal.slice(0, 50)));

    if (d.audioUrl) playFull(entry);
    if (d.videoId) {
      S.videoId = d.videoId;
      showVideoStatus('processing');
      startVideoPoll(d.videoId);
    }

    var tp = $('tab-player'); if (tp) tp.style.display = '';
    navigate('player');
    toast('Story unlocked!');
  }).catch(function(e) {
    hideOverlay();
    showErr('err-preview', e.error || 'Unlock failed');
    syncUser();
  }).finally(function() {
    S.busy = false;
  });
}

// ─── Player ───
function playFull(entry) {
  setText($('pl-title'), entry.type === 'video' ? 'Video Story' : 'Audio Story');
  setText($('pl-sub'), (entry.prompt || '').slice(0, 80));
  var vc = $('vid-status'); if (vc) vc.classList.add('hidden');
  var vw = $('vid-player-wrap'); if (vw) vw.classList.add('hidden');
  if (entry.audioUrl) {
    audio.src = entry.audioUrl;
    audio.play().catch(function() {});
  }
}

function togglePlay() {
  if (!audio.src) return;
  audio.paused ? audio.play().catch(function() {}) : audio.pause();
}

function seekRel(sec) {
  if (!audio.duration) return;
  audio.currentTime = clamp(audio.currentTime + sec, 0, audio.duration);
}

function onPlay() {
  var ip = $('pl-ic-play'); if (ip) ip.classList.add('hidden');
  var ipa = $('pl-ic-pause'); if (ipa) ipa.classList.remove('hidden');
  var pc = $('pl-circle'); if (pc) pc.classList.add('on');
}

function onPause() {
  var ip = $('pl-ic-play'); if (ip) ip.classList.remove('hidden');
  var ipa = $('pl-ic-pause'); if (ipa) ipa.classList.add('hidden');
  var pc = $('pl-circle'); if (pc) pc.classList.remove('on');
}

function onEnded() {
  onPause();
  var sk = $('pl-seek'); if (sk) sk.value = 0;
}

function onTimeUpdate() {
  if (!audio.duration) return;
  var sk = $('pl-seek'); if (sk) sk.value = (audio.currentTime / audio.duration) * 100;
  setText($('pl-cur'), fmtTime(audio.currentTime));
}

function onMeta() {
  setText($('pl-dur'), fmtTime(audio.duration));
}

// ─── Video Polling ───
function showVideoStatus(status) {
  var card = $('vid-status');
  var title = $('vid-status-title');
  var desc = $('vid-status-desc');
  var icon = $('vid-status-icon');
  var wrap = $('vid-player-wrap');

  if (status === 'processing') {
    if (card) card.classList.remove('hidden');
    if (wrap) wrap.classList.add('hidden');
    setText(title, 'Processing\u2026');
    setText(desc, 'Video is being generated');
    if (icon) icon.innerHTML = '<div class="spin-sm"></div>';
  } else if (status === 'completed') {
    if (card) card.classList.add('hidden');
    if (wrap) wrap.classList.remove('hidden');
  } else if (status === 'failed') {
    if (card) card.classList.remove('hidden');
    if (wrap) wrap.classList.add('hidden');
    setText(title, 'Failed');
    setText(desc, 'Balance refunded');
    if (icon) icon.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#FF5252" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>';
  }
}

function startVideoPoll(videoId) {
  stopVideoPoll();
  S.videoPoll = setInterval(function() {
    apiCall('/generate?videoId=' + encodeURIComponent(videoId))
      .then(function(d) {
        if (d.status === 'completed' && d.videoUrl) {
          stopVideoPoll();
          showVideoStatus('completed');
          var vel = $('vid-el'); if (vel) vel.src = d.videoUrl;
          toast('Video ready!');
          syncUser();
        } else if (d.status === 'failed') {
          stopVideoPoll();
          showVideoStatus('failed');
          syncUser();
        }
      }).catch(function() {});
  }, 5000);
}

function stopVideoPoll() {
  if (S.videoPoll) { clearInterval(S.videoPoll); S.videoPoll = null; }
}

// ─── Journal ───
function renderJournal() {
  var targets = [$('journal-home'), $('journal-full')];
  targets.forEach(function(container) {
    if (!container) return;
    if (!S.journal.length) {
      container.innerHTML = '<div class="journal-empty">No stories yet</div>';
      return;
    }
    container.innerHTML = S.journal.map(function(entry) {
      var isVideo = entry.type === 'video';
      var d = entry.date ? new Date(entry.date) : null;
      var dateStr = d ? d.toLocaleDateString() : '';
      return '<button class="ji" type="button" data-ji="' + (entry.id || '') + '">' +
        '<div class="ji-icon ' + (isVideo ? 'ji-video' : 'ji-audio') + '">' + (isVideo ? '🎬' : '🎧') + '</div>' +
        '<div class="ji-body"><span class="ji-title">' + escHtml((entry.prompt || 'Story').slice(0, 60)) + '</span>' +
        '<span class="ji-date">' + escHtml(dateStr) + ' · ' + (isVideo ? 'Video' : 'Audio') + '</span></div></button>';
    }).join('');

    delegate(container, 'click', '.ji', function() {
      var id = this.getAttribute('data-ji');
      var entry = S.journal.find(function(j) { return j.id === id; });
      if (entry) {
        playFull(entry);
        var tp = $('tab-player'); if (tp) tp.style.display = '';
        navigate('player');
      }
    });
  });
}

function escHtml(s) {
  var d = document.createElement('div');
  d.textContent = s;
  return d.innerHTML;
}

// ─── Init ───
function init() {
  S.journal = jsonParse(localStorage.getItem('rt_journal'), []);

  audio.addEventListener('timeupdate', onTimeUpdate);
  audio.addEventListener('loadedmetadata', onMeta);
  audio.addEventListener('ended', onEnded);
  audio.addEventListener('play', onPlay);
  audio.addEventListener('pause', onPause);

  on($('pl-seek'), 'input', function(e) {
    if (audio.duration) audio.currentTime = (e.target.value / 100) * audio.duration;
  });

  on($('gen-prompt'), 'input', function() {
    setText($('gen-count'), this.value.length);
  });

  var tuAmt = $('tu-amount');
  var tuSlider = $('tu-slider');
  on(tuAmt, 'input', function() {
    var v = clamp(parseInt(tuAmt.value) || 10, 10, 500);
    tuSlider.value = v;
    updateTuLabel(v);
  });
  on(tuSlider, 'input', function() {
    tuAmt.value = tuSlider.value;
    updateTuLabel(parseInt(tuSlider.value));
  });

  on($('av-input'), 'change', onAvatarFile);
  on($('av-drop'), 'click', function() {
    var fi = $('av-input'); if (fi) fi.click();
  });

  delegate(document, 'click', '[data-goto]', function() {
    navigate(this.getAttribute('data-goto'));
  });

  delegate(document, 'click', '[data-tab]', function() {
    navigate(this.getAttribute('data-tab'));
  });

  delegate($('mood-row'), 'click', '.mood-chip', function() {
    $$('.mood-chip').forEach(function(c) { c.classList.remove('active'); });
    this.classList.add('active');
    S.mood = this.getAttribute('data-mood');
  });

  delegate($('fmt-list'), 'click', '.fmt-item', function() {
    var self = this;
    $$('.fmt-item').forEach(function(f) { f.classList.toggle('active', f === self); });
    S.fmt = this.getAttribute('data-fmt');
  });

  on($('bal-btn'), 'click', openTopUp);
  on($('scard'), 'click', openTopUp);
  on($('btn-rec'), 'click', toggleRecord);
  on($('btn-submit-voice'), 'click', submitVoice);
  on($('btn-submit-avatar'), 'click', submitAvatar);
  on($('btn-generate'), 'click', generateStory);
  on($('unlock-audio'), 'click', function() { unlockStory('audio'); });
  on($('unlock-video'), 'click', function() { unlockStory('video'); });
  on($('btn-topup'), 'click', processTopUp);
  on($('pl-playbtn'), 'click', togglePlay);
  on($('pl-rew'), 'click', function() { seekRel(-15); });
  on($('pl-ffw'), 'click', function() { seekRel(15); });

  delegate(document, 'click', '.modal-bg', closeModal);

  syncUser();
  renderJournal();
}

document.addEventListener('DOMContentLoaded', init);

})();
