/* Tweaks panel — MD3 light-mode tokens, full propagation */

/* ── Color math ────────────────────────────────────────── */
function hexToHsl(hex) {
  const r = parseInt(hex.slice(1,3),16)/255;
  const g = parseInt(hex.slice(3,5),16)/255;
  const b = parseInt(hex.slice(5,7),16)/255;
  const max = Math.max(r,g,b), min = Math.min(r,g,b);
  let h=0, s=0, l=(max+min)/2;
  if (max !== min) {
    const d = max-min;
    s = l>0.5 ? d/(2-max-min) : d/(max+min);
    switch(max) {
      case r: h=((g-b)/d+(g<b?6:0))/6; break;
      case g: h=((b-r)/d+2)/6; break;
      case b: h=((r-g)/d+4)/6; break;
    }
  }
  return [h*360, s*100, l*100];
}

function hslToHex(h, s, l) {
  h/=360; s/=100; l/=100;
  const q = l<0.5 ? l*(1+s) : l+s-l*s, p=2*l-q;
  const hue2rgb = (p,q,t) => {
    if(t<0) t+=1; if(t>1) t-=1;
    if(t<1/6) return p+(q-p)*6*t;
    if(t<1/2) return q;
    if(t<2/3) return p+(q-p)*(2/3-t)*6;
    return p;
  };
  return '#' + [hue2rgb(p,q,h+1/3), hue2rgb(p,q,h), hue2rgb(p,q,h-1/3)]
    .map(x => Math.round(x*255).toString(16).padStart(2,'0')).join('');
}

/* deriveTonal — proportional L scaling so input color lands exactly at baseStop.
   satCap clamps raw saturation before the per-stop taper (use ~14 for neutral palette). */
function deriveTonal(hex, baseStop = 40, satCap = 100) {
  if (!hex || hex.length < 7) return {};
  const [h, s, inputL] = hexToHsl(hex);
  const effS = Math.min(s, satCap);
  /* MD3 tonal stop → perceptual lightness mapping */
  const SL = { 0:0, 10:6, 20:13, 30:22, 40:32, 50:44, 60:58, 70:70, 80:80, 90:90, 92:92, 94:94, 95:95, 96:96, 99:99, 100:100 };
  const targetL = SL[baseStop];
  const result = {};
  Object.entries(SL).forEach(([stop, stopL]) => {
    stop = Number(stop);
    if (stop === 0)   { result[0]   = '#000000'; return; }
    if (stop === 100) { result[100] = '#ffffff'; return; }
    /* scale proportionally: input placed at baseStop, 0→black, 99→white */
    const adj = Math.min(99, Math.max(1,
      stopL <= targetL
        ? (targetL > 0 ? (stopL / targetL) * inputL : 0)
        : inputL + ((stopL - targetL) / (99 - targetL)) * (99 - inputL)
    ));
    /* smooth sat taper toward white at high stops, preserve punch at dark stops */
    const satF = adj > 80 ? 0.30 + (0.70 * (99 - adj) / 19) : adj < 20 ? 0.65 : 1;
    result[stop] = hslToHex(h, effS * satF, adj);
  });
  return result;
}

window.hexToHsl    = hexToHsl;
window.deriveTonal = deriveTonal;

/* ── Seed → full colour scheme ─────────────────────────── */
/* Triadic split: secondary = seed+120°, tertiary = seed+180° (complementary warm) */
function seedToScheme(seed) {
  if (!seed || seed.length < 7) seed = '#1A2D6B';
  const [h, s, l] = hexToHsl(seed);
  return {
    primary:   seed,
    /* triadic +120° — gives rose/pink from sapphire, gold from green, etc. */
    secondary: hslToHex((h + 120) % 360, Math.min(s, 70), l),
    /* complementary +180° at lighter stop — champagne/warm gold effect */
    tertiary:  hslToHex((h + 180) % 360, Math.min(s * 0.55, 40), 76),
    /* neutral: same hue, very low chroma — warm near-neutral */
    neutral:   hslToHex(h, 8, 91),
    /* surface: near-white seed tint */
    ivory:     hslToHex(h, 3, 97),
  };
}

window.seedToScheme = seedToScheme;

