// Shell components — header, bottom nav, glass card, sparkline, switch, primitives.

function MiniAppHeader({ title, subtitle, onClose, onBack, right }) {
  return (
    <div className="miniapp-header">
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
        {onBack ? (
          <div className="miniapp-icon-btn" onClick={onBack}><Icons.chevL size={18}/></div>
        ) : (
          <div style={{
            position: 'relative',
            width: 38, height: 38, borderRadius: 12,
            background: 'linear-gradient(150deg, var(--accent-2) 0%, var(--accent) 52%, #0a63c9 100%)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            boxShadow: '0 6px 16px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.4), inset 0 -2px 6px rgba(0,0,0,0.25)',
            flexShrink: 0, overflow: 'hidden',
          }}>
            {/* glossy diagonal sheen */}
            <div style={{
              position: 'absolute', top: 0, left: 0, right: 0, height: '55%',
              background: 'linear-gradient(180deg, rgba(255,255,255,0.35), transparent)',
              borderRadius: '12px 12px 50% 50%',
            }}/>
            <svg width="26" height="26" viewBox="0 0 32 32" fill="none" style={{ position: 'relative', zIndex: 1 }}>
              {/* portal swirl */}
              <path d="M16 5.5 a10.5 10.5 0 0 1 10.5 10.5" stroke="#fff" strokeWidth="3" strokeLinecap="round" opacity="0.95"/>
              <path d="M16 26.5 a10.5 10.5 0 0 1 -10.5 -10.5" stroke="#fff" strokeWidth="3" strokeLinecap="round" opacity="0.95"/>
              <path d="M16 10.5 a5.5 5.5 0 0 1 5.5 5.5" stroke="rgba(255,255,255,0.6)" strokeWidth="2.4" strokeLinecap="round"/>
              <path d="M16 21.5 a5.5 5.5 0 0 1 -5.5 -5.5" stroke="rgba(255,255,255,0.6)" strokeWidth="2.4" strokeLinecap="round"/>
              <circle cx="16" cy="16" r="2.6" fill="#fff"/>
              {/* sparkle */}
              <path d="M25 6 l0.9 2.2 2.2 0.9 -2.2 0.9 -0.9 2.2 -0.9 -2.2 -2.2 -0.9 2.2 -0.9 Z" fill="#fff"/>
            </svg>
          </div>
        )}
        <div style={{ minWidth: 0 }}>
          <div className="title" style={{ lineHeight: 1.1 }}>{title}</div>
          {subtitle && <div style={{ fontSize: 11, color: 'var(--text-mute)', marginTop: 1 }}>{subtitle}</div>}
        </div>
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        {right}
        <div className="miniapp-icon-btn" onClick={onClose}>
          <Icons.close size={16}/>
        </div>
      </div>
    </div>
  );
}

const TABS = [
  { id: 'market',   icon: 'grid' },
  { id: 'wallet',   icon: 'wallet' },
  { id: 'mine',     icon: 'gift' },
  { id: 'profile',  icon: 'user' },
];

// Simple i18n — RU is default, EN is provided where it matters.
const LangContext = React.createContext({ lang: 'ru', setLang: () => {} });
const I18N = {
  tab_market:   { ru: 'Маркет',          en: 'Market' },
  tab_wallet:   { ru: 'Кошелёк',         en: 'Wallet' },
  tab_mine:     { ru: 'Мои',             en: 'My gifts' },
  tab_profile:  { ru: 'Профиль',         en: 'Profile' },
  title_market: { ru: 'vMarket',         en: 'vMarket' },
  sub_market:   { ru: 'Маркет подарков', en: 'Gift market' },
  title_wallet: { ru: 'Кошелёк',         en: 'Wallet' },
  sub_wallet:   { ru: 'Пополнение и история', en: 'Top up & history' },
  title_mine:   { ru: 'Мои подарки',     en: 'My gifts' },
  title_profile:{ ru: 'Профиль',         en: 'Profile' },
};
function useT() {
  const { lang } = React.useContext(LangContext);
  return (key) => (I18N[key] && I18N[key][lang]) || key;
}

