// Predictions — its own mode.
//
// A slate is a frozen set of fixtures, built in SQL by md_build_slate and the
// same for everyone, which is what lets slate scores compare the way matchday
// scores do. The alternative — pick any fixture you like — turns the leaderboard
// into a ranking of who chose the easiest games.
//
// Everything on this screen comes from one call (md_current_slate): the
// fixtures, the kits, whether each one is still open, my picks, and the result
// once it is in. Saving is per fixture, because an early kickoff has to lock on
// its own while the rest of the slate stays open.

function PredictScreen() {
  const T = window.T;

  const [slate,  setSlate]  = React.useState(undefined);  // undefined=loading, null=none
  const [view,   setView]   = React.useState('slate');    // slate | table
  const [board,  setBoard]  = React.useState(null);
  const [askName, setAskName] = React.useState(false);
  const [name,   setName]   = React.useState(
    () => (window.getSavedName && window.getSavedName()) || null);

  // null means "whichever slate is live". Set when the player picks a day.
  const [slateId, setSlateId] = React.useState(null);

  const anonId = window.getAnonId ? window.getAnonId() : null;

  const load = React.useCallback(() => {
    if (!window.fetchSlateView) { setSlate(null); return; }
    window.fetchSlateView(slateId, anonId)
      .then(s => setSlate(s || null))
      .catch(() => setSlate(null));
  }, [anonId, slateId]);

  React.useEffect(() => { load(); }, [load]);

  React.useEffect(() => {
    if (view !== 'table' || board) return;
    if (!window.fetchSlateLeaderboard) { setBoard([]); return; }
    window.fetchSlateLeaderboard('all', 25)
      .then(r => setBoard(r || []))
      .catch(() => setBoard([]));
  }, [view, board]);

  // ── Local echo of a saved pick ──────────────────────────────────────────────
  // The screen updates from its own state rather than re-fetching the slate on
  // every save. Eight fixtures means up to eight saves in a sitting, and a full
  // reload after each one would make the whole list flicker.
  const applyPick = (fixtureId, home, away) => {
    setSlate(s => !s ? s : ({
      ...s,
      fixtures: s.fixtures.map(f => f.fixture_id === fixtureId
        ? { ...f, mine: { home_goals: home, away_goals: away, points_earned: null } }
        : f),
    }));
  };

  const fixtures = slate?.fixtures || [];
  const openCount = fixtures.filter(f => f.open).length;
  const madeCount = fixtures.filter(f => f.mine).length;
  const settled   = fixtures.filter(f => f.home_score !== null && f.mine);
  const earned    = settled.reduce((n, f) => n + (f.mine.points_earned || 0), 0);

  return (
    <Content>
      <AppHeader left={<PageWordmark text="PREDICT" />} />

      {slate === undefined && (
        <div style={{ padding: '40px 0', textAlign: 'center',
          fontFamily: 'DM Sans', fontSize: 12, color: T.textSec }}>
          Loading the slate…
        </div>
      )}

      {slate === null && (
        <>
          <PageTitleBlock title="No Slate Yet"
            sub="Predictions open when the next slate is built" />
          <div style={{ background: T.card, borderRadius: 16, padding: 18,
            border: `1px dashed ${T.border}`, textAlign: 'center' }}>
            <div style={{ fontSize: 26, marginBottom: 8 }}>🔮</div>
            <div style={{ fontFamily: 'DM Sans', fontSize: 12.5, color: T.textSec,
              lineHeight: 1.6 }}>
              Every slate is the same eight fixtures for everyone — so the table
              ranks how well you read the games, not which ones you picked.
              Check back before the next round.
            </div>
          </div>
        </>
      )}

      {slate && (
        <>
          <PageTitleBlock
            title={slate.name}
            sub={openCount > 0
              ? `${madeCount} of ${fixtures.length} called · 5 pts for an exact score`
              : settled.length > 0
                ? `${earned} ${earned === 1 ? 'point' : 'points'} from this slate`
                : 'All locked — results coming in'} />

          <div style={{ display: 'flex', gap: 6, marginBottom: 14 }}>
            {[['slate', 'The Slate'], ['table', 'Table']].map(([id, label]) => (
              <button key={id} onClick={() => setView(id)} style={{
                flex: 1, padding: '9px 0', borderRadius: 10, cursor: 'pointer',
                fontFamily: 'DM Sans', fontSize: 12, fontWeight: 700,
                border: `1px solid ${view === id ? 'rgba(232,184,32,0.35)' : T.border}`,
                background: view === id ? 'rgba(232,184,32,0.12)' : 'transparent',
                color: view === id ? T.gold400 : T.textSec,
                transition: 'all 180ms ease',
              }}>{label}</button>
            ))}
          </div>

          {/* Day switcher. A weekend is two slates — Saturday and Sunday are
              built separately so the bigger day cannot crowd the other out —
              so the screen needs a way to move between them. Hidden when
              there is only one, which is most of the week. */}
          {view === 'slate' && (slate.slates || []).length > 1 && (
            <div style={{ display: 'flex', gap: 6, marginBottom: 12,
              overflowX: 'auto', paddingBottom: 2 }}>
              {slate.slates.map(d => {
                const on = d.slate_id === slate.slate_id;
                return (
                  <button key={d.slate_id} onClick={() => setSlateId(d.slate_id)}
                    style={{
                      flexShrink: 0, padding: '7px 12px', borderRadius: 9,
                      cursor: 'pointer', fontFamily: 'DM Sans', fontSize: 11,
                      fontWeight: 700, whiteSpace: 'nowrap',
                      border: `1px solid ${on ? 'rgba(232,184,32,0.35)' : T.border}`,
                      background: on ? 'rgba(232,184,32,0.12)' : 'transparent',
                      color: on ? T.gold400 : d.has_open ? T.textSec : T.textMute,
                    }}>
                    {d.name}
                    {!d.has_open && (
                      <span style={{ marginLeft: 5, fontSize: 9, fontWeight: 600,
                        color: T.textMute }}>done</span>
                    )}
                  </button>
                );
              })}
            </div>
          )}

          {view === 'slate' && fixtures.map(f => (
            <SlateFixture
              key={f.fixture_id}
              fx={f}
              anonId={anonId}
              displayName={name}
              onSaved={applyPick}
              onNeedName={() => setAskName(true)}
            />
          ))}

          {view === 'table' && <SlateTable rows={board} />}
        </>
      )}

      {askName && (
        <NamePrompt
          initial={name || ''}
          subtitle="This is the name that shows on the predictions table."
          onSave={async (v) => {
            setName(v);
            try { window.setSessionName && await window.setSessionName(v); } catch (e) {}
            setAskName(false);
          }}
          onSkip={() => setAskName(false)}
        />
      )}
    </Content>
  );
}


