// Topbar, WelcomeHero, Market-closed banner, Tweaks panel
//
// Cutover polish (2026-04-27):
//   - Honest session pill: derive label from window.MMM_LIVE_SESSION
//     (set by chart.jsx's currentSessionET()). No more hardcoded "Live Session"
//     when the market is in maintenance / extended hours. No more hardcoded
//     "NY Session" in the hero pill.
//   - i18n: switches strings on document.documentElement.lang.
//   - marketOpen prop is now optional — if absent, derived from session.
//
const { useState: useStateT, useEffect: useEffectT } = React;

// ─── i18n ───────────────────────────────────────────────────────────
const STR_chrome = {
  en: {
    asia_session: 'Asia Session',
    london_session: 'London Session',
    ny_session: 'NY Session',
    extended_hours: 'Extended Hours',
    maintenance: 'Maintenance',
    market_closed: 'Market Closed',
    off_hours: 'Off-hours',
    live_tab: 'Live Tab',
    welcome_back: 'Welcome back,',
    session_uptime_label: 'Session uptime',
    sub_open_pre: "You're watching NQ trade in the ",
    sub_open_post: ' session.',
    sub_open_line2: 'Signals refresh every 30 seconds.',
    sub_closed_line1: "Markets are closed. You're looking at the last completed session.",
    sub_closed_line2: 'Live updates resume when CME Globex reopens.',
    asia: 'Asia', london: 'London', new_york: 'New York', extended: 'Extended',
    active: 'Active',
    closed_banner_title: 'Markets are currently closed.',
    closed_banner_body: 'You are viewing the last completed session. Live updates resume when CME Globex reopens at 18:00 ET on Sunday.',
    next_open: 'Next open',
    opens_in: 'Opens in',
  },
  es: {
    asia_session: 'Sesión de Asia',
    london_session: 'Sesión de Londres',
    ny_session: 'Sesión de Nueva York',
    extended_hours: 'Horario Extendido',
    maintenance: 'Mantenimiento',
    market_closed: 'Mercado Cerrado',
    off_hours: 'Fuera de Horario',
    live_tab: 'Pestaña en Vivo',
    welcome_back: 'Bienvenido,',
    session_uptime_label: 'Tiempo de sesión',
    sub_open_pre: 'Estás observando NQ operar en la sesión ',
    sub_open_post: '.',
    sub_open_line2: 'Las señales se actualizan cada 30 segundos.',
    sub_closed_line1: 'Los mercados están cerrados. Estás viendo la última sesión completada.',
    sub_closed_line2: 'Las actualizaciones en vivo se reanudan cuando CME Globex reabre.',
    asia: 'Asia', london: 'Londres', new_york: 'Nueva York', extended: 'Extendido',
    active: 'Activa',
    closed_banner_title: 'Los mercados están cerrados.',
    closed_banner_body: 'Estás viendo la última sesión completada. Las actualizaciones en vivo se reanudan cuando CME Globex reabre a las 18:00 ET el domingo.',
    next_open: 'Próxima apertura',
    opens_in: 'Abre en',
  },
};
function t_chrome(key) {
  const lang = (document.documentElement.lang || 'en').toLowerCase().split('-')[0];
  return (STR_chrome[lang] && STR_chrome[lang][key]) || STR_chrome.en[key] || key;
}

// Pretty session label for header pill.
function sessionLabel(session) {
  switch (session) {
    case 'asia':     return t_chrome('asia_session');
    case 'london':   return t_chrome('london_session');
    case 'ny':       return t_chrome('ny_session');
    case 'extended': return t_chrome('extended_hours');
    case 'maint':    return t_chrome('maintenance');
    default:         return t_chrome('off_hours');
  }
}
function sessionShort(session) {
  switch (session) {
    case 'asia':     return t_chrome('asia');
    case 'london':   return t_chrome('london');
    case 'ny':       return t_chrome('new_york');
    case 'extended': return t_chrome('extended');
    default:         return t_chrome('off_hours');
  }
}
function isOpenSession(s) {
  return s === 'asia' || s === 'london' || s === 'ny';
}

