const {
  useEffect: useEffectP,
  useMemo: useMemoP,
  useRef: useRefP,
  useState: useStateP,
} = React;

const PREDICTOR_PAYOUTS = {
  correctResult: 100,
  exactScore: 500,
};

function getPredictorMatchdays() {
  return window.WC_DATA?.PREDICTOR_MATCHDAYS || [];
}

function getPredictorFixtures() {
  return getPredictorMatchdays().flatMap(matchday => (
    matchday.fixtures.map(fixture => ({ ...fixture, matchdayId: matchday.id }))
  ));
}

function getPredictorFixture(fixtureId) {
  return getPredictorFixtures().find(fixture => fixture.id === fixtureId) || null;
}

function getPredictorMatchday(matchdayId) {
  return getPredictorMatchdays().find(matchday => matchday.id === matchdayId) || null;
}

function getMatchdayFirstKickoff(matchday) {
  const times = (matchday?.fixtures || []).map(fixture => Date.parse(fixture.kickoff)).filter(Number.isFinite);
  return times.length ? Math.min(...times) : Infinity;
}

function clampPredictorScore(value) {
  if (value === '' || value === null || value === undefined) return null;
  const next = Number.parseInt(value, 10);
  if (!Number.isFinite(next)) return null;
  return Math.max(0, Math.min(99, next));
}

function isValidPredictorScore(value) {
  return Number.isInteger(value) && value >= 0 && value <= 99;
}

function isCompletePrediction(prediction) {
  return isValidPredictorScore(prediction?.homeScore) && isValidPredictorScore(prediction?.awayScore);
}

function isCompleteResult(result) {
  return isValidPredictorScore(result?.homeScore) && isValidPredictorScore(result?.awayScore);
}

function createDefaultPredictorState() {
  return {
    predictions: {},
    submittedMatchdays: {},
    results: {},
    settled: {},
  };
}

function normalizePredictorState(raw) {
  const next = createDefaultPredictorState();

  if (raw?.predictions && typeof raw.predictions === 'object') {
    Object.entries(raw.predictions).forEach(([fixtureId, prediction]) => {
      next.predictions[fixtureId] = {
        homeScore: clampPredictorScore(prediction?.homeScore),
        awayScore: clampPredictorScore(prediction?.awayScore),
        double: Boolean(prediction?.double),
        updatedAt: Number.isFinite(prediction?.updatedAt) ? prediction.updatedAt : 0,
      };
    });
  }

  if (raw?.submittedMatchdays && typeof raw.submittedMatchdays === 'object') {
    Object.entries(raw.submittedMatchdays).forEach(([matchdayId, submission]) => {
      const submittedAt = Number.isFinite(submission?.submittedAt) ? submission.submittedAt : Date.now();
      next.submittedMatchdays[matchdayId] = {
        submittedAt,
        fixtureIds: Array.isArray(submission?.fixtureIds) ? submission.fixtureIds : [],
      };
    });
  }

  if (raw?.results && typeof raw.results === 'object') {
    Object.entries(raw.results).forEach(([fixtureId, result]) => {
      const normalized = {
        homeScore: clampPredictorScore(result?.homeScore),
        awayScore: clampPredictorScore(result?.awayScore),
        postedAt: Number.isFinite(result?.postedAt) ? result.postedAt : 0,
        source: result?.source || 'local',
      };
      if (isCompleteResult(normalized)) next.results[fixtureId] = normalized;
    });
  }

  if (raw?.settled && typeof raw.settled === 'object') {
    Object.entries(raw.settled).forEach(([fixtureId, settlement]) => {
      const gold = Number.isFinite(settlement?.gold) ? settlement.gold : 0;
      next.settled[fixtureId] = {
        gold,
        baseGold: Number.isFinite(settlement?.baseGold) ? settlement.baseGold : gold,
        exact: Boolean(settlement?.exact),
        correctResult: Boolean(settlement?.correctResult),
        doubled: Boolean(settlement?.doubled),
        settledAt: Number.isFinite(settlement?.settledAt) ? settlement.settledAt : 0,
      };
    });
  }

  return next;
}

