// tweaks-app.jsx (Ghost-theme copy)
// ─────────────────────────────────────────────────────────────────
// Mounts the Tweaks panel inside a real Ghost render. Unlike the
// project-preview copy in /tweaks-app.jsx, this version:
//   1. Persists state to localStorage (no file-writing host available).
//   2. Auto-mounts its own floating "Tweaks" toggle button if no
//      design-tool host responds within 600ms (so the panel is reachable
//      in production).
//   3. Reads first-paint defaults from the body class set by default.hbs
//      (which reads from @custom.* settings in Ghost Admin).
// ─────────────────────────────────────────────────────────────────

const STORAGE_KEY = 'tm.tweaks.v1';

// First-paint defaults inferred from body classes (set by default.hbs
// based on Ghost Admin custom settings).
function readDefaultsFromBody() {
  const cls = document.body.className;
  function match(prefix, fallback) {
    const m = cls.match(new RegExp('\\b' + prefix + '-([a-z0-9]+)\\b', 'i'));
    return m ? m[1] : fallback;
  }
  return {
    accent:  match('accent',  'cheshire'),
    glow:    match('glow',    'standard'),
    density: match('density', 'corridor'),
    italic:  match('italic',  'full'),
    chain:   match('chain',   'on'),
    lantern: /\blantern-on\b/.test(cls)
  };
}

function readStored() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    const obj = JSON.parse(raw);
    return obj && typeof obj === 'object' ? obj : null;
  } catch (_) { return null; }
}

function writeStored(obj) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(obj)); } catch (_) {}
}

// Defaults: Ghost Admin (body class) overlaid with visitor's localStorage.
const TWEAK_DEFAULTS = Object.assign(
  readDefaultsFromBody(),
  readStored() || {}
);

function applyTweaks(t) {
  const b = document.body;
  for (const c of [...b.classList]) {
    if (/^(accent|glow|density|italic|chain)-/.test(c) || c === 'lantern-on') {
      b.classList.remove(c);
    }
  }
  b.classList.add('accent-'  + t.accent);
  b.classList.add('glow-'    + t.glow);
  b.classList.add('density-' + t.density);
  b.classList.add('italic-'  + t.italic);
  b.classList.add('chain-'   + t.chain);
  if (t.lantern) b.classList.add('lantern-on');
}

// Apply on load so first paint matches state
applyTweaks(TWEAK_DEFAULTS);

// Cursor lantern coordinate writer
(function mountLantern() {
  if (document.querySelector('.cursor-lantern')) return;
  const el = document.createElement('div');
  el.className = 'cursor-lantern';
  document.body.appendChild(el);
  let raf = 0, lx = window.innerWidth / 2, ly = window.innerHeight / 2;
  function flush() {
    document.documentElement.style.setProperty('--lx', lx + 'px');
    document.documentElement.style.setProperty('--ly', ly + 'px');
    raf = 0;
  }
  document.addEventListener('pointermove', (e) => {
    lx = e.clientX; ly = e.clientY;
    if (!raf) raf = requestAnimationFrame(flush);
  });
  flush();
})();


// ── Fallback toggle ───────────────────────────────────────────────
// In production Ghost there is no design-tool toolbar to send the
// __activate_edit_mode message. We mount a small floating button that
// dispatches the message ourselves. The button hides itself if a host
// announces availability (i.e., we're in the design tool).
function mountFallbackToggle() {
  if (document.getElementById('tm-tweaks-toggle')) return;

  // CSS for the floating button — scoped, minimal.
  const css = document.createElement('style');
  css.textContent = `
    .tm-tweaks-toggle{
      position:fixed; right:18px; bottom:18px; z-index:2147483645;
      display:inline-flex; align-items:center; gap:8px;
      padding:10px 14px;
      background: color-mix(in oklab, var(--ink-floor, #131a28) 92%, transparent);
      border: 1px solid var(--cheshire, #7ad7df);
      color: var(--cheshire, #7ad7df);
      font: 600 10.5px/1 "JetBrains Mono", ui-monospace, monospace;
      letter-spacing: 0.22em;
      text-transform: uppercase;
      border-radius: 2px;
      cursor: pointer;
      backdrop-filter: blur(6px) saturate(150%);
      -webkit-backdrop-filter: blur(6px) saturate(150%);
      transition: background .15s, color .15s, transform .15s;
      box-shadow: 0 0 18px color-mix(in oklch, var(--cheshire, #7ad7df) 18%, transparent);
    }
    .tm-tweaks-toggle:hover{
      background: var(--cheshire, #7ad7df);
      color: var(--ink-void, #0d1320);
    }
    .tm-tweaks-toggle .dot{
      width:6px; height:6px; border-radius:50%;
      background: currentColor; box-shadow: 0 0 8px currentColor;
    }
    .tm-tweaks-toggle[data-open="true"]{ display:none }
  `;
  document.head.appendChild(css);

  const btn = document.createElement('button');
  btn.id = 'tm-tweaks-toggle';
  btn.className = 'tm-tweaks-toggle';
  btn.type = 'button';
  btn.setAttribute('aria-label', 'Open Tweaks panel');
  btn.innerHTML = '<span class="dot"></span><span>Tweaks</span>';
  btn.addEventListener('click', () => {
    btn.setAttribute('data-open', 'true');
    window.postMessage({ type: '__activate_edit_mode' }, '*');
  });
  document.body.appendChild(btn);

  // Hide button when panel is dismissed (so it re-appears)
  window.addEventListener('message', (e) => {
    const t = e?.data?.type;
    if (t === '__deactivate_edit_mode' || t === '__edit_mode_dismissed') {
      btn.removeAttribute('data-open');
    }
    // NOTE: we intentionally do NOT self-remove on __edit_mode_available.
    // The panel itself posts that message to window.parent on mount, and
    // on a non-iframed Ghost site parent === self — so the message would
    // land here and remove our only way to open the panel. On a real
    // Ghost page the visitor needs this button; in the design tool the
    // toolbar drives activation directly and the duplicate button is
    // harmless.
  });
}


