/* ════════════════════════════════════════════════════════════════════
   Austria Leaderboard — app
   ════════════════════════════════════════════════════════════════════ */
const { useState, useMemo, useEffect } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "dark",
  "hero": "editorial",
  "roleConfig": "sidebar",
  "language": "de"
}/*EDITMODE-END*/;

const COPY = {
  de: {
    nav: { board: 'Bestenliste', roles: 'Rollen', events: 'Termine', rules: 'Hausordnung' },
    cta: 'Discord beitreten',
    heroEyebrow: 'Austria · Discord-Bestenliste',
    heroTitle: 'Bestenliste',
    statMembers: 'Mitglieder gesamt',
    statXP: 'Erfahrung gesamt',
    statMsg: 'Nachrichten gesamt',
    statRoles: 'Rollen',
    podiumEyebrow: 'Stockerl · die drei Bestplatzierten',
    podiumTitle: 'Stockerl',
    podiumFirst: 'Erster',
    podiumSecond: 'Zweiter',
    podiumThird: 'Dritter',
    boardTitle: 'Bestenliste',
    boardRangeFmt: (a, b, n) => `Rang ${a}–${b} · ${n} Mitglieder`,
    th: { rank: 'Rang', member: 'Mitglied', role: 'Rolle', xp: 'Erfahrung', msg: 'Nachrichten', lvl: 'Stufe' },
    rc: { title: 'Rollenstufen', count: (n) => `${n} Stufen`, foot: 'Erfahrung durch Schreiben sammeln. Stufe halten oder die nächste Rolle erringen.' },
    legend: 'Stufen',
    searchPh: 'Mitglieder oder Rollen suchen…',
    nothing: 'Nichts gefunden für',
    tryAgain: 'Anderer Name oder andere Rolle?',
    prev: '← Zurück', next: 'Weiter →',
    pStat: { xp: 'XP', lvl: 'Stufe', msg: 'Nachr.' },
    toLevel: (l) => `auf St. ${l}`,
    motto: 'In Braka we trust',
    footMeta: 'Austria · Bestenliste · live',
    footUpdated: 'Aktualisiert', footDisc: 'Discord',
    loading: 'Lade Bestenliste…',
    offline: 'API nicht erreichbar · zeige Beispieldaten',
  },
  en: {
    nav: { board: 'Leaderboard', roles: 'Roles', events: 'Events', rules: 'Rules' },
    cta: 'Join Discord',
    heroEyebrow: 'Austria · Discord Leaderboard',
    heroTitle: 'Leaderboard',
    statMembers: 'Members total',
    statXP: 'Total XP earned',
    statMsg: 'Messages sent',
    statRoles: 'Roles',
    podiumEyebrow: 'Top three',
    podiumTitle: 'Podium',
    podiumFirst: 'First',
    podiumSecond: 'Second',
    podiumThird: 'Third',
    boardTitle: 'Leaderboard',
    boardRangeFmt: (a, b, n) => `Rank ${a}–${b} · ${n} members`,
    th: { rank: 'Rank', member: 'Member', role: 'Role', xp: 'Experience', msg: 'Messages', lvl: 'Level' },
    rc: { title: 'Role config', count: (n) => `${n} tiers`, foot: 'Earn XP by chatting. Level up to unlock the next tier.' },
    legend: 'Tiers',
    searchPh: 'Search members or roles…',
    nothing: 'No results for',
    tryAgain: 'Try a different name or role.',
    prev: '← Prev', next: 'Next →',
    pStat: { xp: 'XP', lvl: 'Lvl', msg: 'Msgs' },
    toLevel: (l) => `to L${l}`,
    motto: 'In Braka we trust',
    footMeta: 'Austria · Leaderboard · live',
    footUpdated: 'Updated', footDisc: 'Discord',
    loading: 'Loading leaderboard…',
    offline: 'API unreachable · showing sample data',
  }
};

