// nyashelf-app.jsx
// Top-level app: phone frame + screen router + tweaks panel + keyframes.

const NYA_KEYFRAMES = `
  @keyframes nya-twinkle {
    0%, 100% { opacity: var(--start, .35); }
    50%      { opacity: 1; }
  }
  @keyframes nya-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
  }
  @keyframes nya-bounce {
    0%, 100% { transform: translateY(0); opacity: .4; }
    50%      { transform: translateY(-6px); opacity: 1; }
  }
  @keyframes nya-fade-down {
    0% { opacity: 0; transform: translateY(-6px); }
    100% { opacity: 1; transform: translateY(0); }
  }
  @keyframes nya-fade-in {
    0% { opacity: 0; }
    100% { opacity: 1; }
  }
  @keyframes nya-slide-up {
    0% { transform: translateY(100%); }
    100% { transform: translateY(0); }
  }
  @keyframes nya-toast-in {
    0% { opacity: 0; transform: translateY(20px) scale(.95); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
  }
  @keyframes nya-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }
  @keyframes nya-spin {
    100% { transform: rotate(360deg); }
  }
  @keyframes nya-petal-fall {
    0%   { transform: translateY(-10px) rotate(0deg); opacity: 0; }
    10%  { opacity: 1; }
    100% { transform: translateY(800px) rotate(360deg); opacity: 0; }
  }
  /* Animations-off variant: disable everything inside .nya-anims-off */
  .nya-anims-off * {
    animation: none !important;
    transition: none !important;
  }

  /* nicer scrollbar inside the phone */
  .nya-scroll::-webkit-scrollbar { width: 0; height: 0; }
  .nya-scroll { scrollbar-width: none; }

  /* status bar tweak for ghost cat icon row */
  .nya-status-bar {
    position: absolute; top: 0; left: 0; right: 0; height: 47px;
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 28px; z-index: 50;
    font-family: -apple-system, system-ui;
    color: #fff;
  }
`;

function PhoneFrame({ tweaks, children, nav, fullscreen = false }) {
  const bg = `
    radial-gradient(ellipse 120% 80% at 80% 20%, ${getPaletteValue(tweaks.palette, 'bg2')}55 0%, transparent 50%),
    radial-gradient(ellipse 100% 70% at 20% 70%, ${getPaletteValue(tweaks.palette, 'bg0')}66 0%, transparent 50%),
    linear-gradient(170deg, ${getPaletteValue(tweaks.palette, 'bg1')} 0%, ${getPaletteValue(tweaks.palette, 'bg0')} 50%, ${getPaletteValue(tweaks.palette, 'bg1')} 100%)
  `;

  if (fullscreen) {
    // Telegram WebApp mode — content fills the whole viewport. No fake
    // iOS bezel / status bar / dynamic island; Telegram already provides those.
    return (
      <div className={tweaks.animations ? '' : 'nya-anims-off'} style={{
        position: 'fixed', inset: 0,
        background: bg,
        fontFamily: 'Quicksand, sans-serif',
        color: 'var(--text)',
        overflow: 'hidden',
      }}>
        {tweaks.animations && tweaks.decor > 20 && (
          <DriftingPetals palette={tweaks.palette} density={tweaks.decor} />
        )}
        <div className="nya-scroll" style={{
          position: 'absolute', top: 0, bottom: 0, left: 0, right: 0,
          overflowY: 'auto',
          paddingTop: 'env(safe-area-inset-top, 0px)',
        }}>
          {children}
        </div>
        {nav}
      </div>
    );
  }

  return (
    <div className={tweaks.animations ? '' : 'nya-anims-off'} style={{
      width: 390, height: 844, borderRadius: 56,
      position: 'relative', overflow: 'hidden',
      background: bg,
      boxShadow: `
        0 0 0 9px #1a1230,
        0 0 0 10px rgba(255,255,255,.07),
        0 30px 80px rgba(0,0,0,.55),
        0 0 60px ${getPaletteValue(tweaks.palette, 'glow')}33
      `,
      fontFamily: 'Quicksand, sans-serif',
      color: 'var(--text)',
    }}>
      {/* dynamic island */}
      <div style={{
        position: 'absolute', top: 11, left: '50%', transform: 'translateX(-50%)',
        width: 122, height: 36, borderRadius: 22, background: '#000', zIndex: 55,
      }}/>

      {/* status bar */}
      <div className="nya-status-bar">
        <span style={{ fontWeight: 600, fontSize: 16, fontFamily: '-apple-system, system-ui' }}>9:41</span>
        <span style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
          <svg width="17" height="11" viewBox="0 0 17 11"><rect x="0" y="6.5" width="3" height="4" rx=".5" fill="#fff"/><rect x="4.5" y="4.5" width="3" height="6" rx=".5" fill="#fff"/><rect x="9" y="2.5" width="3" height="8" rx=".5" fill="#fff"/><rect x="13.5" y="0" width="3" height="10.5" rx=".5" fill="#fff"/></svg>
          <svg width="16" height="11" viewBox="0 0 16 11"><path d="M8 3c2 0 4 .9 5.3 2.3l1-1A8 8 0 008 1.5 8 8 0 001.7 4.3l1 1A7 7 0 018 3z" fill="#fff"/><path d="M8 6.5c1.2 0 2.3.5 3.1 1.3l1-1A5.5 5.5 0 008 5 5.5 5.5 0 003.9 6.8l1 1A4.5 4.5 0 018 6.5z" fill="#fff"/><circle cx="8" cy="9.5" r="1.3" fill="#fff"/></svg>
          <svg width="25" height="11" viewBox="0 0 25 11"><rect x=".5" y=".5" width="21" height="10" rx="2.5" stroke="#fff" strokeOpacity=".5" fill="none"/><rect x="2" y="2" width="18" height="7" rx="1" fill="#fff"/></svg>
        </span>
      </div>

      {tweaks.animations && tweaks.decor > 20 && <DriftingPetals palette={tweaks.palette} density={tweaks.decor} />}

      <div className="nya-scroll" style={{
        position: 'absolute', top: 47, bottom: 0, left: 0, right: 0,
        overflowY: 'auto',
      }}>
        {children}
      </div>

      {nav}
    </div>
  );
}