// ── One fixture ───────────────────────────────────────────────────────────────
function SlateFixture({ fx, anonId, displayName, onSaved, onNeedName }) {
  const T = window.T;
  const mine = fx.mine;
  const done = fx.home_score !== null && fx.home_score !== undefined;

  const [home, setHome] = React.useState(mine ? mine.home_goals : 1);
  const [away, setAway] = React.useState(mine ? mine.away_goals : 1);
  const [saving, setSaving] = React.useState(false);
  const [msg, setMsg] = React.useState(null);
  const [dirty, setDirty] = React.useState(false);

  const save = async () => {
    setSaving(true); setMsg(null);
    try {
      const res = await window.saveSlatePrediction({
        fixtureId: fx.fixture_id, homeGoals: home, awayGoals: away,
        anonId, displayName,
      });
      if (!res?.ok) {
        // A refused save arrives in the payload, not as a throw. Kicking off
        // mid-session is the common one and reads as information, not an error.
        setMsg(res?.reason === 'kicked off'
          ? 'Too late — this one has kicked off.'
          : `Couldn't save (${res?.reason || 'unknown'}).`);
        setSaving(false);
        return;
      }
      onSaved(fx.fixture_id, home, away);
      setDirty(false);
      setSaving(false);
      window.trackEvent?.('slate_prediction_saved', {
        fixture_id: fx.fixture_id, competition: fx.competition || null,
      });
      // Worth asking only once there is something to be notified about.
      if (!displayName && onNeedName) onNeedName();
    } catch (e) {
      setMsg(e.message || 'Something went wrong.');
      setSaving(false);
    }
  };

  const bump = (setter, value, delta) => {
    setter(Math.min(20, Math.max(0, value + delta)));
    setDirty(true);
    setMsg(null);
  };

  const pts = mine?.points_earned;
  const hit = done && mine && pts > 0;

  return (
    <div style={{
      background: hit ? 'rgba(45,140,78,0.10)' : T.card,
      border: `1px solid ${hit ? 'rgba(45,140,78,0.40)' : T.border}`,
      borderRadius: 14, padding: '12px 14px', marginBottom: 10,
    }}>
      {/* meta line */}
      <div style={{ display: 'flex', justifyContent: 'space-between',
        alignItems: 'center', marginBottom: 10 }}>
        <span style={{ fontFamily: 'DM Sans', fontSize: 9.5, fontWeight: 700,
          letterSpacing: '0.12em', color: T.textMute }}>
          {fx.competition}
        </span>
        <span style={{ fontFamily: 'DM Sans', fontSize: 10, color: T.textMute }}>
          {predictKickoffLabel(fx.kickoff, fx.open, done)}
        </span>
      </div>

      {/* clubs */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <window.MatchdayKitSwatch club={fx.home} size={30} />
        <div style={{ flex: 1, minWidth: 0, textAlign: 'center',
          fontFamily: 'Bakbak One,sans-serif', lineHeight: 1.25,
          fontSize: predictNameSize(fx), color: T.text, letterSpacing: '0.01em' }}>
          {fx.home?.name}
          <span style={{ color: T.textMute, padding: '0 5px',
            fontSize: predictNameSize(fx) * 0.72 }}>v</span>
          {fx.away?.name}
        </div>
        <window.MatchdayKitSwatch club={fx.away} size={30} />
      </div>

      {/* ── Result in ── */}
      {done && (
        <div style={{ marginTop: 10, display: 'flex', alignItems: 'center',
          justifyContent: 'center', gap: 18 }}>
          {mine && (
            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 9, color: T.textMute, fontFamily: 'DM Sans',
                letterSpacing: '0.08em' }}>YOU SAID</div>
              <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 20,
                color: T.textSec }}>{mine.home_goals}–{mine.away_goals}</div>
            </div>
          )}
          <div style={{ textAlign: 'center' }}>
            <div style={{ fontSize: 9, color: T.gold400, fontFamily: 'DM Sans',
              fontWeight: 700, letterSpacing: '0.08em' }}>FINAL</div>
            <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 24,
              color: T.text }}>{fx.home_score}–{fx.away_score}</div>
          </div>
          {mine && pts !== null && pts !== undefined && (
            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 9, color: T.textMute, fontFamily: 'DM Sans',
                letterSpacing: '0.08em' }}>POINTS</div>
              <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 20,
                color: pts > 0 ? T.green : T.textMute }}>{pts}</div>
            </div>
          )}
        </div>
      )}

      {/* ── Locked, waiting ── */}
      {!done && !fx.open && (
        <div style={{ marginTop: 10, textAlign: 'center' }}>
          {mine ? (
            <>
              <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 24,
                color: T.text }}>{mine.home_goals}–{mine.away_goals}</div>
              <div style={{ fontSize: 10, color: T.textMute, fontFamily: 'DM Sans',
                marginTop: 2 }}>Locked in</div>
            </>
          ) : (
            <div style={{ fontSize: 10.5, color: T.textMute, fontFamily: 'DM Sans' }}>
              Kicked off without a call from you
            </div>
          )}
        </div>
      )}

      {/* ── Open ── */}
      {!done && fx.open && (
        <>
          <div style={{ marginTop: 10, display: 'flex', alignItems: 'center',
            justifyContent: 'center', gap: 10 }}>
            <PredictStepper value={home} onBump={d => bump(setHome, home, d)}
              label={fx.home?.name} disabled={saving} />
            <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 18,
              color: T.textMute }}>–</div>
            <PredictStepper value={away} onBump={d => bump(setAway, away, d)}
              label={fx.away?.name} disabled={saving} />
          </div>

          {(dirty || !mine) && (
            <button onClick={save} disabled={saving} style={{
              width: '100%', marginTop: 10, padding: '9px 0', borderRadius: 10,
              border: 'none', cursor: saving ? 'default' : 'pointer',
              background: saving ? 'rgba(245,200,66,0.35)' : T.gold400,
              color: '#10192F', fontFamily: 'DM Sans', fontWeight: 800, fontSize: 12,
            }}>
              {saving ? 'Saving…' : mine ? 'Update' : 'Lock it in'}
            </button>
          )}

          {!dirty && mine && (
            <div style={{ marginTop: 8, textAlign: 'center', fontSize: 10,
              fontFamily: 'DM Sans', color: T.green, fontWeight: 700 }}>
              Called {mine.home_goals}–{mine.away_goals} · change it any time
              before kick-off
            </div>
          )}

          {msg && (
            <div style={{ marginTop: 8, textAlign: 'center', fontSize: 10.5,
              fontFamily: 'DM Sans', color: T.red }}>{msg}</div>
          )}
        </>
      )}
    </div>
  );
}