// Austria coat-of-arms — Bindenschild + simplified mural crown
function AustriaShield({ size = 64, withCrown = true }) {
  const w = size;
  const h = size * 1.32;
  return (
    <svg width={w} height={h} viewBox="0 0 100 132" aria-label="Österreich">
      <defs>
        <clipPath id={`sc-${size}`}>
          <path d="M50 18 L100 18 L100 76 Q100 110 50 130 Q0 110 0 76 L0 18 Z" />
        </clipPath>
      </defs>
      {withCrown && (
        <g fill="#C8A24A">
          <rect x="10" y="10" width="80" height="8" rx="1.5" />
          <path d="M14 10 L14 4 L20 4 L20 7 L26 7 L26 4 L32 4 L32 10 Z" />
          <path d="M40 10 L40 2 L46 2 L46 5 L54 5 L54 2 L60 2 L60 10 Z" />
          <path d="M68 10 L68 4 L74 4 L74 7 L80 7 L80 4 L86 4 L86 10 Z" />
        </g>
      )}
      <g clipPath={`url(#sc-${size})`}>
        <rect x="0" y="18"  width="100" height="38" fill="#C8102E" />
        <rect x="0" y="56"  width="100" height="20" fill="#ffffff" />
        <rect x="0" y="76"  width="100" height="54" fill="#C8102E" />
      </g>
      <path
        d="M50 18 L100 18 L100 76 Q100 110 50 130 Q0 110 0 76 L0 18 Z"
        fill="none"
        stroke="rgba(0,0,0,0.22)"
        strokeWidth="0.8"
      />
    </svg>
  );
}

function Avatar({ member, size = 40 }) {
  const [errored, setErrored] = useState(false);
  if (member.avatarUrl && !errored) {
    return (
      <img
        className="avatar-c avatar-img"
        style={{ width: size, height: size }}
        src={member.avatarUrl}
        alt={member.name}
        loading="lazy"
        onError={() => setErrored(true)}
      />
    );
  }
  const style = {
    width: size, height: size, fontSize: size * 0.36,
    background: `linear-gradient(135deg, hsl(${member.avatarHue} 70% 55%), hsl(${member.avatarHue2} 65% 45%))`
  };
  return <div className="avatar-c" style={style}>{member.initials}</div>;
}

const fmt = new Intl.NumberFormat('de-AT');
const num = (n) => fmt.format(n);

function RoleConfigSidebar({ roles, copy }) {
  return (
    <aside className="role-config sidebar">
      <div className="rc-head">
        <span className="eyebrow">{copy.rc.title}</span>
        <span className="rc-count mono">{copy.rc.count(roles.length)}</span>
      </div>
      <ol className="rc-list">
        {roles.map((r) => (
          <li key={r.name} className="rc-row">
            <span className="rc-lvl mono">{String(r.lvl).padStart(2, '0')}</span>
            <span className="rc-dot" style={{ background: r.color }} />
            <span className="rc-name" style={{ color: r.color }}>{r.name}</span>
          </li>
        ))}
      </ol>
      <div className="rc-foot">
        <span className="caption">{copy.rc.foot}</span>
      </div>
    </aside>
  );
}

function RoleConfigCollapsed({ roles, copy }) {
  const [open, setOpen] = useState(false);
  return (
    <details className="role-config collapsed" open={open} onToggle={(e) => setOpen(e.target.open)}>
      <summary>
        <span className="eyebrow">{copy.rc.title}</span>
        <span className="rc-count mono">{copy.rc.count(roles.length)}</span>
        <span className="rc-chevron" aria-hidden>▾</span>
      </summary>
      <ol className="rc-list horizontal">
        {roles.map((r) => (
          <li key={r.name} className="rc-row">
            <span className="rc-lvl mono">{String(r.lvl).padStart(2, '0')}</span>
            <span className="rc-name" style={{ color: r.color }}>{r.name}</span>
          </li>
        ))}
      </ol>
    </details>
  );
}

function RoleLegendInline({ roles, copy }) {
  return (
    <div className="role-legend-inline">
      <span className="eyebrow">{copy.legend}</span>
      <div className="legend-track">
        {roles.map((r) => (
          <span key={r.name} className="legend-chip" title={`Stufe ${r.lvl} · ${r.name}`}>
            <span className="legend-dot" style={{ background: r.color }} />
            <span className="legend-lvl mono">{r.lvl}</span>
          </span>
        ))}
      </div>
    </div>
  );
}