function mergePredictorProgress(localPredictor, remotePredictor) {
  const local = normalizePredictorState(localPredictor);
  const remote = normalizePredictorState(remotePredictor);
  const merged = createDefaultPredictorState();

  const predictionIds = new Set([...Object.keys(remote.predictions), ...Object.keys(local.predictions)]);
  predictionIds.forEach(fixtureId => {
    const localPrediction = local.predictions[fixtureId];
    const remotePrediction = remote.predictions[fixtureId];
    merged.predictions[fixtureId] = (localPrediction?.updatedAt || 0) > (remotePrediction?.updatedAt || 0)
      ? localPrediction
      : (remotePrediction || localPrediction);
  });

  merged.submittedMatchdays = {
    ...remote.submittedMatchdays,
    ...local.submittedMatchdays,
  };

  Object.entries({ ...remote.results, ...local.results }).forEach(([fixtureId, result]) => {
    const localResult = local.results[fixtureId];
    const remoteResult = remote.results[fixtureId];
    merged.results[fixtureId] = (localResult?.postedAt || 0) > (remoteResult?.postedAt || 0)
      ? localResult
      : (remoteResult || localResult || result);
  });

  merged.settled = {
    ...remote.settled,
    ...local.settled,
  };

  return normalizePredictorState(merged);
}

function getScoreOutcome(homeScore, awayScore) {
  if (homeScore > awayScore) return 'home';
  if (awayScore > homeScore) return 'away';
  return 'draw';
}

function calculatePredictorAward(prediction, result) {
  if (!isCompletePrediction(prediction) || !isCompleteResult(result)) {
    return { gold: 0, baseGold: 0, exact: false, correctResult: false, doubled: Boolean(prediction?.double) };
  }

  const exact = prediction.homeScore === result.homeScore && prediction.awayScore === result.awayScore;
  const correctResult = getScoreOutcome(prediction.homeScore, prediction.awayScore) === getScoreOutcome(result.homeScore, result.awayScore);
  const baseGold = exact ? PREDICTOR_PAYOUTS.exactScore : (correctResult ? PREDICTOR_PAYOUTS.correctResult : 0);
  const doubled = Boolean(prediction.double);

  return {
    gold: baseGold * (doubled ? 2 : 1),
    baseGold,
    exact,
    correctResult,
    doubled,
  };
}

function getPredictorStats(predictorState) {
  const state = normalizePredictorState(predictorState);
  const submittedFixtureIds = new Set();
  Object.values(state.submittedMatchdays || {}).forEach(submission => {
    (submission.fixtureIds || []).forEach(fixtureId => {
      if (isCompletePrediction(state.predictions?.[fixtureId])) submittedFixtureIds.add(fixtureId);
    });
  });

  return Object.values(state.settled).reduce((acc, settlement) => {
    acc.gold += settlement.gold || 0;
    acc.exact += settlement.exact ? 1 : 0;
    acc.correctResults += settlement.correctResult ? 1 : 0;
    acc.settled += 1;
    return acc;
  }, { gold: 0, exact: 0, correctResults: 0, settled: 0, picksMade: submittedFixtureIds.size });
}

function formatPredictorKickoff(iso) {
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) return 'TBD';
  return new Intl.DateTimeFormat(undefined, {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).format(date);
}

function formatPredictorDate(iso) {
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) return 'TBD';
  return new Intl.DateTimeFormat(undefined, {
    month: 'short',
    day: 'numeric',
  }).format(date);
}

function getPredictorCountdown(targetTs, now = Date.now()) {
  const diff = targetTs - now;
  if (!Number.isFinite(diff) || diff <= 0) return 'Locked';
  const totalMinutes = Math.ceil(diff / 60000);
  const days = Math.floor(totalMinutes / 1440);
  const hours = Math.floor((totalMinutes % 1440) / 60);
  const minutes = totalMinutes % 60;
  if (days > 0) return `${days}d ${hours}h`;
  if (hours > 0) return `${hours}h ${minutes}m`;
  return `${minutes}m`;
}

function getPredictorTeam(teamId) {
  return window.WC_DATA?.PREDICTOR_TEAMS?.[teamId] || {
    name: teamId?.toUpperCase?.() || 'TBD',
    short: teamId?.toUpperCase?.() || 'TBD',
    flag: '',
    colors: ['#26364f', '#d7b15e'],
  };
}

