import { useEffect, useMemo, useRef, useState } from 'react'
import type { Dispatch, SetStateAction } from 'react'
import { Link } from 'react-router-dom'
import {
  getCourtAssignments,
  getReceivingPlayer,
  getServiceCourt,
  getServingPlayer,
  getTeamDisplayName,
} from '../lib/match'
import type {
  CourtSide,
  GroupTeam,
  PlayerSlot,
  RoundGroup,
  ScoreSide,
  ScoreState,
} from '../types'

type VoiceSettings = {
  announceScore: boolean
  announceServer: boolean
  rate: number
}

const VOICE_SETTINGS_STORAGE_KEY = 'badminton-scoreboard::voice-settings'
const defaultVoiceSettings: VoiceSettings = {
  announceScore: true,
  announceServer: true,
  rate: 1,
}

type ScoreboardPageProps = {
  currentSelectionOrder: string[]
  finishDialogError: string
  finishDialogOpen: boolean
  finishDialogUploading: boolean
  groupSource: 'idle' | 'db' | 'manual'
  hasRecordedPoint: boolean
  leftTeam: GroupTeam | null
  rightTeam: GroupTeam | null
  scoreState: ScoreState
  selectedGroup: RoundGroup | null
  streakAnnouncement: {
    count: number
    key: number
    teamName: string
    title: string
  } | null
  victoryAnnouncement: {
    key: number
    scoreLabel: string
    teamName: string
    title: string
  } | null
  targetDate: string
  onApplyMatchup: (
    leftTeam: GroupTeam,
    rightTeam: GroupTeam,
    targetScore: number,
  ) => void
  onCloseFinishDialog: () => void
  onConfirmUpload: () => void
  onOpenFinishDialog: () => void
  onRecordPoint: (side: ScoreSide) => void
  onSetServing: (side: ScoreSide) => void
  onSkipUpload: () => void
  onSwapMatchup: () => void
  onSwapTeamPlayers: (side: ScoreSide) => void
  onUndoLastPoint: () => void
}