/* ── Apply tokens (light mode only) ─────────────────────── */
function applyTokens(t) {
  if (!t?.seed) return;
  const root = document.documentElement;
  const css = (k,v) => root.style.setProperty(k,v);

  const scheme = seedToScheme(t.seed);
  /* Manual overrides — empty string means "auto-derive from seed" */
  const effectiveSecondary = t.secondary || scheme.secondary;
  const effectiveBg        = t.bg        || scheme.ivory;

  const sap   = deriveTonal(scheme.primary,    40);
  const rose  = deriveTonal(effectiveSecondary, 40);
  const champ = deriveTonal(scheme.tertiary,   80);
  /* Neutral: cap saturation at 14 so seed hue doesn't overpower at dark stops */
  const neu   = deriveTonal(scheme.neutral, 95, 14);

  const palStops = [10,20,30,40,50,60,70,80,90,92,94,95,96,99];
  palStops.forEach(s => {
    css(`--zo-sapphire-${s}`, sap[s]);
    css(`--zo-rose-${s}`,     rose[s]);
    css(`--zo-champ-${s}`,    champ[s]);
    css(`--zo-neutral-${s}`,  neu[s]);
  });
  css('--zo-sapphire',  sap[40]);
  css('--zo-rose-gold', rose[40]);
  css('--zo-champagne', champ[80]);
  css('--zo-ivory',     effectiveBg);
  css('--zo-cream',     scheme.neutral);

  /* ── MD3 semantic tokens — light mode, per spec ── */
  /* Primary  P-40 / P-100 / P-90 / P-10 */
  css('--md-primary',                  sap[40]);
  css('--md-on-primary',               '#FFFFFF');
  css('--md-primary-container',        sap[90]);
  css('--md-on-primary-container',     sap[10]);
  /* Secondary  P-40 / P-100 / P-90 / P-10 */
  css('--md-secondary',                rose[40]);
  css('--md-on-secondary',             '#FFFFFF');
  css('--md-secondary-container',      rose[90]);
  css('--md-on-secondary-container',   rose[10]);
  /* Tertiary  P-40 / P-100 / P-90 / P-10 */
  css('--md-tertiary',                 champ[40]);
  css('--md-on-tertiary',              '#FFFFFF');
  css('--md-tertiary-container',       champ[90]);
  css('--md-on-tertiary-container',    champ[10]);
  /* Error — fixed per MD3 */
  css('--md-error',                    '#BA1A1A');
  css('--md-on-error',                 '#FFFFFF');
  css('--md-error-container',          '#FFDAD6');
  css('--md-on-error-container',       '#410002');
  /* Background / Surface  N-99 / N-10 */
  css('--md-background',               effectiveBg);
  css('--md-on-background',            neu[10]);
  css('--md-surface',                  effectiveBg);
  css('--md-on-surface',               neu[10]);
  /* Surface variant  N-90 / N-30 */
  css('--md-surface-variant',          neu[90]);
  css('--md-on-surface-variant',       neu[30]);
  /* Surface container hierarchy  N-100 / N-96 / N-94 / N-92 / N-90 */
  css('--md-surface-container-lowest', '#FFFFFF');
  css('--md-surface-container-low',    neu[96]);
  css('--md-surface-container',        neu[94]);
  css('--md-surface-container-high',   neu[92]);
  css('--md-surface-container-highest',neu[90]);
  /* Outline  N-50 / N-80 */
  css('--md-outline',                  neu[50]);
  css('--md-outline-variant',          neu[80]);
  /* Inverse  N-20 / N-95 / P-80 */
  css('--md-inverse-surface',          neu[20]);
  css('--md-inverse-on-surface',       neu[95]);
  css('--md-inverse-primary',          sap[80]);

  if (t.displayFont) css('--font-display', `"${t.displayFont}", Georgia, serif`);
  if (t.bodyFont)    css('--font-body',    `"${t.bodyFont}", -apple-system, sans-serif`);

  if (typeof t.radiusMd === 'number') {
    const r = t.radiusMd;
    css('--shape-xs',  Math.max(0, r - 8) + 'px');
    css('--shape-sm',  Math.max(0, r - 4) + 'px');
    css('--shape-md',  r + 'px');
    css('--shape-lg',  (r + 8) + 'px');
    css('--shape-xl',  (r + 16) + 'px');
    css('--shape-2xl', (r + 24) + 'px');
  }

  root.setAttribute('data-theme', 'light');
  root.removeAttribute('data-seed');

  window.__ZO_LIVE_TWEAKS = t;
  document.dispatchEvent(new CustomEvent('zo:tweaks', { detail: t }));
}