function Hero({ variant, total, guildTotal, totalXP, totalMsg, copy }) {
  // Prefer actual Discord member count from the guild API when available
  const memberDisplay = guildTotal != null ? num(guildTotal) : (total ? num(total) : '—');
  return (
    <header className={`hero hero-${variant}`}>
      <div className="hero-meta">
        <span className="eyebrow">{copy.heroEyebrow}</span>
      </div>
      <div className="hero-body">
        <div className="hero-mark">
          <AustriaShield size={variant === 'gradient' ? 88 : 104} />
        </div>
        <div className="hero-text">
          <h1 className="hero-title">
            Austria<br />
            <span className="hero-title-second">{copy.heroTitle}</span>
          </h1>
          <div className="hero-motto">
            <span className="motto-bar" />
            <span className="mono motto-text">{copy.motto.toUpperCase()}</span>
          </div>
        </div>
      </div>
      <div className="hero-stats">
        <Stat label={copy.statMembers} value={memberDisplay} />
        <Stat label={copy.statXP}      value={totalXP ? num(totalXP) : '—'} />
        <Stat label={copy.statMsg}     value={totalMsg ? num(totalMsg) : '—'} />
        <Stat label={copy.statRoles}   value={String(window.ROLES.length)} />
      </div>
    </header>
  );
}

function Stat({ label, value }) {
  return (
    <div className="stat-c">
      <span className="stat-label eyebrow">{label}</span>
      <span className="stat-value">{value}</span>
    </div>
  );
}

function Podium({ members, copy }) {
  const [first, second, third] = members;
  return (
    <section className="podium" aria-label={copy.podiumTitle}>
      <PodiumCard member={second} rank={2} label={copy.podiumSecond} copy={copy} />
      <PodiumCard member={first}  rank={1} label={copy.podiumFirst} featured copy={copy} />
      <PodiumCard member={third}  rank={3} label={copy.podiumThird} copy={copy} />
    </section>
  );
}

function PodiumCard({ member, rank, label, featured = false, copy }) {
  return (
    <article className={`podium-card${featured ? ' featured' : ''}`} style={{ '--accent': member.roleColor }}>
      <div className="podium-rank">
        <span className="podium-rank-num">{rank}</span>
        <span className="podium-rank-suffix mono">{label}</span>
      </div>
      <div className="podium-avatar-wrap">
        <Avatar member={member} size={featured ? 96 : 72} />
        {featured && <span className="podium-crown" aria-hidden>★</span>}
      </div>
      <div className="podium-name">{member.name}</div>
      <div className="podium-role">
        <span className="role-dot" style={{ background: member.roleColor }} />
        <span style={{ color: member.roleColor }}>{member.roleName}</span>
      </div>
      <dl className="podium-stats">
        <div><dt className="eyebrow">{copy.pStat.xp}</dt><dd className="mono">{num(member.xp)}</dd></div>
        <div><dt className="eyebrow">{copy.pStat.lvl}</dt><dd className="mono">{member.lvl}</dd></div>
        <div><dt className="eyebrow">{copy.pStat.msg}</dt><dd className="mono">{num(member.msg)}</dd></div>
      </dl>
      <div className="podium-progress" title={`${member.progressNext}% ${copy.toLevel(member.lvl + 1)}`}>
        <div className="podium-progress-fill" style={{ width: `${member.progressNext}%`, background: member.roleColor }} />
      </div>
      <div className="podium-progress-label">
        <span className="mono">{member.progressNext}%</span>
        <span className="caption">{copy.toLevel(member.lvl + 1)}</span>
      </div>
    </article>
  );
}

function MemberRow({ member, rank }) {
  return (
    <li className="lb-row" style={{ '--tier': member.roleColor }}>
      <div className="lb-row-main">
        <div className="lb-rank mono">{String(rank).padStart(2, '0')}</div>
        <div className="lb-member">
          <Avatar member={member} size={40} />
          <div className="lb-id">
            <div className="lb-name-line">
              <span className="lb-name">{member.name}</span>
            </div>
            <div className="lb-handle mono">{member.handle}</div>
          </div>
        </div>
        <div className="lb-role">
          <span className="role-dot" style={{ background: member.roleColor }} />
          <span style={{ color: member.roleColor }}>{member.roleName}</span>
        </div>
        <div className="lb-xp">{num(member.xp)}</div>
        <div className="lb-msg">{num(member.msg)}</div>
        <div className="lb-level">
          <span className="lb-level-num">{member.lvl}</span>
        </div>
      </div>
      <div className="lb-progress-strip" title={`${member.progressNext}% to L${member.lvl + 1}`}>
        <div className="lb-progress-bar" style={{ width: `${member.progressNext}%` }} />
        <div className="lb-progress-label">
          {num(member.xpInLevel)} / {num(member.xpNeeded)} XP · {member.progressNext}%
        </div>
      </div>
    </li>
  );
}

