/* global React, useApi, useMeta, useInfiniteList, LiveMarketCard, LiveMarketGrid, Loading, LoadingBar, ApiError, LivePill, Tag, Sparkline, MDot, SearchInput, ConditionsBuilder, serializeConditions, formatMoney */

// =========================================================================
// CATEGORY PAGE — dynamic slug, real data.
// =========================================================================
function CategoryPage({ navigate, slug }) {
  const { categoryBySlug, models } = useMeta();

  // We rely on the backend to validate the slug — meta might still be loading
  // on the very first render, so don't gate the API calls on it.
  const [resolvedFilter, setResolvedFilter] = React.useState('open');
  const [disagreementOnly, setDisagreementOnly] = React.useState(false);
  // wonByModel = a modelId when the user clicks a ribbon card; null otherwise.
  // Activating this implies resolved=true (model accuracy only exists on
  // resolved markets) — we override the resolved filter on the API call.
  const [wonByModel, setWonByModel] = React.useState(null);
  const [conditions, setConditions] = React.useState({});
  const [searchInput, setSearchInput] = React.useState('');
  const [q, setQ] = React.useState('');

  // Reset search + filters when navigating to a different category so
  // stale state doesn't carry over to a fresh page.
  React.useEffect(() => {
    setSearchInput(''); setQ('');
    setDisagreementOnly(false);
    setWonByModel(null);
    setConditions({});
  }, [slug]);

  const stats = useApi(
    () => slug ? window.CloudLayerAPI.categoryStats(slug).catch((e) => { if (e.status === 404) return null; throw e; }) : Promise.resolve(null),
    [slug],
  );

  // Infinite-scroll the category market grid. Same pattern as the home page.
  const PAGE_SIZE = 24;
  // When a ribbon card is selected, decide which backend filter to use:
  //   • accuracy basis (category has resolved markets) → correctBy (got it right)
  //   • market-agreement basis (no resolutions yet)    → agreedBy (same side as market)
  // Both produce a focused list "this model performed well here" for the
  // metric currently on display.
  const usingAccuracy = stats.data?.sortedBy === 'accuracy';
  const conditionsStr = serializeConditions(conditions);
  const markets = useInfiniteList(
    (offset, limit) =>
      slug
        ? window.CloudLayerAPI.listMarkets({
            category: slug,
            // correctBy implies resolved=true on the backend; agreedBy works
            // on any state, so we leave the user's resolved chip alone for it.
            resolved: wonByModel && usingAccuracy ? 'true' : resolvedFilter,
            disagreement: disagreementOnly ? 'true' : '',
            correctBy: wonByModel && usingAccuracy ? wonByModel : '',
            agreedBy:  wonByModel && !usingAccuracy ? wonByModel : '',
            conditions: conditionsStr,
            q,
            limit,
            offset,
          })
        : Promise.resolve({ items: [], hasMore: false }),
    [slug, resolvedFilter, disagreementOnly, wonByModel, usingAccuracy, conditionsStr, q],
    PAGE_SIZE,
  );

  React.useEffect(() => {
    if (!slug) return;
    const t = setInterval(() => { stats.refresh(); markets.refresh(); }, 60_000);
    return () => clearInterval(t);
  }, [slug, stats.refresh, markets.refresh]);

  // 404 from the stats endpoint (or no slug at all) → not found.
  const notFound = !slug || (stats.data === null && !stats.loading);
  if (notFound) {
    return (
      <div className="container" style={{ padding: '80px 32px' }}>
        <h1 style={{ fontFamily: 'var(--ff-display)', fontSize: 48, fontWeight: 300 }}>Category not found</h1>
        <div className="mono faint" style={{ marginTop: 12, marginBottom: 24 }}>{slug ? `slug: ${slug}` : ''}</div>
        <button className="btn" onClick={() => navigate('home')}>← Back to benchmark</button>
      </div>
    );
  }

  const meta = stats.data?.category || categoryBySlug(slug) || { slug, name: slug, blurb: '' };
  const ribbon = stats.data?.perModel || [];

  return (
    <div data-screen-label="02 Category">
      <div className="page-head">
        <div className="container">
          <div className="crumb">
            <span style={{ cursor: 'pointer' }} onClick={() => navigate('home')}>Benchmark</span>
            <span style={{ margin: '0 8px' }}>/</span>
            <span>Categories</span>
            <span style={{ margin: '0 8px' }}>/</span>
            <span style={{ color: 'var(--fg-dim)' }}>{meta?.name}</span>
          </div>
          {(() => {
            // Headline uses the same #1 from the stats endpoint. When the
            // category has any resolved markets AND the leader actually got
            // some right, we name them; otherwise we fall back to the static
            // "<category> forecasting" framing — claiming "X leads" at 0%
            // accuracy is misleading.
            const onAccuracy = stats.data?.sortedBy === 'accuracy';
            const leader = (stats.data?.perModel || []).find((r) => onAccuracy ? r.resolvedCount > 0 : r.predictions > 0);
            const hasRealLead = leader && onAccuracy && (leader.accuracy || 0) > 0;
            return (
              <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 32 }}>
                <h1 style={{ flex: 1, margin: 0, fontSize: 72, fontFamily: 'var(--ff-display)', fontWeight: 300, letterSpacing: '-0.025em' }}>
                  {hasRealLead ? (
                    <>
                      <span style={{ color: leader.color }}>{leader.short || leader.modelLabel}</span>{' '}
                      <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>leads</em>{' '}
                      {meta?.name?.toLowerCase()}
                    </>
                  ) : (
                    <>
                      {meta?.name}{' '}
                      <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>forecasting</em>
                    </>
                  )}
                </h1>
                <div className="row gap-8">
                  <LivePill>Live</LivePill>
                  <button className="btn" onClick={() => navigate('share')}>Share ↗</button>
                </div>
              </div>
            );
          })()}
          {meta?.blurb && (
            <div className="dim" style={{ marginTop: 18, fontSize: 16, maxWidth: 720 }}>{meta.blurb}</div>
          )}
          <div className="stat-strip">
            <div className="stat">
              <div className="label">Active markets</div>
              <div className="value mono">{stats.data?.markets ?? '—'}</div>
            </div>
            <div className="stat">
              <div className="label">Volume tracked</div>
              <div className="value mono">{formatMoney(stats.data?.volume || 0)}</div>
            </div>
            <div className="stat">
              <div className="label">Avg market YES</div>
              <div className="value mono">
                {stats.data ? `${Math.round(stats.data.avgMarketYes * 100)}%` : '—'}
              </div>
            </div>
            <div className="stat">
              {(() => {
                const onAccuracy = stats.data?.sortedBy === 'accuracy';
                const leader = (stats.data?.perModel || []).find((r) => onAccuracy ? r.resolvedCount > 0 : r.predictions > 0);
                // No leader at all → "—". On the accuracy basis, a 0% leader
                // isn't a leader either — say so explicitly.
                if (!leader) {
                  return <>
                    <div className="label">Leading</div>
                    <div className="value mono" style={{ fontSize: 22, color: 'var(--fg-faint)' }}>—</div>
                  </>;
                }
                const pct = onAccuracy ? leader.accuracy : leader.marketAgreement;
                if (onAccuracy && (pct || 0) <= 0) {
                  return <>
                    <div className="label">Leading · resolved</div>
                    <div className="value mono" style={{ fontSize: 18, color: 'var(--fg-faint)', fontStyle: 'italic', fontFamily: 'var(--ff-display)' }}>
                      no model has called one right
                    </div>
                  </>;
                }
                return <>
                  <div className="label">
                    Leading · {onAccuracy ? 'resolved' : 'agreement'}
                  </div>
                  <div className="value mono" style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
                    <span style={{ width: 12, height: 12, borderRadius: '50%', background: leader.color, display: 'inline-block', alignSelf: 'center' }} />
                    <span style={{ fontFamily: 'var(--ff-display)', fontSize: 24, color: 'var(--fg)' }}>
                      {leader.short || leader.modelLabel}
                    </span>
                    <span className="mono tnum" style={{ fontSize: 18, color: leader.color }}>
                      {Math.round((pct || 0) * 100)}%
                    </span>
                  </div>
                </>;
              })()}
            </div>
          </div>
        </div>
      </div>

      {/* ACCURACY RIBBON */}
      <section className="container" style={{ padding: '48px 32px 0' }}>
        <div className="between" style={{ marginBottom: 16 }}>
          <div>
            <div className="uppercase faint">
              {stats.data?.sortedBy === 'accuracy'
                ? 'Accuracy · resolved markets'
                : 'Market agreement · pre-resolution proxy'}
            </div>
            <h2 style={{ fontFamily: 'var(--ff-display)', fontWeight: 400, fontSize: 24, letterSpacing: '-0.01em', margin: '4px 0 0' }}>
              {ribbon.length || 'All'} models on <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>{meta?.name?.toLowerCase()}</em>
            </h2>
          </div>
          <span className="mono faint" style={{ fontSize: 11, maxWidth: 360, textAlign: 'right', lineHeight: 1.4 }}>
            {stats.data?.sortedBy === 'accuracy'
              ? 'Real ground-truth on the markets in this category that Polymarket has resolved.'
              : 'Until markets resolve, this is the rate at which each model lands on the same side of 50% as the market line.'}
          </span>
        </div>

        {stats.error ? (
          <ApiError error={stats.error} />
        ) : stats.loading && !stats.data ? (
          <Loading label="Loading models…" />
        ) : (
          <div className="ribbon">
            {ribbon.map((row, idx) => {
              const showAccuracy = stats.data?.sortedBy === 'accuracy';
              const value = showAccuracy ? row.accuracy : row.marketAgreement;
              const denom = showAccuracy ? row.resolvedCount : row.predictions;
              const num = showAccuracy ? row.correct : row.agreed;
              const hasData = showAccuracy ? row.resolvedCount > 0 : row.predictions > 0;
              // Click is always enabled if this model has predictions in the
              // category. The underlying filter adapts to whatever metric the
              // ribbon is showing:
              //   accuracy basis  → markets the model GOT RIGHT
              //   agreement basis → markets the model AGREED WITH THE MARKET
              const clickable = showAccuracy
                ? row.resolvedCount > 0 && (row.correct || 0) > 0
                : row.predictions > 0 && (row.agreed || 0) > 0;
              const isActive = wonByModel === row.modelId;
              return (
                <button
                  key={row.modelId}
                  type="button"
                  onClick={() => clickable && setWonByModel(isActive ? null : row.modelId)}
                  disabled={!clickable}
                  className="ribbon-card"
                  title={
                    !clickable
                      ? (showAccuracy ? 'No correct calls to filter on yet' : 'No predictions to filter on yet')
                      : (isActive
                          ? 'Clear this filter'
                          : showAccuracy
                            ? `Show markets ${row.modelLabel} called right`
                            : `Show markets ${row.modelLabel} agreed with the market line`)
                  }
                  style={{
                    border: 0,
                    textAlign: 'left',
                    background: isActive ? 'var(--bg-elev)' : 'var(--bg)',
                    cursor: clickable ? 'pointer' : 'default',
                    outline: isActive ? `1px solid ${row.color}` : 'none',
                    outlineOffset: '-1px',
                    transition: 'background 120ms, outline-color 120ms',
                  }}
                >
                  <span className="rank">#{idx + 1}</span>
                  <div className="mname">
                    <span className="mdot" style={{ background: row.color, width: 9, height: 9, borderRadius: '50%', display: 'inline-block', opacity: row.enabled ? 1 : 0.35 }} />
                    {row.modelLabel}
                    {!row.enabled && <span className="mono faint" style={{ fontSize: 9, marginLeft: 6 }}>off</span>}
                  </div>
                  <div className="brier" style={{ color: isActive ? row.color : undefined }}>
                    {!hasData ? '—' : `${Math.round((value || 0) * 100)}%`}
                  </div>
                  <div className="brier-label">
                    {!hasData
                      ? (showAccuracy ? 'no resolutions yet' : 'awaiting data')
                      : `${num} of ${denom}`}
                  </div>
                  {clickable && (
                    <div className="mono" style={{
                      position: 'absolute', bottom: 10, right: 14,
                      fontSize: 9.5, letterSpacing: '0.08em',
                      color: isActive ? row.color : 'var(--fg-faint)',
                      textTransform: 'uppercase',
                    }}>
                      {isActive ? '✓ filtered' : 'click to filter'}
                    </div>
                  )}
                </button>
              );
            })}
          </div>
        )}
      </section>

      {/* MARKETS LIST */}
      <section className="container" style={{ padding: '64px 32px 96px' }}>
        <div className="between" style={{ marginBottom: 20 }}>
          <h2 style={{ fontFamily: 'var(--ff-display)', fontWeight: 300, fontSize: 28, letterSpacing: '-0.02em', margin: 0 }}>
            {wonByModel ? (() => {
              const w = (stats.data?.perModel || []).find((r) => r.modelId === wonByModel);
              const label = usingAccuracy ? 'called these right' : 'agreed with the market';
              return <>
                <span style={{ color: w?.color || 'var(--fg)' }}>{w?.short || w?.modelLabel || wonByModel}</span>{' '}
                <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>{label}</em>
              </>;
            })()
              : resolvedFilter === 'true' ? <>Resolved <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>markets</em></>
              : resolvedFilter === 'all' ? <>All <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>markets</em></>
              : <>Currently <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>active</em></>}
          </h2>
          <div className="row gap-12">
            <SearchInput
              value={searchInput}
              onChange={setSearchInput}
              onCommit={setQ}
              placeholder={`Search ${meta?.name?.toLowerCase() || ''}…`}
            />
            {wonByModel && (() => {
              const w = (stats.data?.perModel || []).find((r) => r.modelId === wonByModel);
              return (
                <button
                  className="fchip active"
                  onClick={() => setWonByModel(null)}
                  style={{ borderColor: w?.color, color: w?.color, display: 'inline-flex', gap: 6, alignItems: 'center' }}
                  title="Clear model filter"
                >
                  <span style={{ width: 8, height: 8, borderRadius: '50%', background: w?.color, display: 'inline-block' }} />
                  {w?.short || wonByModel} {usingAccuracy ? 'called right' : 'agreed with market'}
                  <span style={{ marginLeft: 4, opacity: 0.7 }}>×</span>
                </button>
              );
            })()}
            <div className="filter-row" style={{ opacity: wonByModel ? 0.4 : 1, pointerEvents: wonByModel ? 'none' : 'auto' }}>
              <button className={`fchip ${resolvedFilter === 'open' ? 'active' : ''}`} onClick={() => setResolvedFilter('open')}>Open</button>
              <button className={`fchip ${resolvedFilter === 'true' ? 'active' : ''}`} onClick={() => setResolvedFilter('true')}>Resolved</button>
              <button className={`fchip ${resolvedFilter === 'all' ? 'active' : ''}`} onClick={() => setResolvedFilter('all')}>All</button>
              <button
                className={`fchip ${disagreementOnly ? 'active' : ''}`}
                onClick={() => setDisagreementOnly((v) => !v)}
                title="Markets where models split YES vs NO"
              >
                ⚡ Disagree
              </button>
              <ConditionsBuilder
                value={conditions}
                onChange={setConditions}
                modelsList={models}
              />
            </div>
            <span className="mono faint" style={{ fontSize: 11 }}>
              {markets.total != null && markets.total > markets.items.length
                ? <>{markets.items.length} <span style={{ opacity: 0.6 }}>of</span> {markets.total} markets</>
                : <>{markets.total ?? markets.items.length} {(markets.total ?? markets.items.length) === 1 ? 'market' : 'markets'}</>}
            </span>
          </div>
        </div>

        {markets.error ? (
          <ApiError error={markets.error} />
        ) : markets.loading && markets.items.length === 0 ? (
          <Loading label="Loading markets…" height={400} />
        ) : markets.items.length === 0 ? (
          <div className="panel" style={{ padding: 28, textAlign: 'center' }}>
            <div className="dim">
              {q
                ? <>No markets match <em style={{ color: 'var(--fg)' }}>"{q}"</em> in {meta?.name?.toLowerCase()}.</>
                : disagreementOnly
                ? <>No model disagreements in {meta?.name?.toLowerCase()} right now.</>
                : 'No markets in this category.'}
            </div>
          </div>
        ) : (
          <>
            {/* Subtle progress bar shown while refetching with items already
                on screen. Keeps the grid visible instead of blanking. */}
            {markets.loading && <LoadingBar />}

            <div
              className="mgrid"
              style={{
                opacity: markets.loading ? 0.45 : 1,
                transition: 'opacity 180ms ease',
                pointerEvents: markets.loading ? 'none' : 'auto',
              }}
            >
              {markets.items.map((m) => (
                <LiveMarketCard key={m.polymarketId} market={m} navigate={navigate} />
              ))}
            </div>

            {/* Invisible sentinel — fetches the next page when it nears the viewport. */}
            <div
              ref={markets.sentinelRef}
              aria-hidden={markets.hasMore ? 'true' : undefined}
              style={{
                marginTop: 24,
                padding: markets.hasMore ? '8px 0' : '32px 0',
                textAlign: 'center',
                minHeight: markets.hasMore ? 8 : 40,
              }}
            >
              {!markets.hasMore && markets.items.length > 0 && (
                <span className="mono faint" style={{ fontSize: 11, opacity: 0.5 }}>
                  end of list · {markets.items.length} markets
                </span>
              )}
            </div>
          </>
        )}
      </section>
    </div>
  );
}

window.CategoryPage = CategoryPage;
