import {
  mdiChartBoxOutline,
  mdiFlash,
  mdiGamepadSquareOutline,
  mdiHeart,
  mdiHeartOutline,
  mdiHistory,
  mdiRefresh,
  mdiShieldCheckOutline,
  mdiTimerOutline,
} from '@mdi/js';
import Head from 'next/head';
import Link from 'next/link';
import React, { ReactElement } from 'react';
import axios from 'axios';
import BaseButton from '../../components/BaseButton';
import BaseIcon from '../../components/BaseIcon';
import CardBox from '../../components/CardBox';
import GuessCard from '../../components/OsuHigherLower/GuessCard';
import {
  GameplayLeaderboardEntry,
  GameplayRound,
  GameplaySession,
  HistorySession,
  ModeSelection,
  TimerProfile,
} from '../../components/OsuHigherLower/types';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import LayoutAuthenticated from '../../layouts/Authenticated';

const ADVANCE_DELAY_MS = 1050;

const modeOptions: Array<{
  value: ModeSelection;
  label: string;
  helper: string;
}> = [
  {
    value: 'mixed',
    label: 'Mixed',
    helper: 'Server shuffles beatmap, mapper, and artist rounds. The label stays hidden until reveal.',
  },
  {
    value: 'beatmap_only',
    label: 'Beatmap',
    helper: 'Classic higher-lower using ranked or loved osu!standard beatmap playcount.',
  },
  {
    value: 'mapper_only',
    label: 'Mapper',
    helper: 'Compare total plays accumulated across each mapper’s ranked and loved maps.',
  },
  {
    value: 'artist_only',
    label: 'Artist',
    helper: 'Compare total plays accumulated across each artist’s ranked and loved maps.',
  },
];

const timerOptions: Array<{
  value: TimerProfile;
  label: string;
  helper: string;
  icon: string;
}> = [
  {
    value: 'normal',
    label: 'Normal · 8s',
    helper: 'A quick pace that still gives you time to read each card carefully.',
    icon: mdiTimerOutline,
  },
  {
    value: 'insane',
    label: 'Insane · 5s',
    helper: 'Very fast rounds with no breathing room between reveals.',
    icon: mdiFlash,
  },
];

const formatModeSelection = (modeSelection: GameplaySession['modeSelection']) => {
  if (modeSelection === 'beatmap_only') {
    return 'Beatmap';
  }

  if (modeSelection === 'mapper_only') {
    return 'Mapper';
  }

  if (modeSelection === 'artist_only') {
    return 'Artist';
  }

  return 'Mixed';
};

const formatSessionDate = (date?: string | null) => {
  if (!date) {
    return 'Still running';
  }

  return new Intl.DateTimeFormat(undefined, {
    month: 'short',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
  }).format(new Date(date));
};

const getResultMessage = (round?: GameplayRound | null) => {
  if (!round?.reveal) {
    return 'Mode hidden until you answer.';
  }

  if (round.reveal.loseReason === 'timeout') {
    return `${round.reveal.modeLabel} revealed — time ran out.`;
  }

  return round.reveal.isCorrect
    ? `${round.reveal.modeLabel} revealed — correct guess.`
    : `${round.reveal.modeLabel} revealed — wrong guess.`;
};

