// app.jsx — Color Shades tool
const { useState, useEffect, useMemo, useRef, useCallback } = React;
const CU = window.ColorUtil;

// ─── TWEAK DEFAULTS ──────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "algorithm": "oklch",
  "steps": 19,
  "tint": 0.5,
  "tintSteps": 10,
  "basePos": 3,
  "layout": "horizontal",
  "tintLayout": "horizontal",
  "dark": false,
  "showLabels": true
}/*EDITMODE-END*/;

// ─── helpers ─────────────────────────────────────────────────────────────────
const fmtHsl = h => `hsl(${Math.round(h.h)}, ${Math.round(h.s)}%, ${Math.round(h.l)}%)`;
const fmtRgb = r => `rgb(${Math.round(r.r)}, ${Math.round(r.g)}, ${Math.round(r.b)})`;
const fmtOklch = o => `oklch(${(o.L * 100).toFixed(1)}% ${o.C.toFixed(3)} ${o.H.toFixed(1)})`;
const fmtCmyk = c => `cmyk(${Math.round(c.c)}%, ${Math.round(c.m)}%, ${Math.round(c.y)}%, ${Math.round(c.k)}%)`;

function randomHex() {
  const H = Math.random() * 360;
  const C = 0.10 + Math.random() * 0.20;  // 0.10–0.30 — насыщеннее
  const L = 0.36 + Math.random() * 0.38;  // 0.36–0.74 — шире диапазон яркости
  return CU.rgbToHex(CU.oklchToRgb({ L, C, H }));
}

function colorName(hex) {
  return CU.closestNamed(hex).name.replace(/([A-Z])/g, ' $1').trim();
}

function useCopy() {
  const [copied, setCopied] = useState(null);
  const tRef = useRef();
  const copy = useCallback((text, tag) => {
    navigator.clipboard?.writeText(text);
    setCopied(tag || text);
    clearTimeout(tRef.current);
    tRef.current = setTimeout(() => setCopied(null), 1200);
  }, []);
  return [copied, copy];
}

// Derive an accent from base HEX using OKLCH so it behaves nicely in both themes
function deriveAccents(hex, dark) {
  const rgb = CU.hexToRgb(hex);
  const { C, H } = CU.rgbToOklch(rgb);
  const primary = CU.rgbToHex(CU.oklchToRgb({ L: dark ? 0.72 : 0.58, C: Math.min(C, 0.18), H }));
  const softBg  = CU.rgbToHex(CU.oklchToRgb({ L: dark ? 0.18 : 0.97, C: Math.min(C, 0.05), H }));
  const ring    = CU.rgbToHex(CU.oklchToRgb({ L: dark ? 0.65 : 0.62, C: Math.min(C, 0.15), H }));
  const fg      = CU.rgbToHex(CU.oklchToRgb({ L: dark ? 0.96 : 0.14, C: 0, H: 0 }));
  const bg      = dark ? '#0B0B0C' : '#FFFFFF';
  const muted   = dark ? '#18181B' : '#FAFAFA';
  const border  = dark ? '#27272A' : '#E4E4E7';
  const mutedFg = dark ? '#A1A1AA' : '#71717A';
  return { primary, softBg, ring, fg, bg, muted, border, mutedFg };
}