export function ScoreboardPage({
  currentSelectionOrder,
  finishDialogError,
  finishDialogOpen,
  finishDialogUploading,
  groupSource,
  hasRecordedPoint,
  leftTeam,
  rightTeam,
  scoreState,
  selectedGroup,
  streakAnnouncement,
  victoryAnnouncement,
  targetDate,
  onApplyMatchup,
  onCloseFinishDialog,
  onConfirmUpload,
  onOpenFinishDialog,
  onRecordPoint,
  onSetServing,
  onSkipUpload,
  onSwapMatchup,
  onSwapTeamPlayers,
  onUndoLastPoint,
}: ScoreboardPageProps) {
  const [pickerOpen, setPickerOpen] = useState(false)
  const [settingsOpen, setSettingsOpen] = useState(false)
  const [draftPlayers, setDraftPlayers] = useState<string[]>([])
  const [draftTargetScore, setDraftTargetScore] = useState(() =>
    String(scoreState.targetScore),
  )
  const [clock, setClock] = useState(() => formatClock())
  const [voiceSettings, setVoiceSettings] = useState<VoiceSettings>(() =>
    loadVoiceSettings(),
  )
  const lastAnnouncedPointRef = useRef(0)
  const previousScoresRef = useRef({ left: 0, right: 0 })

  useEffect(() => {
    const timer = window.setInterval(() => {
      setClock(formatClock())
    }, 1000)

    return () => window.clearInterval(timer)
  }, [])

  useEffect(() => {
    window.localStorage.setItem(
      VOICE_SETTINGS_STORAGE_KEY,
      JSON.stringify(voiceSettings),
    )
  }, [voiceSettings])

  useEffect(() => {
    return () => {
      if ('speechSynthesis' in window) {
        window.speechSynthesis.cancel()
      }
    }
  }, [])

  const selectablePlayers = useMemo(() => {
    if (!selectedGroup) {
      return []
    }

    const seen = new Set<string>()
    const players: string[] = []

    selectedGroup.teams.forEach((team) => {
      if (!team.isPlaceholderA && !seen.has(team.playerA)) {
        seen.add(team.playerA)
        players.push(team.playerA)
      }

      if (!team.isPlaceholderB && !seen.has(team.playerB)) {
        seen.add(team.playerB)
        players.push(team.playerB)
      }
    })

    return players
  }, [selectedGroup])

  const presetTeams = useMemo(
    () =>
      selectedGroup?.teams.filter((team) => !team.isPlaceholderA && !team.isPlaceholderB) ?? [],
    [selectedGroup],
  )

  const canArrangeMatch = !hasRecordedPoint
  const canScore = scoreState.serving !== null

  const servingScore =
    scoreState.serving === 'left' ? scoreState.scoreLeft : scoreState.scoreRight
  const servingCourt =
    scoreState.serving === null ? null : getServiceCourt(servingScore)

  const leftAssignments = useMemo(
    () =>
      leftTeam ? getCourtAssignments(leftTeam, scoreState.leftRightCourtPlayer) : [],
    [leftTeam, scoreState.leftRightCourtPlayer],
  )
  const rightAssignments = useMemo(
    () =>
      rightTeam ? getCourtAssignments(rightTeam, scoreState.rightRightCourtPlayer) : [],
    [rightTeam, scoreState.rightRightCourtPlayer],
  )

  const currentServer =
    scoreState.serving === 'left'
      ? leftTeam
        ? getServingPlayer(leftTeam, scoreState.leftRightCourtPlayer, scoreState.scoreLeft)
        : null
      : scoreState.serving === 'right'
        ? rightTeam
          ? getServingPlayer(
              rightTeam,
              scoreState.rightRightCourtPlayer,
              scoreState.scoreRight,
            )
          : null
        : null

  const currentReceiver =
    scoreState.serving === 'left'
      ? rightTeam
        ? getReceivingPlayer(
            rightTeam,
            scoreState.rightRightCourtPlayer,
            scoreState.scoreLeft,
          )
        : null
      : scoreState.serving === 'right'
        ? leftTeam
          ? getReceivingPlayer(
              leftTeam,
              scoreState.leftRightCourtPlayer,
              scoreState.scoreRight,
            )
          : null
        : null

  useEffect(() => {
    const totalPoints = scoreState.scoreLeft + scoreState.scoreRight

    if (scoreState.serving === null || !currentServer?.name || totalPoints === 0) {
      lastAnnouncedPointRef.current = totalPoints
      previousScoresRef.current = {
        left: scoreState.scoreLeft,
        right: scoreState.scoreRight,
      }
      return
    }

    if (lastAnnouncedPointRef.current === totalPoints) {
      return
    }

    lastAnnouncedPointRef.current = totalPoints

    const scorerSide =
      scoreState.scoreLeft > previousScoresRef.current.left
        ? 'left'
        : scoreState.scoreRight > previousScoresRef.current.right
          ? 'right'
          : null

    previousScoresRef.current = {
      left: scoreState.scoreLeft,
      right: scoreState.scoreRight,
    }

    const parts: string[] = []

    if (voiceSettings.announceScore && scorerSide) {
      parts.push(
        `${getAnnouncementName(scorerSide === 'left' ? leftTeam : rightTeam)}得分`,
      )
    }

    if (voiceSettings.announceServer) {
      parts.push(`${currentServer.name}發球`)
    }

    if (parts.length > 0) {
      speakAnnouncement(parts.join('，'), voiceSettings.rate)
    }
  }, [
    currentServer?.name,
    leftTeam,
    rightTeam,
    scoreState.scoreLeft,
    scoreState.scoreRight,
    scoreState.serving,
    voiceSettings.announceScore,
    voiceSettings.announceServer,
    voiceSettings.rate,
  ])

  if (!selectedGroup) {
    return (
      <section className="page-grid">
        <article className="panel panel-hero">
          <p className="panel-kicker">Step 3</p>
          <h2>請先回到選隊伍頁面</h2>
          <p className="panel-copy">
            目前還沒有帶入要上場的組別，先回去選擇分組，再進入記分板。
          </p>
          <Link className="primary-button inline-link" to="/teams">
            回到選隊伍
          </Link>
        </article>
      </section>
    )
  }

  const matchupLabel =
    leftTeam && rightTeam
      ? `${getTeamDisplayName(leftTeam)} vs ${getTeamDisplayName(rightTeam)}`
      : '尚未設定對戰隊伍'

  const openPicker = () => {
    setDraftPlayers(currentSelectionOrder.filter((player) => selectablePlayers.includes(player)))
    setDraftTargetScore(String(scoreState.targetScore))
    setPickerOpen(true)
  }

  const toggleDraftPlayer = (playerName: string) => {
    setDraftPlayers((current) => {
      if (current.includes(playerName)) {
        return current.filter((value) => value !== playerName)
      }

      if (current.length >= 4) {
        return current
      }

      return [...current, playerName]
    })
  }

  const togglePresetTeam = (team: GroupTeam) => {
    setDraftPlayers((current) => {
      const removed = removePresetTeamFromDraft(current, team)

      if (removed.length !== current.length) {
        return removed
      }

      if (current.length >= 4 || current.length % 2 !== 0) {
        return current
      }

      if (current.includes(team.playerA) || current.includes(team.playerB)) {
        return current
      }

      return [...current, team.playerA, team.playerB]
    })
  }

  const confirmDraftTeams = () => {
    if (draftPlayers.length !== 4) {
      return
    }

    onApplyMatchup(
      {
        id: -1,
        playerA: draftPlayers[0],
        playerB: draftPlayers[1],
        isPlaceholderA: false,
        isPlaceholderB: false,
      },
      {
        id: -2,
        playerA: draftPlayers[3],
        playerB: draftPlayers[2],
        isPlaceholderA: false,
        isPlaceholderB: false,
      },
      sanitizeTargetScore(draftTargetScore),
    )
    setPickerOpen(false)
  }

  const autoPickDraftPlayers = () => {
    const shuffled = [...selectablePlayers]

    for (let index = shuffled.length - 1; index > 0; index -= 1) {
      const swapIndex = Math.floor(Math.random() * (index + 1))
      ;[shuffled[index], shuffled[swapIndex]] = [shuffled[swapIndex], shuffled[index]]
    }

    setDraftPlayers(shuffled.slice(0, 4))
  }

  return (
    <>
      {streakAnnouncement ? (
        <div className="streak-banner" key={streakAnnouncement.key}>
          <span className="streak-banner-count">{streakAnnouncement.count} 連勝</span>
          <strong>{streakAnnouncement.title}</strong>
          <small>{streakAnnouncement.teamName}</small>
        </div>
      ) : null}

      {victoryAnnouncement ? (
        <div className="victory-banner" key={victoryAnnouncement.key}>
          <span className="victory-banner-kicker">目標分數達成</span>
          <strong>{victoryAnnouncement.title}</strong>
          <small>{victoryAnnouncement.teamName}</small>
          <em>{victoryAnnouncement.scoreLabel}</em>
        </div>
      ) : null}

      <section className="scoreboard-screen">
        <div className="scoreboard-court">
          <ScoreboardTeamPanel
            assignments={leftAssignments}
            canArrangeMatch={canArrangeMatch}
            canScore={canScore}
            currentReceiver={scoreState.serving === 'right' ? currentReceiver?.name ?? null : null}
            currentServer={scoreState.serving === 'left' ? currentServer?.name ?? null : null}
            onRecordPoint={() => onRecordPoint('left')}
            onSetServing={() => onSetServing('left')}
            onSwapPlayers={() => onSwapTeamPlayers('left')}
            onSwapTeams={onSwapMatchup}
            score={scoreState.scoreLeft}
            serviceCourt={scoreState.serving === 'left' ? servingCourt : null}
            showServingPrompt={scoreState.serving === null}
            team={leftTeam}
            teamSlot="top"
          />

          <div className="scoreboard-center-banner">
            <p>{scoreState.serving === null ? '請先設定發球方' : '比賽進行中'}</p>
            <small>
              {scoreState.serving === null
                ? `本場 ${scoreState.targetScore} 分獲勝`
                : `發球：${currentServer?.name ?? '-'}${
                    currentReceiver ? ` / 接發：${currentReceiver.name}` : ''
                  } / 目標 ${scoreState.targetScore} 分`}
            </small>
          </div>

          <ScoreboardTeamPanel
            assignments={rightAssignments}
            canArrangeMatch={canArrangeMatch}
            canScore={canScore}
            currentReceiver={scoreState.serving === 'left' ? currentReceiver?.name ?? null : null}
            currentServer={scoreState.serving === 'right' ? currentServer?.name ?? null : null}
            onRecordPoint={() => onRecordPoint('right')}
            onSetServing={() => onSetServing('right')}
            onSwapPlayers={() => onSwapTeamPlayers('right')}
            onSwapTeams={onSwapMatchup}
            score={scoreState.scoreRight}
            serviceCourt={scoreState.serving === 'right' ? servingCourt : null}
            showServingPrompt={scoreState.serving === null}
            team={rightTeam}
            teamSlot="bottom"
          />
        </div>

        <aside className="scoreboard-rail">
          <div className="rail-icon-grid">
            {hasRecordedPoint ? (
              <button className="rail-square-button" type="button" onClick={onUndoLastPoint}>
                上一步
              </button>
            ) : (
              <button className="rail-square-button" type="button" onClick={openPicker}>
                設定隊伍
              </button>
            )}
            <button
              aria-label="語音設定"
              className="rail-square-button"
              type="button"
              onClick={() => setSettingsOpen(true)}
            >
              設定
            </button>
          </div>

          <div className="rail-clock">{clock}</div>

          <button className="rail-pill rail-pill-danger" type="button" onClick={onOpenFinishDialog}>
            比賽結算
          </button>
        </aside>
      </section>

      {pickerOpen ? (
        <TeamPickerModal
          draftPlayers={draftPlayers}
          draftTargetScore={draftTargetScore}
          group={selectedGroup}
          presetTeams={presetTeams}
          selectablePlayers={selectablePlayers}
          selectionCount={draftPlayers.length}
          sourceLabel={groupSource === 'db' ? 'DB' : groupSource === 'manual' ? '手動' : '-'}
          targetDate={targetDate}
          onAutoPick={autoPickDraftPlayers}
          onClear={() => setDraftPlayers([])}
          onClose={() => setPickerOpen(false)}
          onConfirm={confirmDraftTeams}
          onDraftTargetScoreChange={setDraftTargetScore}
          onTogglePlayer={toggleDraftPlayer}
          onTogglePresetTeam={togglePresetTeam}
        />
      ) : null}

      {settingsOpen ? (
        <VoiceSettingsModal
          settings={voiceSettings}
          onClose={() => setSettingsOpen(false)}
          onUpdateSettings={setVoiceSettings}
        />
      ) : null}

      {finishDialogOpen ? (
        <FinishDialog
          error={finishDialogError}
          leftScore={scoreState.scoreLeft}
          leftTeamName={leftTeam ? getTeamDisplayName(leftTeam) : '未設定'}
          matchupLabel={matchupLabel}
          rightScore={scoreState.scoreRight}
          rightTeamName={rightTeam ? getTeamDisplayName(rightTeam) : '未設定'}
          uploading={finishDialogUploading}
          onClose={onCloseFinishDialog}
          onConfirm={onConfirmUpload}
          onSkip={onSkipUpload}
        />
      ) : null}
    </>
  )
}