function PredictorTeamBlock({ team, side }) {
  const gradient = `linear-gradient(135deg, ${team.colors?.[0] || '#15233d'} 0%, ${team.colors?.[1] || '#d7b15e'} 100%)`;
  return (
    <div className={`predictor-team predictor-team--${side}`}>
      <div className="predictor-team__flag" style={{ '--flag-grad': gradient }}>{team.flag}</div>
      <strong>{team.name}</strong>
      <span>{team.short}</span>
    </div>
  );
}

function PredictorScoreInput({ value, disabled, ariaLabel, onChange }) {
  return (
    <input
      className="predictor-score-input"
      type="number"
      min="0"
      max="99"
      inputMode="numeric"
      value={value ?? ''}
      aria-label={ariaLabel}
      disabled={disabled}
      onChange={event => onChange(clampPredictorScore(event.target.value))}
    />
  );
}

function PredictorFixtureCard({ fixture, predictor, matchdaySubmitted, matchdayLocked, onPredictionChange }) {
  const home = getPredictorTeam(fixture.home);
  const away = getPredictorTeam(fixture.away);
  const prediction = predictor.predictions?.[fixture.id] || {};
  const result = predictor.results?.[fixture.id] || null;
  const settlement = predictor.settled?.[fixture.id] || null;
  const locked = matchdayLocked;
  const hasPrediction = isCompletePrediction(prediction);

  return (
    <div className={`predictor-fixture ${prediction.double ? 'is-double' : ''} ${locked ? 'is-locked' : ''}`}>
      <div className="predictor-fixture__meta">
        <span>{fixture.group}</span>
        <span>Match {fixture.matchNumber}</span>
        <span>{formatPredictorKickoff(fixture.kickoff)}</span>
        <button
          className="predictor-fixture__double"
          data-active={prediction.double}
          disabled={locked}
          title={locked ? 'Gameday locked' : 'Use double score on this match'}
          onClick={() => onPredictionChange(fixture.id, { double: !prediction.double })}
        >
          {prediction.double ? '★' : '☆'}
        </button>
      </div>

      <div className="predictor-fixture__teams">
        <PredictorTeamBlock team={home} side="home" />
        <div className="predictor-fixture__versus">VS</div>
        <PredictorTeamBlock team={away} side="away" />
      </div>

      <div className="predictor-fixture__scores">
        <PredictorScoreInput
          value={prediction.homeScore}
          disabled={locked}
          ariaLabel={`${home.name} predicted score`}
          onChange={homeScore => onPredictionChange(fixture.id, { homeScore })}
        />
        <span>-</span>
        <PredictorScoreInput
          value={prediction.awayScore}
          disabled={locked}
          ariaLabel={`${away.name} predicted score`}
          onChange={awayScore => onPredictionChange(fixture.id, { awayScore })}
        />
      </div>

      <div className="predictor-fixture__status">
        {result ? (
          <span>Result: {result.homeScore}-{result.awayScore}</span>
        ) : locked ? (
          <span>{hasPrediction ? 'Prediction locked' : 'No submitted pick'}</span>
        ) : matchdaySubmitted ? (
          <span>{hasPrediction ? 'Submitted - editable until deadline' : 'Submitted day needs this score'}</span>
        ) : (
          <span>{hasPrediction ? 'Ready' : 'Enter score'}</span>
        )}
        {settlement ? <strong className="coin">+{settlement.gold}</strong> : null}
      </div>

      {prediction.double ? <div className="predictor-fixture__double-pill">Double score active</div> : null}
    </div>
  );
}