function BottomNav({ active, onChange, platform }) {
  const t = useT();
  const activeIdx = Math.max(0, TABS.findIndex(x => x.id === active));
  return (
    <div className={"tabbar" + (platform === 'android' ? ' tabbar-android' : '')}>
      <div className="tab-indicator"
           style={{ left: `calc(7px + ${activeIdx} * ((100% - 14px) / ${TABS.length}))`,
                    width: `calc((100% - 14px) / ${TABS.length})` }}/>
      {TABS.map(x => {
        const I = Icons[x.icon];
        return (
          <div key={x.id}
               className={'tab' + (active === x.id ? ' active' : '')}
               onClick={() => onChange(x.id)}>
            <I size={22}/>
            <span>{t('tab_' + x.id)}</span>
          </div>
        );
      })}
    </div>
  );
}

function GlassCard({ children, style, className = '', strong = false, ...rest }) {
  return (
    <div className={'glass ' + (strong ? 'glass-strong ' : '') + className}
         style={{ padding: 16, ...style }} {...rest}>
      {children}
    </div>
  );
}

// Animated screen content wrapper
function ScreenWrap({ children }) {
  return <div className="anim-fade-up col gap-12" style={{ paddingTop: 8 }}>{children}</div>;
}

// ───────── tab-switch loading skeleton ─────────
function SkBlock({ w = '100%', h = 12, r = 10, style }) {
  return <div className="sk" style={{ width: w, height: h, borderRadius: r, flexShrink: 0, ...style }}/>;
}

// Screen-aware skeleton shown for a beat while a tab "loads".
function ScreenSkeleton({ tab }) {
  if (tab === 'market' || tab === 'mine') {
    return (
      <div className="sk-screen col gap-12" style={{ paddingTop: 8 }}>
        {tab === 'market'
          ? <>
              <SkBlock h={44} r={16}/>
              <div className="row gap-8" style={{ overflow: 'hidden' }}>
                <SkBlock w={38} h={38} r={19}/>
                <SkBlock w={38} h={38} r={19}/>
                <SkBlock w={104} h={36} r={18}/>
                <SkBlock w={92} h={36} r={18}/>
              </div>
            </>
          : <SkBlock h={84} r={20}/>}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          {Array.from({ length: 6 }).map((_, i) => (
            <div key={i} className="col gap-8">
              <SkBlock r={22} style={{ aspectRatio: '1 / 1', height: 'auto' }}/>
              <SkBlock w="65%" h={12} r={6} style={{ marginTop: 2 }}/>
              <SkBlock w="42%" h={15} r={6}/>
            </div>
          ))}
        </div>
      </div>
    );
  }
  if (tab === 'wallet') {
    return (
      <div className="sk-screen col gap-12" style={{ paddingTop: 8 }}>
        <SkBlock h={150} r={22}/>
        <SkBlock w={150} h={15} r={6} style={{ marginTop: 4 }}/>
        <SkBlock h={72} r={16}/>
        <SkBlock h={72} r={16}/>
        <SkBlock w={150} h={15} r={6} style={{ marginTop: 4 }}/>
        <SkBlock h={210} r={18}/>
      </div>
    );
  }
  // profile
  return (
    <div className="sk-screen col gap-12" style={{ paddingTop: 8 }}>
      <SkBlock h={168} r={22}/>
      <SkBlock w={170} h={13} r={6} style={{ marginTop: 4 }}/>
      <SkBlock h={172} r={16}/>
      <SkBlock w={120} h={13} r={6} style={{ marginTop: 4 }}/>
      <SkBlock h={108} r={16}/>
    </div>
  );
}

// Number that animates from prev value to new value
function AnimatedNumber({ value, fmt = (v) => v, duration = 1100, className = '', style = {} }) {
  const v = useAnimatedNumber(value, duration);
  return <span className={className} style={style}>{fmt(v)}</span>;
}