type ScoreboardTeamPanelProps = {
  assignments: Array<{ slot: PlayerSlot; name: string; court: CourtSide }>
  canArrangeMatch: boolean
  canScore: boolean
  currentReceiver: string | null
  currentServer: string | null
  onRecordPoint: () => void
  onSetServing: () => void
  onSwapPlayers: () => void
  onSwapTeams: () => void
  score: number
  serviceCourt: CourtSide | null
  showServingPrompt: boolean
  team: GroupTeam | null
  teamSlot: 'top' | 'bottom'
}

function ScoreboardTeamPanel({
  assignments,
  canArrangeMatch,
  canScore,
  currentReceiver,
  currentServer,
  onRecordPoint,
  onSetServing,
  onSwapPlayers,
  onSwapTeams,
  score,
  serviceCourt,
  showServingPrompt,
  team,
  teamSlot,
}: ScoreboardTeamPanelProps) {
  const orderedAssignments = [...assignments].sort((left, right) => {
    if (left.court === right.court) {
      return 0
    }

    return left.court === 'left' ? -1 : 1
  })

  const header = (
    <div className="scoreboard-team-head">
      <div className="team-head-main">
        {orderedAssignments.map((assignment) => (
          <div
            className={
              assignment.name === currentServer
                ? 'scoreboard-name-chip scoreboard-name-chip-serving'
                : 'scoreboard-name-chip'
            }
            key={assignment.slot}
          >
            <span className="team-number">{getPlayerNumber(teamSlot, assignment.slot)}</span>
            <strong>{assignment.name}</strong>
          </div>
        ))}
      </div>

      <div className="team-head-buttons">
        <button
          aria-label="交換上下隊伍"
          className="team-icon-button"
          disabled={!canArrangeMatch}
          type="button"
          onClick={onSwapTeams}
        >
          ↕
        </button>
        <button
          aria-label="交換左右位置"
          className="team-icon-button"
          disabled={!canArrangeMatch}
          type="button"
          onClick={onSwapPlayers}
        >
          ↔
        </button>
      </div>
    </div>
  )

  const serveBar = (
    <button
      className={
        currentServer && !canArrangeMatch
          ? 'serve-lane serve-lane-locked'
          : showServingPrompt
            ? 'serve-lane serve-lane-prompt'
            : 'serve-lane'
      }
      disabled={!canArrangeMatch || !team}
      type="button"
      onClick={onSetServing}
    >
      <span className={currentServer ? 'serve-lane-box serve-lane-box-checked' : 'serve-lane-box'} />
      <span>先攻</span>
      {currentServer ? (
        <small>
          發球區：{serviceCourt === 'left' ? '左' : serviceCourt === 'right' ? '右' : '-'}
          {currentReceiver ? ` / 接發：${currentReceiver}` : ''}
        </small>
      ) : (
        <small>點擊設定這一隊先攻</small>
      )}
    </button>
  )

  const scoreBoard = (
    <button
      className={canScore ? 'score-panel-surface score-panel-surface-live' : 'score-panel-surface'}
      disabled={!canScore || !team}
      type="button"
      onClick={onRecordPoint}
    >
      <span className="score-panel-value">{score}</span>
    </button>
  )

  return (
    <section className="scoreboard-team-section">
      {teamSlot === 'top' ? (
        <>
          {header}
          {serveBar}
          {scoreBoard}
        </>
      ) : (
        <>
          {scoreBoard}
          {serveBar}
          {header}
        </>
      )}
    </section>
  )
}