function getPaletteValue(palKey, k) {
  const p = NYA_PALETTES[palKey] || NYA_PALETTES.night;
  return ({ bg0: p.bg0, bg1: p.bg1, bg2: p.bg2, glow: p.glow })[k];
}

// Drifting petal/sparkle layer — full-phone, ambient
function DriftingPetals({ palette, density }) {
  const items = React.useMemo(() => {
    const count = Math.round(4 + density / 20);
    let s = 13;
    const rng = () => { s = (s*1103515245+12345) & 0x7fffffff; return s / 0x7fffffff; };
    return Array.from({ length: count }, (_, i) => ({
      i,
      x: rng() * 100,
      dur: 14 + rng() * 14,
      delay: -rng() * 20,
      size: 8 + rng() * 8,
      kind: rng() > 0.5 ? 'petal' : 'sparkle',
    }));
  }, [density]);
  const col = palette === 'sakura' ? '#ffd9eb' :
              palette === 'moonlit' ? '#c4e4ff' :
              palette === 'twilight' ? '#c89bf0' : '#ffb3d9';
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 30, overflow: 'hidden' }}>
      {items.map(it => (
        <div key={it.i} style={{
          position: 'absolute', top: -20, left: `${it.x}%`,
          animation: `nya-petal-fall ${it.dur}s linear ${it.delay}s infinite`,
        }}>
          {it.kind === 'petal'
            ? <Petal size={it.size} color={col} opacity={.5} rot={Math.random() * 360} />
            : <Sparkle size={it.size * .8} color="#fff" opacity={.7} />}
        </div>
      ))}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Custom palette picker — visual grid of swatches