// Count-up number — eases from 0 → value, replays whenever `trigger` changes.
// Used for the wallet balance "refresh" flourish.
function CountUp({ value, duration = 900, fmt = (v) => Math.round(v), trigger = 0, className = '', style = {} }) {
  const [v, setV] = React.useState(0);
  const raf = React.useRef(0);
  React.useEffect(() => {
    const start = performance.now();
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const e = 1 - Math.pow(1 - p, 3);
      setV(value * e);
      if (p < 1) raf.current = requestAnimationFrame(tick);
      else setV(value);
    };
    cancelAnimationFrame(raf.current);
    raf.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf.current);
  }, [trigger, value, duration]);
  return <span className={className} style={style}>{fmt(v)}</span>;
}

// Toggle switch
function Switch({ on, onChange }) {
  return <div className={'switch' + (on ? ' on' : '')} onClick={() => onChange(!on)} />;
}

// Section header
function SectionHeader({ title, action, onAction, right }) {
  return (
    <div className="row aic jcsb" style={{ padding: '4px 4px 0' }}>
      <div style={{ fontSize: 16, fontWeight: 650, letterSpacing: '-0.01em' }}>{title}</div>
      {right}
      {action && (
        <div className="t-accent" style={{ fontSize: 13, fontWeight: 600, cursor: 'pointer' }} onClick={onAction}>{action}</div>
      )}
    </div>
  );
}

// Status dot with text
function StatusPill({ on, label }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--text-soft)' }}>
      <div className={'dot' + (on ? '' : ' red')}/>
      <span>{label}</span>
    </div>
  );
}

// Tier badge
function TierBadge({ tier, mini = false, label }) {
  const t = TIER_COLOR[tier] || TIER_COLOR.common;
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      padding: mini ? '1px 6px' : '2px 8px',
      borderRadius: 999,
      background: t.c + '22',
      color: t.c,
      fontSize: mini ? 9 : 10, fontWeight: 700,
      letterSpacing: '0.04em', textTransform: 'uppercase',
    }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', background: t.c }}/>
      {label || t.name}
    </div>
  );
}

// Confetti burst (12 particles emitting from center)
function Confetti() {
  const colors = ['#ff6b9d','#fde047','#60a5fa','#a78bfa','#34d399','#fb923c'];
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden' }}>
      {Array.from({length: 28}).map((_, i) => {
        const angle = (i / 28) * Math.PI * 2;
        const dist = 80 + Math.random() * 120;
        const dx = Math.cos(angle) * dist;
        const dy = Math.sin(angle) * dist;
        const c = colors[i % colors.length];
        const size = 6 + Math.random() * 6;
        return (
          <span key={i} style={{
            position: 'absolute',
            left: '50%', top: '50%',
            width: size, height: size,
            background: c,
            borderRadius: Math.random() > 0.5 ? '50%' : '2px',
            animation: `confettiBurst 1.2s cubic-bezier(0.1, 0.5, 0.3, 1) forwards`,
            '--dx': dx + 'px',
            '--dy': dy + 'px',
            '--rot': (Math.random() * 720 - 360) + 'deg',
          }}/>
        );
      })}
    </div>
  );
}

// Tiny helpers so any descendant of SheetShell can trigger the animated
// close instead of calling raw onClose (which would yank the sheet without
// the slide-down animation). <SheetCloseX/> is the round header button;
// <WithSheetClose>{close => …}</WithSheetClose> hands the closer to a
// caller (use on Cancel/Done buttons that should also animate down).
function SheetCloseX({ size = 16, className = 'miniapp-icon-btn', style }) {
  const close = React.useContext(SheetCloseContext);
  return (
    <div className={className} onClick={close} style={style}>
      <Icons.close size={size}/>
    </div>
  );
}
function WithSheetClose({ children }) {
  const close = React.useContext(SheetCloseContext);
  return children(close);
}