type TeamPickerModalProps = {
  draftPlayers: string[]
  draftTargetScore: string
  group: RoundGroup
  presetTeams: GroupTeam[]
  selectablePlayers: string[]
  selectionCount: number
  sourceLabel: string
  targetDate: string
  onAutoPick: () => void
  onClear: () => void
  onClose: () => void
  onConfirm: () => void
  onDraftTargetScoreChange: (value: string) => void
  onTogglePlayer: (playerName: string) => void
  onTogglePresetTeam: (team: GroupTeam) => void
}

function TeamPickerModal({
  draftPlayers,
  draftTargetScore,
  group,
  presetTeams,
  selectablePlayers,
  selectionCount,
  sourceLabel,
  targetDate,
  onAutoPick,
  onClear,
  onClose,
  onConfirm,
  onDraftTargetScoreChange,
  onTogglePlayer,
  onTogglePresetTeam,
}: TeamPickerModalProps) {
  return (
    <div className="team-picker-overlay" role="presentation" onClick={onClose}>
      <div
        aria-label="設定隊伍"
        aria-modal="true"
        className="team-picker-shell"
        onClick={(event) => event.stopPropagation()}
        role="dialog"
      >
        <button aria-label="關閉設定隊伍" className="team-picker-close" type="button" onClick={onClose}>
          ×
        </button>

        <div className="team-picker-ribbon">
          <span>{selectionCount >= 4 ? '已完成選擇' : '請選滿 4 位球員'}</span>
        </div>

        <div className="team-picker-layout">
          <section className="team-picker-panel team-picker-list-panel">
            <div className="team-picker-title">
              <span className="team-picker-count">{selectionCount}/4</span>
              <div>
                <strong>依序選擇球員</strong>
                <p>
                  第 {group.id} 組 / {sourceLabel} / {targetDate || '-'}
                </p>
              </div>
            </div>

            <div className="team-picker-config-row">
              <label className="team-picker-config team-picker-config-compact">
                <span>獲勝分數</span>
                <input
                  className="team-picker-score-input team-picker-score-input-compact"
                  inputMode="numeric"
                  maxLength={2}
                  type="text"
                  value={draftTargetScore}
                  onChange={(event) => onDraftTargetScoreChange(event.target.value)}
                />
              </label>
            </div>

            <div className="team-picker-list">
              {selectablePlayers.map((playerName) => {
                const checked = draftPlayers.includes(playerName)
                const selectedOrder = checked ? draftPlayers.indexOf(playerName) + 1 : null

                return (
                  <button
                    className={
                      checked
                        ? 'team-picker-option team-picker-option-active'
                        : 'team-picker-option'
                    }
                    key={`player-option-${playerName}`}
                    type="button"
                    onClick={() => onTogglePlayer(playerName)}
                  >
                    <span className="team-picker-checkbox">
                      {checked ? String(selectedOrder) : ''}
                    </span>
                    <div className="team-picker-option-text">
                      <strong>{playerName}</strong>
                      <small>
                        {selectedOrder ? `已選為第 ${selectedOrder} 位` : '尚未加入上場名單'}
                      </small>
                    </div>
                  </button>
                )
              })}
            </div>

            <div className="team-picker-actions">
              <button className="team-picker-ghost" type="button" onClick={onAutoPick}>
                自動選擇
              </button>
              <button
                className="team-picker-confirm"
                disabled={draftPlayers.length !== 4}
                type="button"
                onClick={onConfirm}
              >
                確認
              </button>
            </div>
          </section>

          <aside className="team-picker-panel team-picker-side-panel">
            <div className="preset-team-block">
              <div className="preset-team-head">
                <strong>快速選預設隊伍</strong>
                <small>可直接點一整隊，或和左側逐一選人混用。</small>
              </div>

              <div className="preset-team-list">
                {presetTeams.map((team) => {
                  const selectedSlot = getPresetTeamSelectionSlot(draftPlayers, team)

                  return (
                    <button
                      className={
                        selectedSlot === null
                          ? 'preset-team-card'
                          : 'preset-team-card preset-team-card-active'
                      }
                      key={`preset-team-${team.id}`}
                      type="button"
                      onClick={() => onTogglePresetTeam(team)}
                    >
                      <span className="preset-team-index">{team.id}</span>
                      <div>
                        <strong>{getTeamDisplayName(team)}</strong>
                        <small>
                          {selectedSlot === null
                            ? '點擊套用這一隊'
                            : `已放入第 ${selectedSlot === 0 ? '1' : '2'} 隊`}
                        </small>
                      </div>
                    </button>
                  )
                })}
              </div>
            </div>

            <button className="team-picker-clear" type="button" onClick={onClear}>
              清空
            </button>

            <p className="picker-side-hint">
              第 1、2 位會自動成為上方隊伍，第 3、4 位會成為下方隊伍。
            </p>
          </aside>
        </div>
      </div>
    </div>
  )
}