function PredictorResultConsole({ matchday, predictor, onSaveResult }) {
  const [drafts, setDrafts] = useStateP({});

  function getDraftValue(fixtureId, side) {
    const draft = drafts[fixtureId];
    if (draft && Object.prototype.hasOwnProperty.call(draft, side)) return draft[side];
    const result = predictor.results?.[fixtureId];
    return result ? result[side] : null;
  }

  function updateDraft(fixtureId, patch) {
    setDrafts(prev => ({
      ...prev,
      [fixtureId]: {
        ...(prev[fixtureId] || {}),
        ...patch,
      },
    }));
  }

  function saveFixture(fixture) {
    const homeScore = clampPredictorScore(getDraftValue(fixture.id, 'homeScore'));
    const awayScore = clampPredictorScore(getDraftValue(fixture.id, 'awayScore'));
    if (!isValidPredictorScore(homeScore) || !isValidPredictorScore(awayScore)) return;
    onSaveResult(fixture.id, { homeScore, awayScore });
  }

  return (
    <div className="predictor-console">
      <div className="predictor-console__head">
        <div>
          <strong>Result Console</strong>
          <span>Post final scores to settle submitted predictions and award gold.</span>
        </div>
      </div>
      <div className="predictor-console__rows">
        {matchday.fixtures.map(fixture => {
          const home = getPredictorTeam(fixture.home);
          const away = getPredictorTeam(fixture.away);
          const homeScore = getDraftValue(fixture.id, 'homeScore');
          const awayScore = getDraftValue(fixture.id, 'awayScore');
          const canSave = isValidPredictorScore(clampPredictorScore(homeScore)) && isValidPredictorScore(clampPredictorScore(awayScore));
          return (
            <div className="predictor-console__row" key={fixture.id}>
              <div className="predictor-console__match">
                <strong>{home.short} vs {away.short}</strong>
                <span>{fixture.group}</span>
              </div>
              <div className="predictor-console__score">
                <PredictorScoreInput
                  value={homeScore}
                  ariaLabel={`${home.name} final score`}
                  onChange={value => updateDraft(fixture.id, { homeScore: value })}
                />
                <span>-</span>
                <PredictorScoreInput
                  value={awayScore}
                  ariaLabel={`${away.name} final score`}
                  onChange={value => updateDraft(fixture.id, { awayScore: value })}
                />
              </div>
              <button className="btn btn-ghost btn-sm" disabled={!canSave} onClick={() => saveFixture(fixture)}>
                Save Result
              </button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function PredictorLeaderboard({ leaderboard, stats }) {
  const entries = [...(leaderboard || [])].sort((a, b) => (b.score - a.score) || ((a.createdAt || 0) - (b.createdAt || 0))).slice(0, 5);
  const displayEntries = entries.length
    ? entries
    : [
        { name: 'Awaiting first result', score: 0, isPlaceholder: true },
        { name: 'Submit your picks', score: 0, isPlaceholder: true },
        { name: 'Exact scores climb fast', score: 0, isPlaceholder: true },
        { name: 'Double pick matters', score: 0, isPlaceholder: true },
        { name: 'Leaderboard opens soon', score: 0, isPlaceholder: true },
      ];

  return (
    <div className="predictor-side-card predictor-leaderboard">
      <div className="predictor-side-card__title">Top Predictors</div>
      <p>Gold from settled World Cup score predictions.</p>
      <div className="predictor-leaderboard__list" data-empty={!entries.length}>
        {displayEntries.map((entry, index) => (
          <div className={`predictor-leaderboard__row ${entry.isPlaceholder ? 'is-placeholder' : ''}`} key={`${entry.userId || entry.name}-${index}`}>
            <span>{index + 1}</span>
            <strong>{entry.name}</strong>
            <b className="coin">{(entry.score || 0).toLocaleString()}</b>
          </div>
        ))}
      </div>
      <div className="predictor-leaderboard__mine">
        Your predictor gold: <strong className="coin">{stats.gold.toLocaleString()}</strong>
      </div>
    </div>
  );
}

function PredictorGame({
  predictor,
  leaderboard,
  signedIn,
  firebaseEnabled,
  isOwner,
  onPredictionChange,
  onSubmitMatchday,
  onSaveResult,
}) {
  const matchdays = getPredictorMatchdays();
  const [activeId, setActiveId] = useStateP(matchdays[0]?.id || null);
  const [managerOpen, setManagerOpen] = useStateP(false);
  const [now, setNow] = useStateP(Date.now());
  const tabsRef = useRefP(null);
  const stats = useMemoP(() => getPredictorStats(predictor), [predictor]);
  const activeMatchday = getPredictorMatchday(activeId) || matchdays[0];

  useEffectP(() => {
    const timer = setInterval(() => setNow(Date.now()), 30000);
    return () => clearInterval(timer);
  }, []);

  useEffectP(() => {
    if (!activeId && matchdays[0]) setActiveId(matchdays[0].id);
  }, [activeId, matchdays]);

  if (!activeMatchday) {
    return (
      <div className="game-stage">
        <div className="display" style={{ fontSize: 28 }}>Predictor fixtures unavailable</div>
      </div>
    );
  }

  const firstKickoff = getMatchdayFirstKickoff(activeMatchday);
  const activeIndex = Math.max(0, matchdays.findIndex(matchday => matchday.id === activeMatchday.id));
  const submitted = Boolean(predictor.submittedMatchdays?.[activeMatchday.id]);
  const locked = now >= firstKickoff;
  const completed = activeMatchday.fixtures.filter(fixture => isCompletePrediction(predictor.predictions?.[fixture.id])).length;
  const doubleUsed = activeMatchday.fixtures.some(fixture => predictor.predictions?.[fixture.id]?.double);
  const canSubmit = !locked && completed === activeMatchday.fixtures.length && doubleUsed;
  const nextLockLabel = locked ? 'Locked' : getPredictorCountdown(firstKickoff, now);
  const submitNotice = locked
    ? (submitted ? 'Gameday submitted. Your picks are now locked.' : 'This gameday is closed. Predictions can no longer be submitted.')
    : completed < activeMatchday.fixtures.length
      ? `Submission not complete: enter scores for all ${activeMatchday.fixtures.length} matches.`
      : !doubleUsed
        ? 'Submission not complete: activate your 1 Double Score pick.'
        : submitted
          ? 'Gameday submitted. You can still edit and resubmit until the deadline.'
          : 'Ready to submit. Lock these picks before kick-off.';
  const submitNoticeState = (locked && submitted) || canSubmit ? 'ready' : 'warning';

  function selectMatchday(index) {
    const nextMatchday = matchdays[index];
    if (!nextMatchday) return;
    setActiveId(nextMatchday.id);
    window.setTimeout(() => {
      const button = tabsRef.current?.querySelector(`[data-matchday-id="${nextMatchday.id}"]`);
      button?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
    }, 0);
  }

  return (
    <div className="predictor-page fade-in">
      <section className="predictor-hero">
        <div className="predictor-hero__copy">
          <div className="predictor-hero__kicker">Predict every score line.</div>
          <h1><span>Gameday</span> Predictor.</h1>
        </div>
        <div className="predictor-rules">
          <div>
            <strong>Correct Result</strong>
            <b>+100 Gold</b>
            <small>Get the winning team, or draw, right.</small>
          </div>
          <div>
            <strong>Exact Score</strong>
            <b>+500 Gold</b>
            <small>Predict the exact scoreline.</small>
          </div>
          <div>
            <strong>Double Score Pick</strong>
            <b>1 per gameday</b>
            <small>Double your payout on one match.</small>
          </div>
        </div>
      </section>

      <div className="predictor-matchday-bar">
        <div>
          <div className="display predictor-matchday-bar__title">Group Stage Gamedays</div>
          <span>{activeMatchday.label} • {activeMatchday.dateLabel} • closes in {nextLockLabel}</span>
        </div>
        <div className="predictor-tabs-shell">
          <button
            className="predictor-tabs-arrow"
            disabled={activeIndex <= 0}
            onClick={() => selectMatchday(activeIndex - 1)}
            aria-label="Previous gameday"
          >
            ‹
          </button>
          <div className="predictor-tabs" ref={tabsRef}>
            {matchdays.map((matchday, index) => {
              const tabSubmitted = Boolean(predictor.submittedMatchdays?.[matchday.id]);
              const tabCompleted = matchday.fixtures.every(fixture => isCompletePrediction(predictor.predictions?.[fixture.id]));
              return (
                <button
                  key={matchday.id}
                  data-matchday-id={matchday.id}
                  data-active={matchday.id === activeMatchday.id}
                  data-submitted={tabSubmitted}
                  data-complete={tabCompleted}
                  onClick={() => selectMatchday(index)}
                  title={`${matchday.label} - ${matchday.dateLabel}${tabSubmitted ? ' - submitted' : ''}`}
                >
                  {index + 1}
                </button>
              );
            })}
          </div>
          <button
            className="predictor-tabs-arrow"
            disabled={activeIndex >= matchdays.length - 1}
            onClick={() => selectMatchday(activeIndex + 1)}
            aria-label="Next gameday"
          >
            ›
          </button>
        </div>
      </div>

      <div className="predictor-fixtures">
        {activeMatchday.fixtures.map(fixture => (
          <PredictorFixtureCard
            key={fixture.id}
            fixture={fixture}
            predictor={predictor}
            matchdaySubmitted={submitted}
            matchdayLocked={locked}
            onPredictionChange={onPredictionChange}
          />
        ))}
      </div>

      <div className="predictor-bottom-grid">
        <div className="predictor-side-card predictor-progress-card">
          <div className="predictor-record-card__header">
            <div>
              <div className="predictor-side-card__title">My Predictor Record</div>
              <p>Complete record across submitted gameday picks.</p>
            </div>
            <div className="predictor-record-card__badge">
              <span>Accuracy</span>
              <strong>{stats.picksMade ? Math.round((stats.correctResults / stats.picksMade) * 100) : 0}%</strong>
            </div>
          </div>
          <div className="predictor-record-card__stats">
            <div className="predictor-record-stat predictor-record-stat--primary">
              <span>Correct picks</span>
              <strong>{stats.correctResults}<small>/{stats.picksMade}</small></strong>
            </div>
            <div className="predictor-record-stat">
              <span>Exact scores</span>
              <strong>{stats.exact}</strong>
            </div>
            <div className="predictor-record-stat">
              <span>Settled picks</span>
              <strong>{stats.settled}</strong>
            </div>
          </div>
        </div>

        <div className="predictor-side-card predictor-submit-card">
          <div className="predictor-side-card__title">Submit Your Predictions</div>
          <p>
            Submit the whole gameday group before the first kick-off.
            {locked
              ? (submitted ? ' Submitted picks are now locked.' : ' This gameday is closed.')
              : ' You can edit and resubmit until the deadline.'}
          </p>
          <button className="btn btn-gold btn-lg" disabled={!canSubmit} onClick={() => onSubmitMatchday(activeMatchday.id)}>
            {locked ? 'Gameday Closed' : submitted ? 'Resubmit Predictions' : 'Submit Predictions'}
          </button>
          <div className="predictor-submit-card__notice" data-state={submitNoticeState}>{submitNotice}</div>
          <div className="predictor-submit-card__timer">Submissions close {formatPredictorDate(activeMatchday.fixtures[0].kickoff)} at {formatPredictorKickoff(activeMatchday.fixtures[0].kickoff)}</div>
        </div>

        <PredictorLeaderboard leaderboard={leaderboard} stats={stats} />
      </div>

      {signedIn && isOwner ? (
        <>
          <div className="predictor-manager">
            <button className="predictor-manager__toggle" onClick={() => setManagerOpen(open => !open)}>
              {managerOpen ? 'Hide Result Console' : 'Open Result Console'}
            </button>
            <span>
              {firebaseEnabled
                ? 'Owner mode: result saves are published for everyone.'
                : 'Owner mode is local only because Firebase is off.'}
            </span>
          </div>

          {managerOpen ? (
            <PredictorResultConsole
              matchday={activeMatchday}
              predictor={predictor}
              onSaveResult={onSaveResult}
            />
          ) : null}
        </>
      ) : null}
    </div>
  );
}

window.WC_PREDICTOR = {
  PREDICTOR_PAYOUTS,
  calculateAward: calculatePredictorAward,
  createDefaultState: createDefaultPredictorState,
  getFixture: getPredictorFixture,
  getFixtures: getPredictorFixtures,
  getMatchday: getPredictorMatchday,
  getMatchdayFirstKickoff,
  getStats: getPredictorStats,
  isCompletePrediction,
  isCompleteResult,
  mergeProgress: mergePredictorProgress,
  normalizeState: normalizePredictorState,
};

window.PredictorGame = PredictorGame;