// ─── App ─────────────────────────────────────────────────────────────────────
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [colors, setColors] = useState(() => {
    const mkId = () => 'c' + Math.random().toString(36).slice(2, 8);
    const mk = hex => ({ hex, name: colorName(hex), locked: false });
    return [
      { id: 'c1',   ...mk('#E8092A') },
      { id: mkId(), ...mk(randomHex()) },
      { id: mkId(), ...mk(randomHex()) },
      { id: mkId(), ...mk(randomHex()) },
      { id: mkId(), ...mk(randomHex()) },
    ];
  });
  const [activeId, setActiveId] = useState('c1');
  const activeIdx = Math.max(0, colors.findIndex(c => c.id === activeId));
  const active = colors[activeIdx] || colors[0];
  const [hexInput, setHexInput] = useState(active.hex);
  const baseHex = active.hex;
  const [selectedIdx, setSelectedIdx] = useState(null);
  const [detailIdx, setDetailIdx] = useState(null);
  const [exportOpen, setExportOpen] = useState(false);
  const [activeTab, setActiveTab] = useState('shades');
  const handleTabChange = (tab) => {
    if (tab === 'shades') setHexInput(active.hex);
    setActiveTab(tab);
  };
  const [copied, copy] = useCopy();
  const [toast, setToast] = useState(null);
  const toastTimer = useRef();
  const showToast = (msg) => {
    setToast(msg);
    clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToast(null), 3000);
  };

  // when active color switches, sync hex input
  useEffect(() => {
    setHexInput(active.hex);
    setSelectedIdx(null);
  }, [activeId]);

  // validate & commit hex back into the active color
  useEffect(() => {
    const v = CU.normalizeHex(hexInput);
    if (v && v !== active.hex) {
      setColors(cs => cs.map(c => c.id === activeId ? { ...c, hex: v } : c));
    }
  }, [hexInput]);

  const MAX_COLORS = 9;
  const addColor = () => {
    if (colors.length >= MAX_COLORS) {
      showToast('Максимум 9 цветов в палитре');
      return;
    }
    const id = 'c' + Math.random().toString(36).slice(2, 8);
    const hex = randomHex();
    setColors(cs => [...cs, { id, name: colorName(hex), hex, locked: false }]);
    setActiveId(id);
  };
  const addColorAt = (afterIdx) => {
    if (colors.length >= MAX_COLORS) {
      showToast('Максимум 9 цветов в палитре');
      return;
    }
    const id = 'c' + Math.random().toString(36).slice(2, 8);
    const hex = randomHex();
    setColors(cs => {
      const n = [...cs];
      n.splice(afterIdx + 1, 0, { id, name: colorName(hex), hex, locked: false });
      return n;
    });
  };
  const removeColor = (id) => {
    setColors(cs => {
      if (cs.length <= 1) return cs;
      const next = cs.filter(c => c.id !== id);
      if (id === activeId) setActiveId(next[0].id);
      return next;
    });
  };
  const renameColor = (id, name) => {
    setColors(cs => cs.map(c => c.id === id ? { ...c, name } : c));
  };
  const toggleLock = (id) => setColors(cs => cs.map(c => c.id === id ? { ...c, locked: !c.locked } : c));
  const generate = () => setColors(cs => cs.map(c => {
    if (c.locked) return c;
    const hex = randomHex();
    return { ...c, hex, name: colorName(hex) };
  }));
  const editColorHex = (id, hex) => setColors(cs => cs.map(c =>
    c.id === id ? { ...c, hex, name: colorName(hex) } : c
  ));

  const shades = useMemo(
    () => CU.generateShades(baseHex, t.steps, t.algorithm, t.tint, t.basePos),
    [baseHex, t.steps, t.algorithm, t.tint, t.basePos]
  );

  const accents = useMemo(() => deriveAccents(baseHex, t.dark), [baseHex, t.dark]);

  // apply theme vars to root
  useEffect(() => {
    const r = document.documentElement;
    Object.entries(accents).forEach(([k, v]) => r.style.setProperty(`--${k}`, v));
    r.style.setProperty('color-scheme', t.dark ? 'dark' : 'light');
    document.body.className = t.dark ? 'dark' : 'light';
  }, [accents, t.dark]);

  const hexValid = !!CU.normalizeHex(hexInput);

  const selected = selectedIdx != null ? shades[selectedIdx] : null;

  return (
    <div className="page">
      <TopBar
        baseHex={baseHex}
        dark={t.dark}
        onToggleDark={() => setTweak('dark', !t.dark)}
        onExport={() => setExportOpen(true)}
        activeTab={activeTab}
        onTabChange={handleTabChange}
      />

      {activeTab === 'ui' ? (
      <UICheckView
        colors={colors}
        activeId={activeId}
        onSelect={setActiveId}
        onAdd={addColor}
        onRemove={removeColor}
        onRename={renameColor}
        hexInput={hexInput}
        setHexInput={setHexInput}
        hexValid={hexValid}
        dark={t.dark}
        tweaks={t}
      />
      ) : activeTab === 'shades' ? (
      <main className="main">
        <ColorTabs
          colors={colors}
          activeId={activeId}
          onSelect={setActiveId}
          onAdd={addColor}
          onRemove={removeColor}
          onRename={renameColor}
          hexInput={hexInput}
          setHexInput={setHexInput}
          hexValid={hexValid}
        />

        <section className="panel controls">
          <div className="controls-head">
            <div>
              <h2 className="h-title">Shades · {active.name}</h2>
            </div>
            <div className="controls-head-actions">
              <LayoutSwitcher value={t.layout} onChange={v => setTweak('layout', v)} />
              <StepsControl
                value={t.steps}
                onChange={v => {
                  setTweak('steps', v);
                  if (t.basePos > v - 1) setTweak('basePos', v - 1);
                }}
                basePos={t.basePos}
                onBasePosChange={v => setTweak('basePos', v)}
              />
            </div>
          </div>

          <AlgoSegmented value={t.algorithm} onChange={v => setTweak('algorithm', v)} />

          <ShadeStrip
            shades={shades}
            baseHex={baseHex}
            selectedIdx={selectedIdx}
            onSelect={i => setSelectedIdx(i)}
            onCopy={(s, idx) => { copy(s.hex, `strip-${idx}`); setSelectedIdx(idx); }}
            onDetails={i => setDetailIdx(i)}
            copiedTag={copied}
            showLabels={t.showLabels}
            layout={t.layout}
          />

          <SelectedCard shade={selected} idx={selectedIdx} onCopy={copy} copiedTag={copied}
            onDetails={() => selectedIdx != null && setDetailIdx(selectedIdx)} />
        </section>

        <TintsPanel
          sourceHex={selected?.hex || baseHex}
          sourceLabel={selected ? `Stop ${selectedIdx + 1} of main palette` : 'Base color'}
          sourceIsSelected={!!selected}
          algorithm={t.algorithm}
          tint={t.tint}
          steps={t.tintSteps}
          onStepsChange={v => setTweak('tintSteps', v)}
          onTintChange={v => setTweak('tint', v)}
          onCopy={copy}
          copiedTag={copied}
          showLabels={t.showLabels}
          layout={t.tintLayout}
          onLayoutChange={v => setTweak('tintLayout', v)}
        />
      </main>
      ) : (
      <PaletteView
        colors={colors}
        onGenerate={generate}
        onToggleLock={toggleLock}
        onRemove={removeColor}
        onAddAt={addColorAt}
        onEditHex={editColorHex}
      />
      )}

      {toast && (
        <div className="toast" role="status">
          <span>{toast}</span>
          <button className="toast-close" onClick={() => setToast(null)}>×</button>
        </div>
      )}

      <footer className="site-footer">
        Made by <a href="https://turchanovich.framer.ai/" target="_blank" rel="noopener noreferrer">Ovič</a>
      </footer>

      {detailIdx != null && (
        <ShadeDetailModal
          shade={shades[detailIdx]}
          idx={detailIdx}
          total={shades.length}
          baseHex={baseHex}
          onClose={() => setDetailIdx(null)}
          onPrev={() => setDetailIdx(i => (i - 1 + shades.length) % shades.length)}
          onNext={() => setDetailIdx(i => (i + 1) % shades.length)}
          onCopy={copy}
          copiedTag={copied}
        />
      )}

      {exportOpen && (
        <ExportModal
          shades={shades}
          baseHex={baseHex}
          onClose={() => setExportOpen(false)}
          onCopy={copy}
          copiedTag={copied}
        />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Generation" />
        <TweakRadio
          label="Algorithm"
          value={t.algorithm}
          options={[
            { value: 'oklch', label: 'OKLCH' },
            { value: 'hsl', label: 'HSL' },
            { value: 'mix', label: 'Mix' },
          ]}
          onChange={v => setTweak('algorithm', v)}
        />
        <TweakSlider label="Steps" value={t.steps} min={5} max={50} step={1}
          onChange={v => {
            setTweak('steps', v);
            if (t.basePos > v - 1) setTweak('basePos', v - 1);
          }} />
        <TweakSlider label="Brand position" value={t.basePos} min={0} max={Math.max(0, t.steps - 1)} step={1}
          onChange={v => setTweak('basePos', v)} />
        <TweakSlider label="Tint density" value={t.tint} min={0} max={1} step={0.05}
          onChange={v => setTweak('tint', v)} />
        <TweakSlider label="Tint block steps" value={t.tintSteps} min={3} max={24} step={1}
          onChange={v => setTweak('tintSteps', v)} />
        <TweakRadio label="Layout" value={t.layout}
          options={[
            { value: 'horizontal', label: 'Row' },
            { value: 'vertical',   label: 'Column' },
            { value: 'grid',       label: 'Grid' },
          ]}
          onChange={v => setTweak('layout', v)} />
        <TweakSection label="Display" />
        <TweakToggle label="Dark mode" value={t.dark} onChange={v => setTweak('dark', v)} />
        <TweakToggle label="Show step labels" value={t.showLabels}
          onChange={v => setTweak('showLabels', v)} />
      </TweaksPanel>
    </div>
  );
}

function algoLabel(a) {
  return { oklch: 'OKLCH · perceptual', hsl: 'HSL · lightness', mix: 'Mix · with white & black' }[a];
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