type VoiceSettingsModalProps = {
  settings: VoiceSettings
  onClose: () => void
  onUpdateSettings: Dispatch<SetStateAction<VoiceSettings>>
}

function VoiceSettingsModal({
  settings,
  onClose,
  onUpdateSettings,
}: VoiceSettingsModalProps) {
  return (
    <div className="voice-settings-overlay" role="presentation" onClick={onClose}>
      <div
        aria-label="語音設定"
        aria-modal="true"
        className="voice-settings-panel"
        role="dialog"
        onClick={(event) => event.stopPropagation()}
      >
        <button
          aria-label="關閉語音設定"
          className="voice-settings-close"
          type="button"
          onClick={onClose}
        >
          ×
        </button>

        <p className="panel-kicker">語音設定</p>
        <h3>播報內容</h3>

        <label className="voice-setting-row">
          <span>播報誰得分</span>
          <input
            checked={settings.announceScore}
            type="checkbox"
            onChange={(event) =>
              onUpdateSettings((current) => ({
                ...current,
                announceScore: event.target.checked,
              }))
            }
          />
        </label>

        <label className="voice-setting-row">
          <span>播報誰發球</span>
          <input
            checked={settings.announceServer}
            type="checkbox"
            onChange={(event) =>
              onUpdateSettings((current) => ({
                ...current,
                announceServer: event.target.checked,
              }))
            }
          />
        </label>

        <label className="voice-setting-slider">
          <span>語速</span>
          <input
            max="10"
            min="0.7"
            step="0.1"
            type="range"
            value={settings.rate}
            onChange={(event) =>
              onUpdateSettings((current) => ({
                ...current,
                rate: Number(event.target.value),
              }))
            }
          />
          <strong>{settings.rate.toFixed(1)}x</strong>
        </label>
      </div>
    </div>
  )
}

