/* @jsx React.createElement */
// ─────────────────────────────────────────────────────────────────────
// METASPEAK Config Loader — Worker-backed
// ─────────────────────────────────────────────────────────────────────
// Fetches settings from the Cloudflare Worker API on first kiosk visit
// (or on every visit if you set FORCE_REFRESH=true). Token validation
// happens here too: invalid/expired tokens render an access-denied page
// before the rest of the kiosk boots.
//
// Set window.METASPEAK_API_URL in HTML before this script loads.

(function() {

const API_URL = window.METASPEAK_API_URL || '';
const PRESET_VERSION_KEY = 'metaspeak.presetAppliedAt';

// ── Token gate ───────────────────────────────────────────────
// Read token from URL (?key=ABC) — fall back to localStorage so a
// reload doesn't kick the user out (kiosks reload after idle).
function getToken() {
  const params = new URLSearchParams(window.location.search);
  const fromUrl = params.get('key');
  if (fromUrl) {
    try { localStorage.setItem('metaspeak.token', fromUrl); } catch {}
    return fromUrl;
  }
  try { return localStorage.getItem('metaspeak.token') || ''; } catch { return ''; }
}

function showAccessDenied(reason) {
  // Replace whole page with a simple denied screen — kiosk shouldn't
  // start renderering Three.js if user isn't authorized.
  document.body.innerHTML = `
    <div style="
      position:fixed; inset:0;
      display:flex; flex-direction:column; align-items:center; justify-content:center;
      background:#0a0e1a; color:#dfe7ff; font-family:-apple-system,system-ui,sans-serif;
      padding:40px; text-align:center;
    ">
      <div style="font-size:44px; margin-bottom:16px;">🔒</div>
      <h1 style="font-size:24px; margin:0 0 8px; font-weight:600;">Access Denied</h1>
      <p style="opacity:0.7; margin:0 0 24px; font-size:14px;">
        ${reason || 'Token tidak sah atau dah expire.'}
      </p>
      <p style="opacity:0.5; font-size:12px;">
        Hubungi admin untuk dapatkan token baru.
      </p>
    </div>
  `;
}

async function fetchSettings() {
  const token = getToken();
  if (!token) {
    showAccessDenied('No access token. URL kena ada ?key=YOUR_TOKEN');
    return null;
  }
  if (!API_URL) {
    console.warn('[ConfigLoader] METASPEAK_API_URL not set — running offline');
    return null;
  }
  try {
    const r = await fetch(`${API_URL}/api/settings?key=${encodeURIComponent(token)}`, {
      cache: 'no-cache',
    });
    if (r.status === 403) {
      const body = await r.json().catch(() => ({}));
      showAccessDenied(`Token: ${body.reason || 'denied'}`);
      return null;
    }
    if (!r.ok) {
      console.warn('[ConfigLoader] settings fetch failed:', r.status);
      return null;
    }
    const data = await r.json();
    return data.settings || null;
  } catch (e) {
    console.warn('[ConfigLoader] settings fetch error:', e.message);
    return null;
  }
}

async function applyTweaks(tweaks) {
  if (!tweaks) return;
  try {
    const existing = JSON.parse(localStorage.getItem('metaspeak-tweaks-mirror') || '{}');
    const merged = { ...existing, ...tweaks };
    localStorage.setItem('metaspeak-tweaks-mirror', JSON.stringify(merged));
  } catch (e) {
    console.warn('[ConfigLoader] tweak persist failed:', e);
  }
}

async function loadAssetsFromURLs(preset) {
  const ready = await waitForMetaspeakAPI(8000);
  if (!ready) {
    console.warn('[ConfigLoader] metaspeak API not ready, skipping asset URL loads');
    return;
  }

  const ms = window.metaspeak;
  const tasks = [];

  if (preset.char1Url) {
    tasks.push(ms.loadGLBFromURL(preset.char1Url, 'char1')
      .then(() => console.log('[ConfigLoader] char1 loaded'))
      .catch(e => console.warn('[ConfigLoader] char1 failed:', e.message)));
  }
  if (preset.char2Url) {
    tasks.push(ms.loadGLBFromURL(preset.char2Url, 'char2')
      .then(() => console.log('[ConfigLoader] char2 loaded'))
      .catch(e => console.warn('[ConfigLoader] char2 failed:', e.message)));
  }
  if (preset.envUrl) {
    tasks.push(ms.loadEnvironmentFromURL(preset.envUrl)
      .then(() => console.log('[ConfigLoader] env loaded'))
      .catch(e => console.warn('[ConfigLoader] env failed:', e.message)));
  }
  if (preset.hdriUrl) {
    tasks.push(ms.loadHDRIFromURL(preset.hdriUrl)
      .then(() => console.log('[ConfigLoader] HDRI loaded'))
      .catch(e => console.warn('[ConfigLoader] HDRI failed:', e.message)));
  }

  for (const charSlot of ['char1', 'char2']) {
    const anims = preset[charSlot + 'Animations'];
    if (!anims || typeof anims !== 'object') continue;
    for (const animSlot of Object.keys(anims)) {
      const url = anims[animSlot];
      if (!url) continue;
      tasks.push(ms.loadAnimationFromURL(charSlot, animSlot, url)
        .then(() => console.log(`[ConfigLoader] ${charSlot}.${animSlot} loaded`))
        .catch(e => console.warn(`[ConfigLoader] ${charSlot}.${animSlot} failed:`, e.message)));
    }
  }

  await Promise.allSettled(tasks);
}

function waitForMetaspeakAPI(timeoutMs) {
  return new Promise((resolve) => {
    const start = Date.now();
    const tick = () => {
      const ms = window.metaspeak;
      if (ms?.loadGLBFromURL && ms?.loadEnvironmentFromURL && ms?.loadHDRIFromURL) {
        return resolve(true);
      }
      if (Date.now() - start > timeoutMs) return resolve(false);
      setTimeout(tick, 100);
    };
    tick();
  });
}

async function tryAutoSetup() {
  // Skip in admin mode
  if (window.METASPEAK_MODE && window.METASPEAK_MODE !== 'kiosk') return;

  const preset = await fetchSettings();
  if (!preset) return;  // fetchSettings already showed denied page if needed

  console.log('[ConfigLoader] applying preset from Worker…');

  // Apply tweaks immediately
  if (preset.tweaks) await applyTweaks(preset.tweaks);

  // Load assets from URLs
  await loadAssetsFromURLs(preset);

  try { localStorage.setItem(PRESET_VERSION_KEY, String(Date.now())); } catch {}
  console.log('[ConfigLoader] preset applied');
}

// ── Polling for live updates ─────────────────────────────────
// Every 30 seconds, check if admin saved new settings. If yes, apply
// the new tweaks (without reloading assets — those are heavy).
async function startPolling() {
  if (window.METASPEAK_MODE && window.METASPEAK_MODE !== 'kiosk') return;
  setInterval(async () => {
    const preset = await fetchSettings();
    if (!preset?.tweaks) return;
    const lastApplied = localStorage.getItem(PRESET_VERSION_KEY);
    const stamp = JSON.stringify(preset.tweaks);
    const lastStamp = localStorage.getItem('metaspeak.lastTweakStamp');
    if (stamp === lastStamp) return;  // no change
    localStorage.setItem('metaspeak.lastTweakStamp', stamp);
    await applyTweaks(preset.tweaks);
    console.log('[ConfigLoader] live tweaks updated, reload to apply asset changes');
    // Soft reload — picks up new tweaks without losing token
    location.reload();
  }, 30000);
}

window.MetaspeakConfigLoader = { tryAutoSetup };

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => {
    tryAutoSetup().then(startPolling);
  });
} else {
  setTimeout(() => tryAutoSetup().then(startPolling), 0);
}

})();