function Pagination({ page, pages, onPage, copy }) {
  const items = [];
  for (let i = 1; i <= pages; i++) items.push(i);
  return (
    <nav className="pagination" aria-label="Pagination">
      <button className="pg-btn" onClick={() => onPage(Math.max(1, page - 1))} disabled={page === 1}>{copy.prev}</button>
      <div className="pg-pages">
        {items.map((i) => (
          <button
            key={i}
            className={`pg-num mono${i === page ? ' active' : ''}`}
            onClick={() => onPage(i)}
          >{i}</button>
        ))}
      </div>
      <button className="pg-btn" onClick={() => onPage(Math.min(pages, page + 1))} disabled={page === pages}>{copy.next}</button>
    </nav>
  );
}

function SearchInput({ value, onChange, placeholder }) {
  return (
    <label className="search-c">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
        <circle cx="11" cy="11" r="7" />
        <path d="M21 21l-4.3-4.3" />
      </svg>
      <input
        type="text"
        placeholder={placeholder}
        value={value}
        onChange={(e) => onChange(e.target.value)}
      />
      {value && <button type="button" className="search-clear" onClick={() => onChange('')} aria-label="Clear">×</button>}
    </label>
  );
}

function SectionHeader({ eyebrow, title, right }) {
  return (
    <div className="section-head">
      <div>
        <span className="eyebrow">{eyebrow}</span>
        <h2 className="section-title-c">{title}</h2>
      </div>
      {right && <div className="section-right">{right}</div>}
    </div>
  );
}