// ── useTweaksLocal ───────────────────────────────────────────────
// Cache-only state hook. The shared useTweaks (from tweaks-panel.jsx)
// posts __edit_mode_set_keys to window.parent so a design-tool host can
// persist values to disk — meaningless on a real Ghost site and a
// theoretical leak surface, so we override it here with a strictly
// localStorage-backed version. Nothing leaves the visitor's browser.
function useTweaksLocal(defaults) {
  const [values, setValues] = React.useState(defaults);
  const setTweak = React.useCallback((keyOrEdits, val) => {
    const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null
      ? keyOrEdits : { [keyOrEdits]: val };
    setValues((prev) => ({ ...prev, ...edits }));
  }, []);
  return [values, setTweak];
}


function App() {
  const [t, setTweakInner] = useTweaksLocal(TWEAK_DEFAULTS);

  // Wrap setTweak to also persist to localStorage.
  const setTweak = React.useCallback((keyOrObj, val) => {
    setTweakInner(keyOrObj, val);
  }, [setTweakInner]);

  React.useEffect(() => {
    applyTweaks(t);
    writeStored(t);
  }, [t]);

  const accentSwatches = {
    cheshire: ['#7ad7df', '#3f888d'],
    chain:    ['#e368b0', '#9b3d80'],
    maze:     ['#a78cdf', '#6f5aa6'],
    tea:      ['#e3c581', '#9e8a5a']
  };

  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Theme" />
      <TweakColor
        label="Accent palette"
        value={accentSwatches[t.accent]}
        options={Object.values(accentSwatches)}
        onChange={(pair) => {
          const key = Object.keys(accentSwatches)
            .find((k) => accentSwatches[k][0] === pair[0]);
          if (key) setTweak('accent', key);
        }}
      />
      <TweakRadio
        label="The chain"
        value={t.chain}
        options={['on', 'off']}
        onChange={(v) => setTweak('chain', v)}
      />

      <TweakSection label="Light" />
      <TweakRadio
        label="Glow"
        value={t.glow}
        options={['quiet', 'standard', 'lanternlit']}
        onChange={(v) => setTweak('glow', v)}
      />
      <TweakToggle
        label="Cursor lantern"
        value={t.lantern}
        onChange={(v) => setTweak('lantern', v)}
      />

      <TweakSection label="Reading" />
      <TweakRadio
        label="Density"
        value={t.density}
        options={['corridor', 'library']}
        onChange={(v) => setTweak('density', v)}
      />
      <TweakRadio
        label="Italic display"
        value={t.italic}
        options={['full', 'soft', 'off']}
        onChange={(v) => setTweak('italic', v)}
      />
    </TweaksPanel>
  );
}

function __mountTweaks() {
  let host = document.getElementById('tweaks-root');
  if (!host) {
    host = document.createElement('div');
    host.id = 'tweaks-root';
    document.body.appendChild(host);
  }
  ReactDOM.createRoot(host).render(<App />);
  // Mount the fallback toggle button (it will self-remove if a real
  // design-tool host announces itself).
  mountFallbackToggle();
}
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', __mountTweaks);
} else {
  __mountTweaks();
}