export default function PlayPage() {
  const [modeSelection, setModeSelection] = React.useState<ModeSelection>('mixed');
  const [timerProfile, setTimerProfile] = React.useState<TimerProfile>('normal');
  const [session, setSession] = React.useState<GameplaySession | null>(null);
  const [currentRound, setCurrentRound] = React.useState<GameplayRound | null>(null);
  const [revealedRound, setRevealedRound] = React.useState<GameplayRound | null>(null);
  const [history, setHistory] = React.useState<HistorySession[]>([]);
  const [leaderboard, setLeaderboard] = React.useState<GameplayLeaderboardEntry[]>([]);
  const [isLoadingState, setIsLoadingState] = React.useState(true);
  const [isStarting, setIsStarting] = React.useState(false);
  const [isSubmittingChoice, setIsSubmittingChoice] = React.useState(false);
  const [errorMessage, setErrorMessage] = React.useState('');
  const [remainingMs, setRemainingMs] = React.useState(0);
  const advanceTimerRef = React.useRef<number | null>(null);
  const timeoutTriggeredRef = React.useRef(false);

  const loadHistory = React.useCallback(async () => {
    const { data } = await axios.get('/gameplay/sessions');
    setHistory(Array.isArray(data?.sessions) ? data.sessions : []);
    setLeaderboard(Array.isArray(data?.leaderboard) ? data.leaderboard : []);
  }, []);

  const queueNextRound = React.useCallback(
    (nextRound: GameplayRound | null, nextSession: GameplaySession) => {
      if (advanceTimerRef.current) {
        window.clearTimeout(advanceTimerRef.current);
      }

      if (!nextRound) {
        void loadHistory();
        return;
      }

      advanceTimerRef.current = window.setTimeout(() => {
        setSession(nextSession);
        setRevealedRound(null);
        setCurrentRound(nextRound);
        setRemainingMs(nextRound.timeLimitMs);
        timeoutTriggeredRef.current = false;
      }, ADVANCE_DELAY_MS);
    },
    [loadHistory],
  );

  const applyResolution = React.useCallback(
    (payload: {
      session: GameplaySession;
      result: GameplayRound;
      nextRound: GameplayRound | null;
    }) => {
      setSession(payload.session);
      setCurrentRound(null);
      setRevealedRound(payload.result);
      queueNextRound(payload.nextRound, payload.session);
    },
    [queueNextRound],
  );

  const loadActiveSession = React.useCallback(async () => {
    const { data } = await axios.get('/gameplay/active');

    if (!data?.activeSession) {
      setSession(null);
      setCurrentRound(null);
      setRevealedRound(null);
      return;
    }

    if (data.currentRound) {
      setSession(data.session);
      setCurrentRound(data.currentRound);
      setRevealedRound(null);
      setRemainingMs(data.currentRound.timeLimitMs);
      timeoutTriggeredRef.current = false;
      return;
    }

    if (data.result) {
      applyResolution({
        session: data.session,
        result: data.result,
        nextRound: data.nextRound ?? null,
      });
    }
  }, [applyResolution]);

  React.useEffect(() => {
    let isMounted = true;

    const loadPage = async () => {
      try {
        setIsLoadingState(true);
        setErrorMessage('');
        await Promise.all([loadHistory(), loadActiveSession()]);
      } catch (error: any) {
        if (!isMounted) {
          return;
        }

        setErrorMessage(
          error?.response?.data || 'Unable to load the game state right now.',
        );
      } finally {
        if (isMounted) {
          setIsLoadingState(false);
        }
      }
    };

    void loadPage();

    return () => {
      isMounted = false;
      if (advanceTimerRef.current) {
        window.clearTimeout(advanceTimerRef.current);
      }
    };
  }, [loadActiveSession, loadHistory]);

  const submitChoice = React.useCallback(
    async (choice: 'a' | 'b' | 'none') => {
      if (!session || !currentRound || isSubmittingChoice) {
        return;
      }

      try {
        setIsSubmittingChoice(true);
        setErrorMessage('');
        timeoutTriggeredRef.current = choice === 'none';

        const { data } = await axios.post(
          `/gameplay/sessions/${session.id}/rounds/${currentRound.id}/answer`,
          { choice },
        );

        applyResolution({
          session: data.session,
          result: data.result,
          nextRound: data.nextRound ?? null,
        });
      } catch (error: any) {
        setErrorMessage(
          error?.response?.data || 'Could not submit your guess. Please try again.',
        );
        await loadActiveSession();
      } finally {
        setIsSubmittingChoice(false);
      }
    },
    [applyResolution, currentRound, isSubmittingChoice, loadActiveSession, session],
  );

  React.useEffect(() => {
    if (!currentRound) {
      return undefined;
    }

    const expiresAt = new Date(currentRound.expiresAt).getTime();

    const tick = () => {
      const nextRemainingMs = Math.max(expiresAt - Date.now(), 0);
      setRemainingMs(nextRemainingMs);

      if (nextRemainingMs === 0 && !timeoutTriggeredRef.current) {
        timeoutTriggeredRef.current = true;
        void submitChoice('none');
      }
    };

    tick();
    const timerId = window.setInterval(tick, 100);

    return () => window.clearInterval(timerId);
  }, [currentRound, submitChoice]);

  const handleStartSession = async () => {
    try {
      setIsStarting(true);
      setErrorMessage('');
      setRevealedRound(null);
      setCurrentRound(null);

      const { data } = await axios.post('/gameplay/sessions/start', {
        modeSelection,
        timerProfile,
      });

      setSession(data.session);
      setCurrentRound(data.currentRound);
      setRemainingMs(data.currentRound?.timeLimitMs || 0);
      timeoutTriggeredRef.current = false;
      await loadHistory();
    } catch (error: any) {
      setErrorMessage(
        error?.response?.data || 'Could not start a new session right now.',
      );
    } finally {
      setIsStarting(false);
    }
  };

  const activeRound = currentRound || revealedRound;
  const timerProgress = React.useMemo(() => {
    if (!currentRound) {
      return 0;
    }

    return Math.max(
      0,
      Math.min((remainingMs / currentRound.timeLimitMs) * 100, 100),
    );
  }, [currentRound, remainingMs]);

  const lives = session?.startingLives ?? 2;
  const lobbyVisible = !session || (!currentRound && !revealedRound);
  const gameOver = session?.status === 'finished' && Boolean(revealedRound);

  return (
    <>
      <Head>
        <title>{getPageTitle('Play')}</title>
      </Head>

      <SectionMain>
        <SectionTitleLineWithButton
          icon={mdiGamepadSquareOutline}
          main
          title="Play"
        >
          <div className="flex items-center gap-3">
            <BaseButton
              color="whiteDark"
              href="/play/history"
              icon={mdiHistory}
              label="Run history"
            />
          </div>
        </SectionTitleLineWithButton>

        {errorMessage ? (
          <div className="mb-6 rounded-2xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-100">
            {errorMessage}
          </div>
        ) : null}

        {isLoadingState ? (
          <CardBox className="border border-white/10 bg-[#10141d] text-white">
            <div className="space-y-2 py-10 text-center">
              <p className="text-sm uppercase tracking-[0.22em] text-white/45">
                Loading arena
              </p>
              <p className="text-lg text-white/80">
                Restoring your current run and recent leaderboard.
              </p>
            </div>
          </CardBox>
        ) : null}

        {!isLoadingState && session ? (
          <div className="mb-6 grid gap-4 lg:grid-cols-[1.6fr_1fr]">
            <CardBox className="border border-white/10 bg-[#10141d] text-white">
              <div className="flex flex-wrap items-center gap-3">
                <div className="rounded-full border border-white/10 bg-white/5 px-3 py-2 text-xs font-medium uppercase tracking-[0.2em] text-white/60">
                  {formatModeSelection(session.modeSelection)}
                </div>
                <div className="rounded-full border border-white/10 bg-white/5 px-3 py-2 text-xs font-medium uppercase tracking-[0.2em] text-white/60">
                  {session.timerProfile === 'insane' ? 'Insane · 5s' : 'Normal · 8s'}
                </div>
                <div className="rounded-full border border-white/10 bg-white/5 px-3 py-2 text-xs font-medium uppercase tracking-[0.2em] text-white/60">
                  Round {activeRound?.roundNumber || session.roundsPlayed + 1}
                </div>
              </div>

              <div className="mt-4 grid gap-3 sm:grid-cols-3">
                <div className="rounded-2xl border border-white/10 bg-white/5 p-4">
                  <div className="flex items-center gap-3">
                    <BaseIcon className="text-white/70" path={mdiHeart} size={18} />
                    <div>
                      <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                        Lives
                      </p>
                      <div className="mt-1 flex items-center gap-1.5">
                        {Array.from({ length: lives }).map((_, index) => (
                          <BaseIcon
                            key={`life-${index}`}
                            className={
                              index < (session.livesRemaining || 0)
                                ? 'text-[#ff668f]'
                                : 'text-white/20'
                            }
                            path={
                              index < (session.livesRemaining || 0)
                                ? mdiHeart
                                : mdiHeartOutline
                            }
                            size={18}
                          />
                        ))}
                      </div>
                    </div>
                  </div>
                </div>

                <div className="rounded-2xl border border-white/10 bg-white/5 p-4">
                  <div className="flex items-center gap-3">
                    <BaseIcon className="text-white/70" path={mdiShieldCheckOutline} size={18} />
                    <div>
                      <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                        Current streak
                      </p>
                      <p className="mt-1 text-2xl font-semibold text-white">
                        {session.streak}
                      </p>
                    </div>
                  </div>
                </div>

                <div className="rounded-2xl border border-white/10 bg-white/5 p-4">
                  <div className="flex items-center gap-3">
                    <BaseIcon className="text-white/70" path={mdiChartBoxOutline} size={18} />
                    <div>
                      <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                        Best streak
                      </p>
                      <p className="mt-1 text-2xl font-semibold text-white">
                        {session.bestStreak}
                      </p>
                    </div>
                  </div>
                </div>
              </div>
            </CardBox>

            <CardBox className="border border-white/10 bg-[#10141d] text-white">
              <div className="space-y-3">
                <div className="flex items-center justify-between gap-3">
                  <div>
                    <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                      Round timer
                    </p>
                    <p className="mt-1 text-lg font-semibold text-white">
                      {currentRound
                        ? `${Math.max(remainingMs / 1000, 0).toFixed(1)}s left`
                        : 'Resolving round'}
                    </p>
                  </div>
                  <BaseIcon className="text-white/60" path={mdiTimerOutline} size={20} />
                </div>

                <div className="h-2 overflow-hidden rounded-full bg-white/10">
                  <div
                    className={`h-full rounded-full transition-[width] duration-100 ${
                      timerProgress > 45
                        ? 'bg-emerald-400'
                        : timerProgress > 15
                          ? 'bg-yellow-400'
                          : 'bg-red-400'
                    }`}
                    style={{ width: `${timerProgress}%` }}
                  />
                </div>

                <p className="text-sm text-white/62">{getResultMessage(revealedRound)}</p>
              </div>
            </CardBox>
          </div>
        ) : null}

        {!isLoadingState && lobbyVisible ? (
          <div className="grid gap-6 xl:grid-cols-[1.5fr_1fr]">
            <CardBox className="border border-white/10 bg-[#10141d] text-white">
              <div>
                <p className="text-sm uppercase tracking-[0.24em] text-white/45">
                  Quick start
                </p>
                <h2 className="mt-3 text-3xl font-semibold tracking-tight text-white">
                  Minimal, fast, and anti-cheat.
                </h2>
                <p className="mt-3 max-w-2xl text-base leading-7 text-white/68">
                  Start a run with two lives, guess the higher hidden stat, and let the
                  server reveal beatmap playcount, mapper totals, or artist totals only
                  after you answer.
                </p>
              </div>

              <div className="mt-8 grid gap-4 lg:grid-cols-2">
                <div>
                  <p className="mb-3 text-[11px] uppercase tracking-[0.18em] text-white/45">
                    Choose your mode
                  </p>
                  <div className="space-y-3">
                    {modeOptions.map((option) => {
                      const isActive = option.value === modeSelection;

                      return (
                        <button
                          className={`w-full rounded-2xl border px-4 py-4 text-left transition ${
                            isActive
                              ? 'border-[#ff66aa]/70 bg-[#ff66aa]/10 text-white'
                              : 'border-white/10 bg-white/5 text-white/78 hover:border-white/25'
                          }`}
                          key={option.value}
                          onClick={() => setModeSelection(option.value)}
                          type="button"
                        >
                          <p className="text-lg font-medium text-white">{option.label}</p>
                          <p className="mt-2 text-sm leading-6 text-white/60">
                            {option.helper}
                          </p>
                        </button>
                      );
                    })}
                  </div>
                </div>

                <div>
                  <p className="mb-3 text-[11px] uppercase tracking-[0.18em] text-white/45">
                    Choose your timer
                  </p>
                  <div className="space-y-3">
                    {timerOptions.map((option) => {
                      const isActive = option.value === timerProfile;

                      return (
                        <button
                          className={`w-full rounded-2xl border px-4 py-4 text-left transition ${
                            isActive
                              ? 'border-[#ff66aa]/70 bg-[#ff66aa]/10 text-white'
                              : 'border-white/10 bg-white/5 text-white/78 hover:border-white/25'
                          }`}
                          key={option.value}
                          onClick={() => setTimerProfile(option.value)}
                          type="button"
                        >
                          <div className="flex items-center gap-3">
                            <BaseIcon
                              className={isActive ? 'text-[#ff99c6]' : 'text-white/55'}
                              path={option.icon}
                              size={18}
                            />
                            <p className="text-lg font-medium text-white">{option.label}</p>
                          </div>
                          <p className="mt-2 text-sm leading-6 text-white/60">
                            {option.helper}
                          </p>
                        </button>
                      );
                    })}
                  </div>
                </div>
              </div>

              <div className="mt-8 flex flex-wrap items-center gap-3">
                <BaseButton
                  color="info"
                  disabled={isStarting}
                  icon={mdiGamepadSquareOutline}
                  label={isStarting ? 'Starting...' : 'Start run'}
                  onClick={handleStartSession}
                />
                <BaseButton
                  color="whiteDark"
                  href="/play/history"
                  icon={mdiHistory}
                  label="View run history"
                />
              </div>
            </CardBox>

            <div className="space-y-6">
              <CardBox className="border border-white/10 bg-[#10141d] text-white">
                <div className="flex items-center justify-between gap-3">
                  <div>
                    <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                      All-time ladder
                    </p>
                    <p className="mt-1 text-xl font-semibold text-white">
                      Top current streaks
                    </p>
                  </div>
                </div>

                <div className="mt-5 space-y-3">
                  {leaderboard.length ? (
                    leaderboard.slice(0, 5).map((entry, index) => (
                      <div
                        className="flex items-center justify-between rounded-2xl border border-white/10 bg-white/5 px-4 py-3"
                        key={entry.id}
                      >
                        <div>
                          <p className="text-sm text-white/55">#{index + 1}</p>
                          <p className="mt-1 text-base font-medium text-white">
                            {entry.user.name}
                          </p>
                        </div>
                        <div className="text-right">
                          <p className="text-xl font-semibold text-white">{entry.score}</p>
                          <p className="text-xs uppercase tracking-[0.16em] text-white/45">
                            correct guesses
                          </p>
                        </div>
                      </div>
                    ))
                  ) : (
                    <div className="rounded-2xl border border-dashed border-white/10 bg-white/[0.03] px-4 py-6 text-sm text-white/55">
                      Finish your first run to create a leaderboard entry.
                    </div>
                  )}
                </div>
              </CardBox>

              <CardBox className="border border-white/10 bg-[#10141d] text-white">
                <p className="text-[11px] uppercase tracking-[0.18em] text-white/45">
                  Recent runs
                </p>
                <div className="mt-4 space-y-3">
                  {history.length ? (
                    history.slice(0, 3).map((item) => (
                      <Link
                        className="block rounded-2xl border border-white/10 bg-white/5 px-4 py-3 transition hover:border-white/20"
                        href={`/play/sessions/${item.id}`}
                        key={item.id}
                      >
                        <div className="flex items-center justify-between gap-3">
                          <div>
                            <p className="text-base font-medium text-white">
                              {formatModeSelection(item.modeSelection)} · {item.correctGuesses} correct
                            </p>
                            <p className="mt-1 text-sm text-white/55">
                              {formatSessionDate(item.endedAt || item.startedAt)}
                            </p>
                          </div>
                          <p className="text-sm text-white/65">
                            Best {item.bestStreak}
                          </p>
                        </div>
                      </Link>
                    ))
                  ) : (
                    <div className="rounded-2xl border border-dashed border-white/10 bg-white/[0.03] px-4 py-6 text-sm text-white/55">
                      No runs yet. Start a session to build your history.
                    </div>
                  )}
                </div>
              </CardBox>
            </div>
          </div>
        ) : null}

        {!isLoadingState && activeRound ? (
          <div className="space-y-5">
            <div className="rounded-2xl border border-white/10 bg-[#10141d] px-4 py-3 text-sm text-white/70">
              {currentRound ? (
                <span>
                  Hidden values stay server-side until you answer. Pick A or B before the
                  timer expires.
                </span>
              ) : (
                <span>{getResultMessage(revealedRound)}</span>
              )}
            </div>

            <div className="grid gap-5 lg:grid-cols-2">
              <GuessCard
                card={activeRound.cards.a}
                disabled={Boolean(revealedRound) || isSubmittingChoice}
                onPick={currentRound ? submitChoice : undefined}
                reveal={
                  revealedRound?.reveal
                    ? {
                        show: true,
                        value: revealedRound.reveal.values.a,
                        valueSuffix: revealedRound.reveal.valueSuffix,
                        isWinner: revealedRound.reveal.winningChoice === 'a',
                        isWrongSelection:
                          !revealedRound.reveal.isCorrect &&
                          revealedRound.reveal.playerChoice === 'a',
                      }
                    : undefined
                }
                slot="a"
              />
              <GuessCard
                card={activeRound.cards.b}
                disabled={Boolean(revealedRound) || isSubmittingChoice}
                onPick={currentRound ? submitChoice : undefined}
                reveal={
                  revealedRound?.reveal
                    ? {
                        show: true,
                        value: revealedRound.reveal.values.b,
                        valueSuffix: revealedRound.reveal.valueSuffix,
                        isWinner: revealedRound.reveal.winningChoice === 'b',
                        isWrongSelection:
                          !revealedRound.reveal.isCorrect &&
                          revealedRound.reveal.playerChoice === 'b',
                      }
                    : undefined
                }
                slot="b"
              />
            </div>
          </div>
        ) : null}

        {!isLoadingState && gameOver && session ? (
          <CardBox className="mt-6 border border-white/10 bg-[#10141d] text-white">
            <div className="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
              <div>
                <p className="text-sm uppercase tracking-[0.24em] text-white/45">
                  Run complete
                </p>
                <h2 className="mt-2 text-3xl font-semibold text-white">
                  {session.correctGuesses} correct guesses with a best streak of {session.bestStreak}.
                </h2>
                <p className="mt-3 text-base leading-7 text-white/65">
                  Review every reveal in the run detail page, then spin up another instant
                  session when you are ready.
                </p>
              </div>
              <div className="flex flex-wrap items-center gap-3">
                <BaseButton
                  color="info"
                  icon={mdiRefresh}
                  label="Start another run"
                  onClick={handleStartSession}
                />
                <BaseButton
                  color="whiteDark"
                  href={`/play/sessions/${session.id}`}
                  label="View run details"
                />
              </div>
            </div>
          </CardBox>
        ) : null}
      </SectionMain>
    </>
  );
}

PlayPage.getLayout = function getLayout(page: ReactElement) {
  return <LayoutAuthenticated permission="READ_GAME_SESSIONS">{page}</LayoutAuthenticated>;
};
