import { onBeforeUnmount, onMounted } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
import type { QuizQuestion, QuizTrack } from '../api/request'
import { getToken } from '../utils/auth'

export type QuizMode = 'daily' | 'spell'

export function quizSessionKey(mode: QuizMode, track: QuizTrack = 'accumulation') {
  return `${mode}_${track}` as `${QuizMode}_${QuizTrack}`
}

export interface QuizSessionSnapshot {
  questions: QuizQuestion[]
  currentIndex: number
  sessionCorrect: number
  sessionWrong: number
  finished: boolean
  selected?: string
  showResult?: boolean
  isCorrect?: boolean
  submittedAnswer?: string
}

interface StoredSession extends QuizSessionSnapshot {
  date: string
  updatedAt: number
}

export interface PendingAnswer {
  word_id: number
  question_type: string
  user_answer: string
  correct_answer: string
  duration_seconds?: number
}

function sessionKey(mode: QuizMode, track: QuizTrack = 'accumulation') {
  return `wordloop_quiz_session_${mode}_${track}`
}

function pendingKey(mode: QuizMode, track: QuizTrack = 'accumulation') {
  return `wordloop_quiz_pending_${mode}_${track}`
}

function todayStr() {
  return new Date().toISOString().slice(0, 10)
}

function readJson<T>(key: string): T | null {
  try {
    const raw = localStorage.getItem(key)
    if (!raw) return null
    return JSON.parse(raw) as T
  } catch {
    return null
  }
}

function writeJson(key: string, value: unknown) {
  try {
    localStorage.setItem(key, JSON.stringify(value))
  } catch {
    /* quota or private mode */
  }
}

export function saveQuizSession(
  mode: QuizMode,
  snapshot: QuizSessionSnapshot,
  track: QuizTrack = 'accumulation'
) {
  if (!snapshot.questions.length) return
  const stored: StoredSession = {
    ...snapshot,
    date: todayStr(),
    updatedAt: Date.now(),
  }
  writeJson(sessionKey(mode, track), stored)
}

export function loadQuizSession(
  mode: QuizMode,
  track: QuizTrack = 'accumulation'
): StoredSession | null {
  const stored = readJson<StoredSession>(sessionKey(mode, track))
  if (!stored?.questions?.length) return null
  if (stored.date !== todayStr()) {
    clearQuizSession(mode, track)
    return null
  }
  return stored
}

export function clearQuizSession(mode: QuizMode, track: QuizTrack = 'accumulation') {
  localStorage.removeItem(sessionKey(mode, track))
  localStorage.removeItem(pendingKey(mode, track))
}

function getPendingList(mode: QuizMode, track: QuizTrack = 'accumulation'): PendingAnswer[] {
  return readJson<PendingAnswer[]>(pendingKey(mode, track)) ?? []
}

function setPendingList(mode: QuizMode, list: PendingAnswer[], track: QuizTrack = 'accumulation') {
  if (list.length === 0) {
    localStorage.removeItem(pendingKey(mode, track))
  } else {
    writeJson(pendingKey(mode, track), list)
  }
}

export function enqueuePendingAnswer(
  mode: QuizMode,
  payload: PendingAnswer,
  track: QuizTrack = 'accumulation'
) {
  const list = getPendingList(mode, track)
  const exists = list.some(
    (p) => p.word_id === payload.word_id && p.question_type === payload.question_type
  )
  if (!exists) {
    list.push(payload)
    setPendingList(mode, list, track)
  }
}

function postAnswerKeepalive(payload: PendingAnswer) {
  const token = getToken()
  if (!token) return
  fetch('/api/quiz/answer', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify(payload),
    keepalive: true,
  }).catch(() => {})
}

export async function flushPendingAnswers(
  mode: QuizMode,
  track: QuizTrack = 'accumulation'
): Promise<void> {
  const list = getPendingList(mode, track)
  if (!list.length) return

  const remaining: PendingAnswer[] = []
  for (const item of list) {
    try {
      const token = getToken()
      const res = await fetch('/api/quiz/answer', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { Authorization: `Bearer ${token}` } : {}),
        },
        body: JSON.stringify(item),
      })
      if (!res.ok) remaining.push(item)
    } catch {
      remaining.push(item)
    }
  }
  setPendingList(mode, remaining, track)
}

export function flushPendingAnswersKeepalive(
  mode: QuizMode,
  track: QuizTrack = 'accumulation'
) {
  for (const item of getPendingList(mode, track)) {
    postAnswerKeepalive(item)
  }
  localStorage.removeItem(pendingKey(mode, track))
}

export async function submitQuizAnswer(
  mode: QuizMode,
  payload: PendingAnswer,
  track: QuizTrack = 'accumulation'
): Promise<{ ok: boolean; is_correct?: boolean; correct_answer?: string }> {
  const token = getToken()
  try {
    const res = await fetch('/api/quiz/answer', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...(token ? { Authorization: `Bearer ${token}` } : {}),
      },
      body: JSON.stringify(payload),
    })
    if (!res.ok) throw new Error('submit failed')
    const data = await res.json()
    return { ok: true, is_correct: data.is_correct, correct_answer: data.correct_answer }
  } catch {
    enqueuePendingAnswer(mode, payload, track)
    return { ok: false }
  }
}

export interface QuizAutoSaveOptions {
  /** 拼写题：退出时若有未提交输入则自动提交 */
  getDraftAnswer?: () => string
  onDraftSubmit?: (answer: string) => void | Promise<void>
}

export function useQuizAutoSave(
  mode: QuizMode,
  getSnapshot: () => QuizSessionSnapshot | null,
  options: QuizAutoSaveOptions = {},
  track: QuizTrack = 'accumulation'
) {
  const persist = () => {
    const snap = getSnapshot()
    if (!snap) return
    if (snap.finished) {
      clearQuizSession(mode, track)
      return
    }
    saveQuizSession(mode, snap, track)
  }

  const flushDraft = async () => {
    const draft = options.getDraftAnswer?.()?.trim()
    if (!draft || !options.onDraftSubmit) return
    await options.onDraftSubmit(draft)
  }

  const onInterrupt = async () => {
    await flushDraft()
    persist()
    await flushPendingAnswers(mode, track)
  }

  const onInterruptSync = () => {
    const draft = options.getDraftAnswer?.()?.trim()
    if (draft && options.onDraftSubmit) {
      void options.onDraftSubmit(draft)
    }
    persist()
    flushPendingAnswersKeepalive(mode, track)
  }

  onMounted(() => {
    const handleVisibility = () => {
      if (document.visibilityState === 'hidden') void onInterrupt()
    }
    const handlePageHide = () => onInterruptSync()
    const handleBeforeUnload = () => onInterruptSync()

    document.addEventListener('visibilitychange', handleVisibility)
    window.addEventListener('pagehide', handlePageHide)
    window.addEventListener('beforeunload', handleBeforeUnload)

    onBeforeUnmount(() => {
      document.removeEventListener('visibilitychange', handleVisibility)
      window.removeEventListener('pagehide', handlePageHide)
      window.removeEventListener('beforeunload', handleBeforeUnload)
      void onInterrupt()
    })
  })

  onBeforeRouteLeave(async () => {
    await onInterrupt()
  })

  return { persist, flushPending: () => flushPendingAnswers(mode, track) }
}