type FinishDialogProps = {
  error: string
  leftScore: number
  leftTeamName: string
  matchupLabel: string
  rightScore: number
  rightTeamName: string
  uploading: boolean
  onClose: () => void
  onConfirm: () => void
  onSkip: () => void
}

function FinishDialog({
  error,
  leftScore,
  leftTeamName,
  matchupLabel,
  rightScore,
  rightTeamName,
  uploading,
  onClose,
  onConfirm,
  onSkip,
}: FinishDialogProps) {
  return (
    <div className="finish-dialog-overlay" role="presentation">
      <div aria-modal="true" className="finish-dialog" role="dialog">
        <button
          aria-label="關閉比賽結算"
          className="finish-dialog-close"
          disabled={uploading}
          type="button"
          onClick={onClose}
        >
          ×
        </button>

        <p className="panel-kicker">比賽結算</p>
        <h3>{matchupLabel}</h3>

        <div className="finish-score">
          <div>
            <strong>{leftScore}</strong>
            <span>{leftTeamName}</span>
          </div>
          <div className="finish-score-divider">:</div>
          <div>
            <strong>{rightScore}</strong>
            <span>{rightTeamName}</span>
          </div>
        </div>

        <p className="finish-dialog-copy">要不要把這場比賽戰績上傳到資料庫？</p>

        {error ? <p className="finish-dialog-error">{error}</p> : null}

        <div className="finish-dialog-actions">
          <button
            className="team-picker-ghost"
            disabled={uploading}
            type="button"
            onClick={onSkip}
          >
            不上傳
          </button>
          <button
            className="team-picker-confirm"
            disabled={uploading}
            type="button"
            onClick={onConfirm}
          >
            {uploading ? '上傳中...' : '確認上傳'}
          </button>
        </div>
      </div>
    </div>
  )
}