function PredictStepper({ value, onBump, label, disabled }) {
  const T = window.T;
  const btn = {
    width: 26, height: 26, borderRadius: 7, border: `1px solid ${T.navy600}`,
    background: 'rgba(30,46,82,0.5)', color: disabled ? T.textSec : T.text,
    fontSize: 15, lineHeight: 1, cursor: disabled ? 'default' : 'pointer',
  };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
      <button style={btn} onClick={() => onBump(-1)} disabled={disabled}
        aria-label={`${label} fewer`}>−</button>
      <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 26,
        color: T.text, minWidth: 22, textAlign: 'center' }}>{value}</div>
      <button style={btn} onClick={() => onBump(1)} disabled={disabled}
        aria-label={`${label} more`}>+</button>
    </div>
  );
}


function SlateTable({ rows }) {
  const T = window.T;

  if (rows === null) {
    return (
      <div style={{ padding: '30px 0', textAlign: 'center', fontFamily: 'DM Sans',
        fontSize: 12, color: T.textSec }}>Loading the table…</div>
    );
  }
  if (!rows.length) {
    return (
      <div style={{ background: T.card, borderRadius: 14, padding: 18,
        border: `1px dashed ${T.border}`, textAlign: 'center',
        fontFamily: 'DM Sans', fontSize: 12, color: T.textSec, lineHeight: 1.6 }}>
        Nothing on the table yet — it fills in once the first slate is settled.
      </div>
    );
  }

  return (
    <div style={{ background: T.card, borderRadius: 14, overflow: 'hidden' }}>
      {rows.map((r, i) => (
        <div key={`${r.display_name}-${i}`} style={{
          display: 'flex', alignItems: 'center', gap: 12,
          padding: '11px 14px',
          borderBottom: i === rows.length - 1 ? 'none' : `1px solid ${T.border}`,
          background: i < 3 ? 'rgba(232,184,32,0.05)' : 'transparent',
        }}>
          <div style={{ width: 20, fontFamily: 'Bakbak One,sans-serif',
            fontSize: 13, color: i < 3 ? T.gold400 : T.textMute }}>{i + 1}</div>
          <div style={{ flex: 1, minWidth: 0, fontFamily: 'DM Sans', fontSize: 12.5,
            fontWeight: 600, color: T.text, overflow: 'hidden',
            textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {r.display_name}
          </div>
          <div style={{ fontFamily: 'DM Sans', fontSize: 10, color: T.textMute }}>
            {r.exact_hits} exact
          </div>
          <div style={{ fontFamily: 'Bakbak One,sans-serif', fontSize: 15,
            color: T.gold400, minWidth: 28, textAlign: 'right' }}>
            {r.total_points}
          </div>
        </div>
      ))}
    </div>
  );
}


// Both names share one size, picked from their combined length, so the line
// fits between the two kits and neither club looks like an afterthought.
function predictNameSize(fx) {
  const total = (fx.home?.name || '').length + (fx.away?.name || '').length;
  return total <= 16 ? 15 : total <= 22 ? 13.5 : total <= 30 ? 12 : 10.5;
}

function predictKickoffLabel(iso, open, done) {
  if (!iso) return '';
  const d = new Date(iso);
  if (done) {
    return d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric' });
  }
  if (!open) return 'Locked';
  const hrs = (d.getTime() - Date.now()) / 36e5;
  if (hrs < 1)  return 'Locks in minutes';
  if (hrs < 24) return `Locks in ${Math.round(hrs)}h`;
  return d.toLocaleDateString(undefined, {
    weekday: 'short', hour: 'numeric', minute: '2-digit',
  });
}

window.PredictScreen = PredictScreen;