// ─── Live session hook — keeps in sync with chart.jsx ───────────────
function useLiveSession() {
  const [sess, setSess] = useStateT(() =>
    (typeof window !== 'undefined' && window.MMM_LIVE_SESSION) || 'extended'
  );
  useEffectT(() => {
    const onUpdate = () => {
      const s = window.MMM_LIVE_SESSION;
      if (s) setSess(s);
    };
    window.addEventListener('mmm-session-update', onUpdate);
    window.addEventListener('mmm-live-update', onUpdate);
    onUpdate();
    return () => {
      window.removeEventListener('mmm-session-update', onUpdate);
      window.removeEventListener('mmm-live-update', onUpdate);
    };
  }, []);
  return sess;
}

function useNowClock() {
  const [now, setNow] = useStateT(() => new Date());
  useEffectT(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return now;
}

// Slice T re-render hook (kept for parity with the Slice B wiring).
function useLiveTickT() {
  const [, setT] = useStateT(0);
  useEffectT(() => {
    const onUpdate = () => setT(t => t + 1);
    window.addEventListener('mmm-live-update', onUpdate);
    return () => window.removeEventListener('mmm-live-update', onUpdate);
  }, []);
}

const ACCENTS = [
  { name: 'Sunset', color: '#d97757' },
  { name: 'Forest', color: '#2e7d4f' },
  { name: 'Ocean',  color: '#5b7cb8' },
  { name: 'Plum',   color: '#8c5fa5' },
];
function applyAccent(idx) {
  const a = ACCENTS[idx % ACCENTS.length];
  document.documentElement.style.setProperty('--accent', a.color);
}

// ─── Live pill — refresh ring + dynamic session label ───────────────
// PR ui-polish: how long without a live_stats.json refresh before we
// stop pretending the bot is live. Publisher cycles are 60s, so 90s
// lets a single missed cycle slide before flipping to OFFLINE.
const BOT_OFFLINE_STALE_THRESHOLD_MS = 180 * 1000;

function LivePillWithRefresh({ seconds = 30, session }) {
  const [pct, setPct] = useStateT(0);
  const [pulse, setPulse] = useStateT(false);
  // PR ui-polish: track staleness so we can render OFFLINE when the
  // publisher hasn't written for > BOT_OFFLINE_STALE_THRESHOLD_MS.
  const [stale, setStale] = useStateT(false);
  useEffectT(() => {
    let start = performance.now();
    let raf;
    const loop = (now) => {
      const elapsed = (now - start) / 1000;
      const p = Math.min(100, (elapsed / seconds) * 100);
      setPct(p);
      // Re-check freshness on every animation tick (cheap: it's
      // just a Date.parse + subtract). MMM_LIVE.updated is the
      // timestamp the publisher writes on every cycle.
      const live = (typeof window !== 'undefined') ? window.MMM_LIVE : null;
      const updatedMs = live && live.updated ? Date.parse(live.updated) : 0;
      const ageMs = updatedMs > 0 ? (Date.now() - updatedMs) : Infinity;
      setStale(ageMs > BOT_OFFLINE_STALE_THRESHOLD_MS);
      if (p >= 100) {
        setPulse(true);
        setTimeout(() => setPulse(false), 600);
        start = now;
      }
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [seconds]);
  if (stale) {
    // No animation, no green pulse, no progress bar. Visitors see
    // immediately that the bot is not currently emitting fresh data.
    return (
      <span className="pill stale">
        <span className="dot"></span>BOT OFFLINE
      </span>
    );
  }
  return (
    <span className="pill live pill-refresh">
      <span className="dot"></span>{sessionLabel(session)}
      <span className={`pill-refresh__fill ${pulse ? 'pulse':''}`} style={{ width: `${pct}%` }}></span>
    </span>
  );
}

// ─── Phase 12: Market countdown ─────────────────────────────────────
// Show "Opens in 1d 4h 23m" next to the Market Closed pill so visitors
// know exactly when CME Globex reopens. Re-computes every 60s.
//
// CME Globex futures hours (ET):
//   Sun 18:00 → Fri 17:00 continuous, with a daily 17:00-18:00
//   maintenance break on Mon/Tue/Wed/Thu evenings.
//
// We compute everything in America/New_York via Intl.DateTimeFormat so
// DST is handled automatically — no offset math needed.
function getMarketState(now) {
  // weekday: 'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
  // hour:    0..23 in ET
  // minute:  0..59
  const fmt = new Intl.DateTimeFormat('en-US', {
    timeZone: 'America/New_York', hour12: false,
    weekday: 'short', hour: '2-digit', minute: '2-digit',
  });
  const parts = fmt.formatToParts(now);
  let weekday = '', hour = 0, minute = 0;
  for (const p of parts) {
    if (p.type === 'weekday') weekday = p.value;
    else if (p.type === 'hour') hour = parseInt(p.value, 10) || 0;
    else if (p.type === 'minute') minute = parseInt(p.value, 10) || 0;
  }
  const DOW = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
  const dow = DOW[weekday];
  if (dow === undefined) return { open: true, msToOpen: 0 };

  // Compute "now" as ET-minutes-of-week (0..7*1440-1).
  const etMinOfWeek = dow * 1440 + hour * 60 + minute;

  // Open range as ET-minutes-of-week:
  //   Sun 18:00 (Sun*1440 + 18*60) — Fri 17:00 (Fri*1440 + 17*60),
  //   minus daily 17:00-18:00 maintenance for Mon..Thu.
  const SUN_OPEN  = 0 * 1440 + 18 * 60;     // Sun 18:00
  const FRI_CLOSE = 5 * 1440 + 17 * 60;     // Fri 17:00

  let open = (etMinOfWeek >= SUN_OPEN && etMinOfWeek < FRI_CLOSE);
  if (open) {
    // Daily 17:00 — 18:00 maintenance for Mon (1), Tue (2), Wed (3), Thu (4).
    if (dow >= 1 && dow <= 4 && hour === 17) open = false;
  }
  if (open) return { open: true, msToOpen: 0 };

  // Closed — figure out next open in ET-minutes-of-week.
  let nextOpen;
  if (etMinOfWeek < SUN_OPEN) {
    // Before Sun 18:00 (i.e. earlier on Sunday).
    nextOpen = SUN_OPEN;
  } else if (etMinOfWeek >= FRI_CLOSE) {
    // Fri >= 17:00, Sat all day, Sun before 18:00 — next open is next Sun 18:00.
    nextOpen = 7 * 1440 + 18 * 60;          // wrap to next week
  } else {
    // Mon..Thu maintenance hour 17:00-18:00 — opens at 18:00 same day.
    nextOpen = dow * 1440 + 18 * 60;
  }
  const minsToOpen = nextOpen - etMinOfWeek;
  return { open: false, msToOpen: minsToOpen * 60 * 1000 };
}

function formatCountdown(ms) {
  if (ms <= 0) return '0m';
  const totalMin = Math.floor(ms / 60000);
  const days  = Math.floor(totalMin / 1440);
  const hours = Math.floor((totalMin % 1440) / 60);
  const mins  = totalMin % 60;
  if (days  > 0) return `${days}d ${hours}h ${mins}m`;
  if (hours > 0) return `${hours}h ${mins}m`;
  return `${mins}m`;
}

function MarketCountdown() {
  const [tick, setTick] = useStateT(0);
  useEffectT(() => {
    const id = setInterval(() => setTick(t => t + 1), 60000);
    return () => clearInterval(id);
  }, []);
  const state = getMarketState(new Date());
  if (state.open) return null;
  return (
    <span className="market-countdown mono" title={t_chrome('next_open')}>
      {t_chrome('opens_in')} {formatCountdown(state.msToOpen)}
    </span>
  );
}

function Topbar({ marketOpen }) {
  const now = useNowClock();
  const session = useLiveSession();
  const open = (marketOpen !== undefined) ? marketOpen : isOpenSession(session);
  const nyTime = now.toLocaleTimeString('en-US', { hour12: false, timeZone: 'America/New_York' });
  const lonTime = now.toLocaleTimeString('en-US', { hour12: false, timeZone: 'Europe/London' });
  return (
    <div className="topbar">
      <div className="topbar__brand">
        <div className="brand-mark">M</div>
        <span className="mono" style={{fontSize:'var(--fs-sm)', letterSpacing:'0.05em'}}>MAKEMONEYMARKETS</span>
      </div>
      {/* MMM-INTEGRATION: prototype nav removed. Global nav rendered by docs/shared/nav.js
          which is loaded as a <script> in index.html. */}
      <nav className="topbar__nav" aria-hidden="true"></nav>
      <div className="topbar__right">
        <span className="clock"><span>NY</span><strong>{nyTime}</strong></span>
        <span className="clock optional"><span>LON</span><strong>{lonTime}</strong></span>
        {open
          ? <LivePillWithRefresh seconds={30} session={session} />
          : <>
              <span className="pill off"><span className="dot"></span>{t_chrome('market_closed')}</span>
              <MarketCountdown />
            </>}
      </div>
    </div>
  );
}

// ─── Session uptime — minutes since the current trading session began ─
// Returns { hours, minutes } when a session is active (Asia/London/NY/
// Extended), or null when the market is closed. Updates every minute.
function useSessionUptime() {
  const session = useLiveSession();
  const [, setTick] = useStateT(0);
  useEffectT(() => {
    const id = setInterval(() => setTick(t => t + 1), 60000);
    return () => clearInterval(id);
  }, []);

  // ET hour-min as integer (HHMM)
  const fmt = new Intl.DateTimeFormat('en-US', {
    timeZone: 'America/New_York', hour12: false,
    hour: '2-digit', minute: '2-digit'
  });
  const parts = fmt.formatToParts(new Date());
  let h = 0, m = 0;
  for (const p of parts) {
    if (p.type === 'hour') h = parseInt(p.value, 10) || 0;
    if (p.type === 'minute') m = parseInt(p.value, 10) || 0;
  }
  const nowMin = h * 60 + m;

  // Session start times (ET, in minutes-from-midnight). Aligned with the
  // sessionForETHour() boundaries in chart.jsx — Samu's schedule:
  //   Asia    18:00 – 02:00 (spans midnight)
  //   London  02:00 – 08:00
  //   NY      08:00 – 17:00
  //   Maint   17:00 – 18:00
  const STARTS = {
    asia:     18 * 60,           // 18:00
    london:    2 * 60,           // 02:00
    ny:        8 * 60,           // 08:00
    extended: 17 * 60,           // 17:00 (maint window is no-uptime)
  };
  const startMin = STARTS[session];
  if (startMin === undefined) return null;  // maint / closed → no uptime

  let elapsedMin;
  if (session === 'asia' && nowMin < 18 * 60) {
    // Asia spans 18:00 → 02:00 — past midnight, so today's nowMin came
    // from yesterday's 18:00. Compute as (24h - 18h) + nowMin = 360 + nowMin.
    elapsedMin = (24 * 60 - 18 * 60) + nowMin;
  } else {
    elapsedMin = nowMin - startMin;
  }

  if (elapsedMin < 0) return null;
  return { hours: Math.floor(elapsedMin / 60), minutes: elapsedMin % 60 };
}


function WelcomeHero({ marketOpen }) {
  useLiveTickT();
  const now = useNowClock();
  const session = useLiveSession();
  const uptime = useSessionUptime();
  const open = (marketOpen !== undefined) ? marketOpen : isOpenSession(session);
  const lang = (document.documentElement.lang || 'en').toLowerCase().split('-')[0];
  const localeStr = lang === 'es' ? 'es-ES' : 'en-US';
  const weekdayShort = now.toLocaleDateString(localeStr, { weekday: 'short' });
  const dateShort = now.toLocaleDateString(localeStr, { month: 'short', day: 'numeric', year: 'numeric' });
  const nyTime = now.toLocaleTimeString('en-US', { hour12: false, timeZone: 'America/New_York' });
  const lonTime = now.toLocaleTimeString('en-US', { hour12: false, timeZone: 'Europe/London' });
  return (
    <section className="welcome-hero welcome-hero--compact">
      <div className="welcome-hero__row welcome-hero__row--lead">
        <div className="welcome-hero__lead">
          <span className="eyebrow-compact">{t_chrome('live_tab')}</span>
          {open
            ? <span className="pill live"><span className="dot"></span>{sessionLabel(session)}</span>
            : <span className="pill warn"><span className="dot"></span>{sessionLabel(session)}</span>}
          {uptime && (
            <span className="uptime-chip mono">
              {uptime.hours}h {uptime.minutes}m
            </span>
          )}
          <span className="hero-date">{weekdayShort}, {dateShort}</span>
        </div>
        <div className="welcome-hero__clocks">
          <span className="clock"><span>NY</span><strong>{nyTime}</strong></span>
          <span className="clock optional"><span>LON</span><strong>{lonTime}</strong></span>
          {open
            ? <LivePillWithRefresh seconds={30} session={session} />
            : <span className="pill off"><span className="dot"></span>{t_chrome('market_closed')}</span>}
        </div>
      </div>
      <div className="welcome-hero__row session-key session-key--compact">
        <div className={`session-key__item asia ${session === 'asia' ? 'is-active' : ''}`}>
          <span className="sk-label">{session === 'asia' && <span className="active-dot"></span>}{t_chrome('asia')}</span>
          <span className="sk-time">18:00 – 02:00</span>
        </div>
        <div className={`session-key__item london ${session === 'london' ? 'is-active' : ''}`}>
          <span className="sk-label">{session === 'london' && <span className="active-dot"></span>}{t_chrome('london')}</span>
          <span className="sk-time">02:00 – 08:00</span>
        </div>
        <div className={`session-key__item ny ${session === 'ny' ? 'is-active' : ''}`}>
          <span className="sk-label">{session === 'ny' && <span className="active-dot"></span>}{t_chrome('new_york')}</span>
          <span className="sk-time">08:00 – 17:00</span>
        </div>
        <div className={`session-key__item extended ${session === 'extended' || session === 'maint' ? 'is-active' : ''}`}>
          <span className="sk-label">{(session === 'extended' || session === 'maint') && <span className="active-dot"></span>}{t_chrome('extended')}</span>
          <span className="sk-time">17:00 – 18:00</span>
        </div>
      </div>
    </section>
  );
}

function MarketClosedBanner() {
  return (
    <div className="market-closed-banner">
      <div>
        <strong>{t_chrome('closed_banner_title')}</strong>
        <span style={{marginLeft:8, color:'var(--muted)'}}>{t_chrome('closed_banner_body')}</span>
      </div>
    </div>
  );
}

// Tweaks panel — kept for dev / preview-only. Hidden by default.
function TweaksPanel({ state, setState }) {
  const [open, setOpen] = useStateT(false);
  if (!open) {
    return (
      <button
        className="tweaks-toggle"
        onClick={() => setOpen(true)}
        aria-label="Tweaks"
        title="Tweaks (dev preview)"
      >⚙</button>
    );
  }
  return (
    <aside className="tweaks-panel">
      <header><span>Preview tweaks</span><button onClick={() => setOpen(false)}>×</button></header>
      <div className="tw-row">
        <span>Variation</span>
        <div className="tw-segments">
          {['classic','focus','terminal'].map(v => (
            <button key={v} className={state.variation === v ? 'active':''} onClick={() => setState(s => ({...s, variation: v}))}>{v}</button>
          ))}
        </div>
      </div>
      <div className="tw-row">
        <span>Density</span>
        <div className="tw-segments">
          {['comfortable','compact'].map(d => (
            <button key={d} className={state.density === d ? 'active':''} onClick={() => setState(s => ({...s, density: d}))}>{d}</button>
          ))}
        </div>
      </div>
      <div className="tw-row">
        <span>Accent</span>
        <div className="tw-segments">
          {ACCENTS.map((a, i) => (
            <button key={a.name} className={state.accentIdx === i ? 'active':''} onClick={() => setState(s => ({...s, accentIdx: i}))}>
              <span className="dot" style={{background: a.color, marginRight: 4}}></span>{a.name}
            </button>
          ))}
        </div>
      </div>
      <div className="tw-row">
        <span>Market</span>
        <div className="tw-segments">
          <button className={state.marketOpen === 'auto' || state.marketOpen === undefined ? 'active' : ''} onClick={() => setState(s => ({...s, marketOpen: 'auto'}))}>Auto</button>
          <button className={state.marketOpen === true ? 'active' : ''} onClick={() => setState(s => ({...s, marketOpen: true}))}>Open</button>
          <button className={state.marketOpen === false ? 'active' : ''} onClick={() => setState(s => ({...s, marketOpen: false}))}>Closed</button>
        </div>
      </div>
    </aside>
  );
}

Object.assign(window, { Topbar, WelcomeHero, MarketClosedBanner, TweaksPanel, applyAccent, ACCENTS });