Object.assign(window, { SheetCloseX, WithSheetClose });

// ───────── SheetShell ─────────
// Bottom-sheet wrapper with iOS-style drag-to-dismiss + close animation.
// Drag the grip down; release past 20% of sheet height to dismiss, otherwise snaps back.
// Closing via backdrop tap or onClose also animates down.
// Children can request an animated close via the SheetCloseContext.
const SheetCloseContext = React.createContext(() => {});

function SheetShell({ onClose, children, hideGrip = false, dismissable = true }) {
  const [opened, setOpened]   = React.useState(false);
  const [closing, setClosing] = React.useState(false);
  const [drag, setDrag]       = React.useState(0);
  const [dragging, setDragging] = React.useState(false);
  const startYRef = React.useRef(0);
  const sheetRef  = React.useRef(null);

  // Scroll-lock фона (body.sheet-open) теперь ВЛАДЕЕТ App — он ставит/снимает
  // класс авторитетно, из своего состояния открытых модалок (см. app.jsx,
  // effect на `anySheet`). Раньше каждый SheetShell дёргал body.classList сам
  // (add на mount, снятие в rAF по факту отсутствия .sheet-backdrop) — при
  // быстрых open/close (карточка→оплата, drag-dismiss) это рассинхронивалось,
  // 'sheet-open' залипал и `.screen-scroll` навсегда оставался overflow:hidden,
  // пока не перезайдёшь в раздел. Единый владелец в App убирает гонку целиком.

  React.useEffect(() => {
    const id = requestAnimationFrame(() => setOpened(true));
    return () => cancelAnimationFrame(id);
  }, []);

  const closeNow = React.useCallback(() => {
    if (closing) return;
    setClosing(true);
    setTimeout(onClose, 320);
  }, [closing, onClose]);

  const onDown = (e) => {
    if (!dismissable) return;
    setDragging(true);
    startYRef.current = e.clientY;
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch (_) {}
  };
  const onMove = (e) => {
    if (!dragging) return;
    const dy = e.clientY - startYRef.current;
    setDrag(Math.max(0, dy));
  };
  const onUp = () => {
    if (!dragging) return;
    setDragging(false);
    const h = sheetRef.current?.offsetHeight || 600;
    // Light pull dismisses — drag past 20% of the sheet height closes it,
    // otherwise it springs back up. Closes easily on a small swipe.
    if (drag > h * 0.2) closeNow();
    else setDrag(0);
  };

  const transform = closing || !opened ? 'translateY(100%)' : `translateY(${drag}px)`;
  const transition = dragging ? 'none' : 'transform 0.36s cubic-bezier(0.2, 0.7, 0.2, 1)';

  return (
    <div className={'sheet-backdrop' + (closing ? ' closing' : '')}
         onClick={dismissable ? closeNow : undefined}>
      <div ref={sheetRef}
           className="sheet"
           onClick={(e) => e.stopPropagation()}
           style={{ transform, transition, willChange: 'transform' }}>
        {!hideGrip && (
          <div className="sheet-grip-zone"
               onPointerDown={onDown}
               onPointerMove={onMove}
               onPointerUp={onUp}
               onPointerCancel={onUp}
               style={{ cursor: dismissable ? 'grab' : 'default' }}>
            <div className="sheet-grip" style={{ opacity: dismissable ? 0.5 : 0.2 }}/>
          </div>
        )}
        <SheetCloseContext.Provider value={closeNow}>
          {children}
        </SheetCloseContext.Provider>
      </div>
    </div>
  );
}

Object.assign(window, {
  MiniAppHeader, BottomNav, GlassCard, ScreenWrap, AnimatedNumber,
  Switch, SectionHeader, StatusPill, TierBadge, Confetti, SheetShell, SheetCloseContext,
  LangContext, useT, TABS, SkBlock, ScreenSkeleton, CountUp,
});
