import { useNavigate } from '@solidjs/router'
import { createEffect, createMemo, createSignal, onMount } from 'solid-js'

import {
  AnswerInput,
  AttemptIndicator,
  GameCard,
  HintButton,
  ScoreDisplay,
  Timer,
} from '@knowfoolery/ui-components'
import Button from '@suid/material/Button'
import Stack from '@suid/material/Stack'
import Typography from '@suid/material/Typography'

import { useTimer } from '../hooks/useTimer'
import { leaderboardClient } from '../services/api'
import { appendGameHistory, saveLastResult } from '../services/session'

type QuizQuestion = {
  theme: string
  text: string
  answer: string
  hint: string
}

const QUESTION: QuizQuestion = {
  theme: 'Général',
  text: 'Quel est le plus petit nombre premier ?',
  answer: '2',
  hint: 'C’est le seul nombre premier pair.',
}

function normalizeAnswer(answer: string): string {
  return answer.trim().toLowerCase()
}

async function estimateLeaderboardPosition(score: number, durationSec: number): Promise<number | null> {
  try {
    const rows = await leaderboardClient.top10()
    const sorted = [...rows].sort((a, b) => {
      if (b.score !== a.score) return b.score - a.score
      const durationA = typeof a.durationSec === 'number' ? a.durationSec : Number.MAX_SAFE_INTEGER
      const durationB = typeof b.durationSec === 'number' ? b.durationSec : Number.MAX_SAFE_INTEGER
      return durationA - durationB
    })

    let rank = 1
    for (const row of sorted) {
      const rowDuration = typeof row.durationSec === 'number' ? row.durationSec : Number.MAX_SAFE_INTEGER
      if (score > row.score) break
      if (score === row.score && durationSec < rowDuration) break
      rank += 1
    }

    return rank <= 10 ? rank : null
  } catch {
    return null
  }
}

export default function GameRoute() {
  const navigate = useNavigate()

  const [question] = createSignal(QUESTION)
  const [answer, setAnswer] = createSignal('')
  const [attempts, setAttempts] = createSignal(0)
  const [hintUsed, setHintUsed] = createSignal(false)
  const [score, setScore] = createSignal(0)
  const [message, setMessage] = createSignal<string | null>(null)
  const [finished, setFinished] = createSignal(false)

  const durationMs = 30 * 60 * 1000
  const startedAt = Date.now()
  const { remainingMs, isExpired, start, stop } = useTimer(durationMs)

  onMount(() => start())

  const attemptsLeft = createMemo(() => Math.max(0, 3 - attempts()))
  const answerLocked = createMemo(() => finished() || attemptsLeft() === 0 || isExpired())

  const finalize = async (finalScore: number, answered: number, correct: number) => {
    if (finished()) return
    setFinished(true)
    stop()

    const elapsedSec = Math.max(1, Math.floor((Date.now() - startedAt) / 1000))
    const durationSec = Math.min(Math.floor(durationMs / 1000), elapsedSec)
    const successRate = answered > 0 ? Math.round((correct / answered) * 100) : 0
    const playerName = window.localStorage.getItem('kf.playerName') ?? 'Anonymous'
    const leaderboardPosition = await estimateLeaderboardPosition(finalScore, durationSec)

    const result = {
      playerName,
      finalScore,
      answered,
      correct,
      successRate,
      durationSec,
      leaderboardPosition,
      finishedAt: new Date().toISOString(),
    }

    saveLastResult(result)
    appendGameHistory(result)
    navigate('/results')
  }

  createEffect(() => {
    if (isExpired() && !finished()) {
      setMessage('Temps écoulé.')
      const answered = attempts() > 0 ? 1 : 0
      void finalize(score(), answered, 0)
    }
  })

  const submit = () => {
    if (answerLocked()) return

    const normalized = normalizeAnswer(answer())
    if (!normalized) {
      setMessage('Saisis une réponse avant d’envoyer.')
      return
    }

    const isCorrect = normalized === normalizeAnswer(question().answer) || normalized === 'deux'
    const nextAttempts = attempts() + 1
    setAttempts(nextAttempts)

    if (isCorrect) {
      const delta = hintUsed() ? 1 : 2
      const nextScore = score() + delta
      setScore(nextScore)
      setMessage(`Bonne réponse (+${delta}).`)
      void finalize(nextScore, 1, 1)
      return
    }

    if (nextAttempts >= 3) {
      setMessage("Mauvaise réponse. Plus d'essais.")
      void finalize(score(), 1, 0)
    } else {
      setMessage('Mauvaise réponse.')
    }

    setAnswer('')
  }

  const confirmHint = () => {
    if (hintUsed() || attempts() > 0 || answerLocked()) return
    setHintUsed(true)
    setMessage(`Indice: ${question().hint}`)
  }

  return (
    <GameCard
      title="Partie"
      theme={question().theme}
      themeLabelPrefix="Thème : "
      questionText={question().text}
      timerSlot={<Timer remainingMs={remainingMs()} />}
      scoreSlot={<ScoreDisplay score={score()} />}
      answerSlot={
        <AnswerInput
          label="Ta réponse"
          value={answer()}
          disabled={answerLocked()}
          onInputValue={setAnswer}
          onSubmit={submit}
        />
      }
      attemptSlot={<AttemptIndicator attemptsUsed={attempts()} attemptsMax={3} />}
      actionsSlot={
        <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
          <Button variant="contained" disabled={answerLocked()} onClick={submit}>
            Envoyer
          </Button>
          <HintButton
            disabled={hintUsed() || attempts() > 0 || answerLocked()}
            buttonLabel="Indice (score réduit)"
            confirmTitle="Confirmer l’indice"
            confirmMessage="Demander un indice réduit le score maximal de la question de 2 à 1 point. Continuer ?"
            confirmLabel="Oui, utiliser un indice"
            cancelLabel="Annuler"
            onConfirm={confirmHint}
          />
        </Stack>
      }
      feedbackSlot={
        <>
          {message() && <Typography sx={{ opacity: 0.9 }}>{message()}</Typography>}
          {attemptsLeft() === 0 && <Typography color="warning.main">Plus d'essais.</Typography>}
          {isExpired() && <Typography color="error.main">Temps écoulé.</Typography>}
        </>
      }
    />
  )
}