function Footer({ copy, live, fetchedAt }) {
  const timestamp = useMemo(() => {
    if (!fetchedAt) return null;
    try {
      return new Intl.DateTimeFormat('de-AT', {
        day: '2-digit', month: '2-digit', year: 'numeric',
        hour: '2-digit', minute: '2-digit',
      }).format(new Date(fetchedAt));
    } catch {
      return null;
    }
  }, [fetchedAt]);

  return (
    <footer className="footer-c">
      <div className="footer-l">
        <AustriaShield size={20} withCrown={false} />
        <span className="mono">{copy.footMeta}</span>
      </div>
      <div className="footer-r mono">
        <span className={live ? 'live-pill live' : 'live-pill offline'}>
          <span className="live-dot" />
          {live ? 'live' : 'offline'}
        </span>
        <span>·</span>
        {timestamp
          ? <span title={fetchedAt}>{copy.footUpdated}: {timestamp}</span>
          : <span>{copy.footUpdated}: —</span>
        }
        <span>·</span>
        <a href="https://discord.gg/austria" target="_blank" rel="noopener">{copy.footDisc}</a>
      </div>
    </footer>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [search, setSearch] = useState('');
  const [page, setPage] = useState(1);
  const [members, setMembers] = useState([]);
  const [total, setTotal] = useState(null);
  const [guildTotal, setGuildTotal] = useState(null);
  const [fetchedAt, setFetchedAt] = useState(null);
  const [live, setLive] = useState(true);
  const [loading, setLoading] = useState(true);
  const PER_PAGE = 25;
  const copy = COPY[t.language] || COPY.de;

  useEffect(() => { document.documentElement.dataset.theme = t.theme; }, [t.theme]);
  useEffect(() => { document.documentElement.lang = t.language; }, [t.language]);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    window.fetchLeaderboard().then((res) => {
      if (cancelled) return;
      setMembers(res.members);
      setTotal(res.total);
      setGuildTotal(res.guildTotal);
      setFetchedAt(res.fetchedAt);
      setLive(res.live);
      setLoading(false);
    });
    return () => { cancelled = true; };
  }, []);

  const filtered = useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return members;
    return members.filter((m) =>
      m.name.toLowerCase().includes(q) ||
      (m.handle || '').toLowerCase().includes(q) ||
      (m.roleName || '').toLowerCase().includes(q)
    );
  }, [search, members]);

  const pages = Math.max(1, Math.ceil(filtered.length / PER_PAGE));
  const safePage = Math.min(page, pages);
  const pageMembers = filtered.slice((safePage - 1) * PER_PAGE, safePage * PER_PAGE);
  useEffect(() => { setPage(1); }, [search]);

  const totalXP  = useMemo(() => members.reduce((s, m) => s + (m.xp || 0), 0), [members]);
  const totalMsg = useMemo(() => members.reduce((s, m) => s + (m.msg || 0), 0), [members]);
  const topThree = members.slice(0, 3);
  const layoutClass = t.roleConfig === 'sidebar' ? 'layout-sidebar' : 'layout-stacked';

  return (
    <div className="page">
      <div className="page-banner" aria-hidden />
      <main className="container">
        <Hero
          variant={t.hero}
          total={total}
          guildTotal={guildTotal}
          totalXP={totalXP}
          totalMsg={totalMsg}
          copy={copy}
        />

        {!live && !loading && (
          <div className="banner-offline"><span className="eyebrow">⚠ {copy.offline}</span></div>
        )}

        {loading ? (
          <div className="loading-state"><span className="eyebrow">{copy.loading}</span></div>
        ) : (
          <>
            {topThree.length === 3 && (
              <section className="podium-section">
                <SectionHeader eyebrow={copy.podiumEyebrow} title={copy.podiumTitle} />
                <Podium members={topThree} copy={copy} />
              </section>
            )}

            <section className={`board ${layoutClass}`}>
              {t.roleConfig === 'sidebar' && <RoleConfigSidebar roles={window.ROLES} copy={copy} />}

              <div className="board-main">
                {t.roleConfig === 'collapsed' && <RoleConfigCollapsed roles={window.ROLES} copy={copy} />}
                {t.roleConfig === 'inline' && <RoleLegendInline roles={window.ROLES} copy={copy} />}

                <SectionHeader
                  eyebrow={copy.boardRangeFmt((safePage - 1) * PER_PAGE + 1, Math.min(safePage * PER_PAGE, filtered.length), num(filtered.length))}
                  title={copy.boardTitle}
                  right={<SearchInput value={search} onChange={setSearch} placeholder={copy.searchPh} />}
                />

                {filtered.length === 0 ? (
                  <div className="empty-state">
                    <span className="eyebrow">{copy.nothing} "{search}"</span>
                    <p>{copy.tryAgain}</p>
                  </div>
                ) : (
                  <ul className="leaderboard">
                    <li className="lb-head">
                      <div className="lb-rank eyebrow">{copy.th.rank}</div>
                      <div className="lb-member eyebrow">{copy.th.member}</div>
                      <div className="lb-role eyebrow">{copy.th.role}</div>
                      <div className="lb-xp eyebrow">{copy.th.xp}</div>
                      <div className="lb-msg eyebrow">{copy.th.msg}</div>
                      <div className="lb-level eyebrow">{copy.th.lvl}</div>
                    </li>
                    {pageMembers.map((m) => (
                      <MemberRow key={m.id} member={m} rank={m.rank} />
                    ))}
                  </ul>
                )}

                {filtered.length > PER_PAGE && (
                  <Pagination page={safePage} pages={pages} onPage={setPage} copy={copy} />
                )}
              </div>
            </section>
          </>
        )}

        <Footer copy={copy} live={live} fetchedAt={fetchedAt} />
      </main>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Sprache · Language">
          <TweakRadio
            label="Sprache"
            value={t.language}
            options={[{ label: 'Deutsch', value: 'de' }, { label: 'English', value: 'en' }]}
            onChange={(v) => setTweak('language', v)}
          />
        </TweakSection>
        <TweakSection label="Theme">
          <TweakRadio
            label="Modus"
            value={t.theme}
            options={[{ label: 'Dunkel', value: 'dark' }, { label: 'Hell', value: 'light' }]}
            onChange={(v) => setTweak('theme', v)}
          />
        </TweakSection>
        <TweakSection label="Rollenstufen">
          <TweakRadio
            label="Layout"
            value={t.roleConfig}
            options={[
              { label: 'Seite',        value: 'sidebar' },
              { label: 'Eingeklappt', value: 'collapsed' },
              { label: 'Inline',       value: 'inline' }
            ]}
            onChange={(v) => setTweak('roleConfig', v)}
          />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

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