/* ── ZocaneTweaks ─────────────────────────────────────── */
function ZocaneTweaks() {
  const [t, setTweak] = useTweaks(window.__ZO_TWEAK_DEFAULTS);

  React.useEffect(() => applyTokens(t), [t]);

  React.useEffect(() => {
    const families = [t?.displayFont, t?.bodyFont].filter(Boolean);
    if (!families.length) return;
    const id = 'zo-tweak-fonts';
    let link = document.getElementById(id);
    if (!link) { link = document.createElement('link'); link.id = id; link.rel = 'stylesheet'; document.head.appendChild(link); }
    link.href = `https://fonts.googleapis.com/css2?${
      families.map(f=>`family=${encodeURIComponent(f)}:ital,wght@0,400;0,500;0,600;0,700;1,400`).join('&')
    }&display=swap`;
  }, [t?.displayFont, t?.bodyFont]);

  const scheme = seedToScheme(t?.seed);
  const effSec = t.secondary || scheme.secondary;
  const effBg  = t.bg        || scheme.ivory;

  const strip = [
    { label: 'Primary',   color: scheme.primary },
    { label: 'Secondary', color: effSec         },
    { label: 'Tertiary',  color: scheme.tertiary},
    { label: 'Neutral',   color: scheme.neutral },
    { label: 'Surface',   color: effBg          },
  ];

  const resetRow = (label, key) => (
    <div style={{ display:'flex', alignItems:'center', gap:4 }}>
      <div style={{ flex:1 }}>{label}</div>
      {t[key] && (
        <span
          onClick={() => setTweak(key, '')}
          style={{ fontSize:10, color:'var(--md-on-surface-variant)', cursor:'pointer',
                   padding:'1px 5px', borderRadius:4, border:'1px solid var(--md-outline-variant)' }}
          title="Reset to auto"
        >↺ auto</span>
      )}
    </div>
  );

  return (
    <TweaksPanel title="Zocane">

      <div style={{ display:'flex', gap:3, height:20, borderRadius:6, overflow:'hidden', marginBottom:4 }}>
        {strip.map(({label,color}) => (
          <div key={label} title={label} style={{ flex:1, background:color, transition:'background 300ms' }}/>
        ))}
      </div>

      <TweakSection label="Theme">
        <TweakSlider
          label="Radius"
          value={t.radiusMd}
          onChange={v => setTweak('radiusMd', v)}
          min={2} max={28} step={1} unit="px"
        />
      </TweakSection>

      <TweakSection label="Colors">
        <TweakColor
          label="Seed"
          value={t.seed}
          onChange={v => setTweak('seed', v)}
          options={['#1A2D6B','#B76E79','#7C3AED','#C62828','#1B5E20','#006064','#4E342E','#1565C0']}
        />
        <TweakColor
          label={resetRow('Secondary', 'secondary')}
          value={effSec}
          onChange={v => setTweak('secondary', v)}
          options={['#B76E79','#E74C3C','#E67E22','#27AE60','#2980B9','#8E44AD','#16A085','#D35400']}
        />
        <TweakColor
          label={resetRow('Background', 'bg')}
          value={effBg}
          onChange={v => setTweak('bg', v)}
          options={['#FFFFFF','#FBF8F3','#F4EDE0','#EDE0C8','#E8DCC4','#E8D5B0','#DDD0B8','#F5E6C8']}
        />
      </TweakSection>

      <TweakSection label="Type">
        <TweakSelect
          label="Display"
          value={t.displayFont}
          onChange={v => setTweak('displayFont', v)}
          options={['Bricolage Grotesque','Fraunces','Instrument Serif','Playfair Display','DM Serif Display','Cormorant Garamond','Space Grotesk','Manrope']}
        />
        <TweakSelect
          label="Body"
          value={t.bodyFont}
          onChange={v => setTweak('bodyFont', v)}
          options={['Inter','Manrope','DM Sans','Work Sans','Plus Jakarta Sans','IBM Plex Sans','Outfit']}
        />
      </TweakSection>

      <div style={{ paddingTop: 4 }}>
        <TweakButton onClick={() => {
          const d = window.__ZO_TWEAK_DEFAULTS;
          Object.keys(d).forEach(k => setTweak(k, d[k]));
        }}>Reset defaults</TweakButton>
      </div>

    </TweaksPanel>
  );
}

window.ZocaneTweaks = ZocaneTweaks;