function PalettePicker({ value, onChange }) {
  const keys = Object.keys(NYA_PALETTES);
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 6 }}>
      {keys.map(k => {
        const p = NYA_PALETTES[k];
        const on = value === k;
        return (
          <button key={k} onClick={() => onChange(k)} title={p.label} style={{
            position: 'relative',
            padding: 0, border: 0, cursor: 'pointer',
            height: 52, borderRadius: 7,
            background: `linear-gradient(160deg, ${p.bg2} 0%, ${p.bg0} 100%)`,
            boxShadow: on
              ? `0 0 0 1.5px ${p.accentA}, 0 4px 12px rgba(0,0,0,.25)`
              : `0 0 0 .5px rgba(0,0,0,.15), 0 1px 3px rgba(0,0,0,.1)`,
            overflow: 'hidden',
          }}>
            <span style={{
              position: 'absolute', bottom: 3, left: 3, right: 3,
              display: 'flex', gap: 2,
            }}>
              <span style={{ flex: 1, height: 8, borderRadius: 3, background: p.accentA }}/>
              <span style={{ flex: 1, height: 8, borderRadius: 3, background: p.accentB }}/>
              <span style={{ width: 8, height: 8, borderRadius: 3, background: p.moon }}/>
            </span>
            {on && (
              <span style={{
                position: 'absolute', top: 4, right: 4,
                width: 14, height: 14, borderRadius: '50%',
                background: p.accentA,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>
                <svg viewBox="0 0 14 14" width="10" height="10">
                  <path d="M3 7.2 5.8 10 11 4.2" fill="none" stroke="#3a1e4d" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </span>
            )}
          </button>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Router
// ─────────────────────────────────────────────────────────────────────────────
// Detect Telegram WebApp environment. Sets up the SDK and returns true
// when we're really inside Telegram (so we can hide dev-only chrome and
// authenticate API calls with initData).
function useTelegramWebApp() {
  return React.useMemo(() => {
    const tg = typeof window !== 'undefined' && window.Telegram && window.Telegram.WebApp;
    if (!tg || !tg.initData) {
      return { inTelegram: false, initData: null, tg: null };
    }
    try { tg.ready(); } catch (_) {}
    try { tg.expand(); } catch (_) {}
    // Stop the swipe-down-to-close gesture from yanking the Telegram
    // chrome along — without this, every scroll attempt drags the
    // WebApp header down with the page.
    try { tg.disableVerticalSwipes && tg.disableVerticalSwipes(); } catch (_) {}
    return { inTelegram: true, initData: tg.initData, tg };
  }, []);
}

function App() {
  const tgEnv = useTelegramWebApp();
  const [t, setTweak] = useTweaks(window.TWEAK_DEFAULTS);
  const [stack, setStack] = React.useState([{ screen: 'home', figureId: null }]);
  const route = stack[stack.length - 1];
  const [appState, setAppState] = React.useState(tgEnv.inTelegram ? 'loading' : 'ready'); // 'loading' | 'ready' | 'error'
  const [currency, setCurrency] = React.useState('eur');
  const [searchTags, setSearchTags] = React.useState(new Set());
  const [figures, setFigures] = React.useState(NYA_FIGURES);
  const [stats, setStats] = React.useState(null);
  // Wishlist is tracked by product URL (not figure id) so it survives
  // catalog→wishlist transitions: a catalog item has id "cat-<n>" but the
  // backend stores it as "wish<tracked_id>" after POST. The URL is the
  // common key across both shapes.
  const [wishlist, setWishlist] = React.useState(() => {
    const s = new Set();
    NYA_FIGURES.forEach(f => f.wishlist && f.product_url && s.add(f.product_url));
    return s;
  });

  // Track first-load so the loading screen has a minimum visible time
  // on cold start but subsequent refetches (after wishlist toggles, etc.)
  // resolve as fast as possible.
  const splashShownRef = React.useRef(false);

  const refetchFigures = React.useCallback(async () => {
    if (!tgEnv.inTelegram) return;
    const MIN_SPLASH_MS = 900;
    const start = Date.now();
    try {
      const r = await fetch('/api/figures', {
        headers: { Authorization: 'tma ' + tgEnv.initData },
      });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const data = await r.json();
      if (Array.isArray(data.figures)) {
        setFigures(data.figures);
        const urls = new Set();
        data.figures.forEach(f => f.wishlist && f.product_url && urls.add(f.product_url));
        setWishlist(urls);
      }
      if (data.stats) setStats(data.stats);
      if (!splashShownRef.current) {
        const wait = Math.max(0, MIN_SPLASH_MS - (Date.now() - start));
        if (wait > 0) await new Promise(r => setTimeout(r, wait));
        splashShownRef.current = true;
      }
      setAppState('ready');
    } catch (e) {
      console.error('figures fetch failed', e);
      if (!splashShownRef.current) {
        const wait = Math.max(0, MIN_SPLASH_MS - (Date.now() - start));
        if (wait > 0) await new Promise(r => setTimeout(r, wait));
        splashShownRef.current = true;
      }
      setAppState('error');
    }
  }, [tgEnv.inTelegram, tgEnv.initData]);

  // Pull live data from the backend when we're running inside Telegram.
  // In a plain browser (design preview) we keep the mock NYA_FIGURES.
  React.useEffect(() => { refetchFigures(); }, [refetchFigures]);

  // scroll-to-top on every navigation
  const scrollTop = () => setTimeout(() => {
    const el = document.querySelector('.nya-scroll');
    if (el) el.scrollTop = 0;
  }, 0);

  // Bottom-nav tab switch — replaces the stack so back doesn't chain through tabs
  const goTab = React.useCallback((screen) => {
    setStack([{ screen, figureId: null }]);
    scrollTop();
  }, []);

  // Push a sub-screen onto the history stack. Second arg can be either
  // a figure id (string) or the whole figure object — passing the object
  // lets the detail screen render catalog items that aren't in figures yet.
  const go = React.useCallback((screen, figureIdOrObj) => {
    const isObj = figureIdOrObj && typeof figureIdOrObj === 'object';
    const figureId = isObj ? figureIdOrObj.id : (figureIdOrObj || null);
    const figureData = isObj ? figureIdOrObj : null;
    setStack(s => [...s, { screen, figureId, figureData }]);
    scrollTop();
  }, []);

  // Pop the stack. If only the root is left, fall back to home.
  const goBack = React.useCallback(() => {
    setStack(s => {
      if (s.length > 1) { scrollTop(); return s.slice(0, -1); }
      scrollTop();
      return [{ screen: 'home', figureId: null }];
    });
  }, []);

  const toggleWishlist = React.useCallback(async (figureOrId) => {
    // Accept either a full figure object (preferred) or just an id
    // (legacy callers) — we still need a product_url to call the API.
    let figure = (typeof figureOrId === 'object' && figureOrId)
      ? figureOrId
      : figures.find(f => f.id === figureOrId);
    if (!figure || !figure.product_url) return;
    const url = figure.product_url;
    const wasIn = wishlist.has(url);

    // Optimistic toggle so the heart flips immediately.
    setWishlist(prev => {
      const next = new Set(prev);
      if (wasIn) next.delete(url); else next.add(url);
      return next;
    });

    if (!tgEnv.inTelegram) return; // dev mode — local state only

    const revert = () => setWishlist(prev => {
      const next = new Set(prev);
      if (wasIn) next.add(url); else next.delete(url);
      return next;
    });
    const tgAlert = (msg) => {
      try { window.Telegram?.WebApp?.showAlert?.(msg); } catch (_) {}
    };

    try {
      if (wasIn) {
        const cur = figures.find(f => f.product_url === url);
        const trackedId = cur && cur.tracked_id;
        if (trackedId) {
          const r = await fetch('/api/wishlist/' + trackedId, {
            method: 'DELETE',
            headers: { Authorization: 'tma ' + tgEnv.initData },
          });
          if (!r.ok) throw new Error('HTTP ' + r.status);
        }
      } else {
        const r = await fetch('/api/wishlist', {
          method: 'POST',
          headers: {
            Authorization: 'tma ' + tgEnv.initData,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            product_url: url,
            name: figure.name || null,
            image_url: figure.image_url || null,
          }),
        });
        if (r.status === 409) {
          // Already part of an order — not a real error; just inform.
          let detail = 'товар уже в твоих заказах';
          try { const j = await r.json(); detail = j.detail || detail; } catch (_) {}
          tgAlert(detail);
          revert();
          return;
        }
        if (!r.ok) throw new Error('HTTP ' + r.status);
      }
      // Server is the source of truth for figure list + flags.
      await refetchFigures();
    } catch (e) {
      console.error('wishlist toggle failed', e);
      tgAlert('Не получилось обновить вишлист — попробуй ещё раз');
      revert();
    }
  }, [wishlist, figures, tgEnv, refetchFigures]);

  const themeStyle = buildThemeStyle(t);

  const showLoading = () => {
    setAppState('loading');
    setTimeout(() => setAppState('ready'), 2500);
  };
  const showError = () => setAppState('error');

  // Click a tag anywhere → jump to catalog with it pre-filtered
  const openCatalogWithTag = React.useCallback((tag) => {
    setSearchTags(prev => {
      const n = new Set(prev);
      n.add(tag);
      return n;
    });
    go('search');
  }, [go]);

  const renderScreen = () => {
    if (appState === 'loading') return <LoadingScreen tweaks={t} />;
    if (appState === 'error') return <ErrorState tweaks={t} onRetry={() => setAppState('ready')} />;
    const s = route.screen;
    if (s === 'home')     return <DashboardScreen   go={go} tweaks={t} />;
    if (s === 'detail')   return <FigureDetailScreen go={go} goBack={goBack} figureId={route.figureId} figureData={route.figureData} tweaks={t} wishlist={wishlist} toggleWishlist={toggleWishlist} openCatalogWithTag={openCatalogWithTag} />;
    if (s === 'calendar') return <CalendarScreen    go={go} tweaks={t} />;
    if (s === 'search')   return <SearchScreen      go={go} tweaks={t} wishlist={wishlist} toggleWishlist={toggleWishlist} tags={searchTags} setTags={setSearchTags} />;
    if (s === 'wishlist') return <WishlistScreen    go={go} tweaks={t} wishlist={wishlist} toggleWishlist={toggleWishlist} />;
    if (s === 'settings') return <SettingsScreen    go={go} tweaks={t} currency={currency} setCurrency={setCurrency} />;
    if (s === 'notifications') return <NotificationsScreen go={go} goBack={goBack} tweaks={t} />;
    if (s === 'profile') return <ProfileScreen goBack={goBack} tweaks={t} />;
    if (s === 'notifSettings') return <NotifSettingsScreen goBack={goBack} tweaks={t} />;
    if (s === 'members') return <MembersScreen goBack={goBack} tweaks={t} />;
    if (s === 'about') return <AboutScreen goBack={goBack} tweaks={t} />;
    return <DashboardScreen go={go} tweaks={t} />;
  };

  const navActive = ({ home: 'home', detail: 'home', calendar: 'calendar', search: 'search', wishlist: 'wishlist', settings: 'settings', profile: 'settings', notifSettings: 'settings', members: 'settings', about: 'settings' })[route.screen];

  return (
    <FiguresContext.Provider value={figures}>
      <CurrencyContext.Provider value={currency}>
        <ToastProvider>
          <div style={themeStyle}>
            <style>{NYA_KEYFRAMES}</style>

            {tgEnv.inTelegram ? (
              <PhoneFrame
                tweaks={t}
                fullscreen
                nav={appState === 'ready' ? <BottomNav active={navActive} onChange={(id) => goTab(id)} /> : null}
              >
                <div style={{ minHeight: '100%', position: 'relative' }} key={route.screen + (route.figureId || '')}>
                  {renderScreen()}
                </div>
              </PhoneFrame>
            ) : (
              <div className="nya-stage">
                <PhoneFrame
                  tweaks={t}
                  nav={appState === 'ready' ? <BottomNav active={navActive} onChange={(id) => goTab(id)} /> : null}
                >
                  <div style={{ minHeight: '100%', position: 'relative' }} key={route.screen + (route.figureId || '')}>
                    {renderScreen()}
                  </div>
                </PhoneFrame>
              </div>
            )}

            {/* Dev-only tweaks panel: hidden when running inside Telegram. */}
            {!tgEnv.inTelegram && (
              <TweaksPanel title="Tweaks">
                <TweakSection label="Палитра">
                  <PalettePicker value={t.palette} onChange={v => setTweak('palette', v)} />
                </TweakSection>

                <TweakSection label="Атмосфера">
                  <TweakSlider label="Свечение" value={t.glow} min={0} max={100} unit="%"
                               onChange={v => setTweak('glow', v)} />
                  <TweakSlider label="Декор" value={t.decor} min={0} max={100} unit="%"
                               onChange={v => setTweak('decor', v)} />
                  <TweakSlider label="Скругления" value={t.radius} min={8} max={36} unit="px"
                               onChange={v => setTweak('radius', v)} />
                  <TweakToggle label="Анимации" value={t.animations}
                               onChange={v => setTweak('animations', v)} />
                </TweakSection>

                <TweakSection label="Состояния">
                  <TweakButton label="Показать загрузку" onClick={showLoading} />
                  <TweakButton label="Показать ошибку" onClick={showError} secondary />
                </TweakSection>
              </TweaksPanel>
            )}
          </div>
        </ToastProvider>
      </CurrencyContext.Provider>
    </FiguresContext.Provider>
  );
}

// Re-export with the proper value for TweakColor — that control accepts arrays directly
// We need to set the active value as the array, not the string. Override: read current
// palette and pass as colors array.
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