function getPlayerNumber(teamSlot: 'top' | 'bottom', slot: PlayerSlot) {
  if (teamSlot === 'top') {
    return slot === 'playerA' ? 1 : 2
  }

  return slot === 'playerA' ? 4 : 3
}

function sanitizeTargetScore(value: string) {
  const numeric = Number.parseInt(value.replace(/[^\d]/g, ''), 10)

  if (Number.isNaN(numeric)) {
    return 21
  }

  return Math.min(99, Math.max(1, numeric))
}

function removePresetTeamFromDraft(players: string[], team: GroupTeam) {
  const firstPairSelected = players[0] === team.playerA && players[1] === team.playerB
  const secondPairSelected = players[2] === team.playerA && players[3] === team.playerB

  if (firstPairSelected) {
    return players.slice(2)
  }

  if (secondPairSelected) {
    return players.slice(0, 2)
  }

  return players
}

function getPresetTeamSelectionSlot(players: string[], team: GroupTeam) {
  if (players[0] === team.playerA && players[1] === team.playerB) {
    return 0
  }

  if (players[2] === team.playerA && players[3] === team.playerB) {
    return 1
  }

  return null
}

function formatClock() {
  return new Date().toLocaleTimeString('zh-TW', {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  })
}

function loadVoiceSettings(): VoiceSettings {
  try {
    const raw = window.localStorage.getItem(VOICE_SETTINGS_STORAGE_KEY)

    if (!raw) {
      return defaultVoiceSettings
    }

    const parsed = JSON.parse(raw) as Partial<VoiceSettings>

    return {
      announceScore: parsed.announceScore ?? defaultVoiceSettings.announceScore,
      announceServer: parsed.announceServer ?? defaultVoiceSettings.announceServer,
      rate:
        typeof parsed.rate === 'number'
          ? Math.min(10, Math.max(0.7, parsed.rate))
          : defaultVoiceSettings.rate,
    }
  } catch {
    return defaultVoiceSettings
  }
}

function getAnnouncementName(team: GroupTeam | null) {
  return team?.playerA ?? '本隊'
}

function speakAnnouncement(message: string, rate: number) {
  if (!('speechSynthesis' in window)) {
    return
  }

  const synthesis = window.speechSynthesis
  const utterance = new SpeechSynthesisUtterance(message)
  const voices = synthesis.getVoices()
  const zhVoice =
    voices.find((voice) => voice.lang.toLowerCase().startsWith('zh-tw')) ??
    voices.find((voice) => voice.lang.toLowerCase().startsWith('zh'))

  utterance.lang = zhVoice?.lang ?? 'zh-TW'
  utterance.rate = rate
  utterance.pitch = 1
  utterance.volume = 1

  if (zhVoice) {
    utterance.voice = zhVoice
  }

  synthesis.cancel()
  synthesis.speak(utterance)
}
