// Writing statistics computed from the document's plain text. These power the
// expanded stats panel that opens when the writer clicks the word count. The
// reading-level formulas are English-centric (Flesch); a document mixing in
// Mandarin still gets sensible counts, with the level treated as approximate.

export interface WritingStats {
  words: number
  characters: number
  charactersNoSpaces: number
  sentences: number
  paragraphs: number
  pages: number
  avgWordLength: number // letters per English word
  uniqueWords: number
  variety: number // type-token ratio, 0–1 (unique ÷ total English words)
  readingTimeMin: number
  gradeLevel: number // Flesch–Kincaid grade
}

// English word tokens (letters with internal apostrophes/hyphens) — used for the
// letter-length, syllable, and variety measures, which only make sense for
// alphabetic words.
const ENGLISH_WORD_RE = /[A-Za-z]+(?:['’-][A-Za-z]+)*/g
// Sentence terminators, including the CJK fullwidth forms.
const SENTENCE_RE = /[.!?。！？]+/g
// Words per page (a rough double-spaced manuscript page) and reading speed.
const WORDS_PER_PAGE = 250
const WORDS_PER_MINUTE = 200

// countSyllables is the common vowel-group heuristic: count vowel runs, drop a
// trailing silent "e"/"es"/"ed", and floor at one. Not perfect, but plenty
// accurate for an at-a-glance reading level.
function countSyllables(word: string): number {
  const w = word.toLowerCase().replace(/[^a-z]/g, '')
  if (w.length === 0) return 0
  if (w.length <= 3) return 1
  const trimmed = w.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '').replace(/^y/, '')
  const groups = trimmed.match(/[aeiouy]{1,2}/g)
  return groups ? groups.length : 1
}

export function computeStats(text: string, wordCount: number): WritingStats {
  const trimmed = text.trim()
  const characters = [...text].length
  const charactersNoSpaces = text.replace(/\s/g, '').length

  const sentenceMatches = trimmed.match(SENTENCE_RE)
  const sentences = sentenceMatches ? sentenceMatches.length : trimmed ? 1 : 0

  const paragraphBlocks = trimmed.split(/\n{2,}/).filter((p) => p.trim().length > 0)
  const paragraphs = paragraphBlocks.length

  const englishWords = trimmed.match(ENGLISH_WORD_RE) ?? []
  const letters = englishWords.reduce((sum, w) => sum + w.length, 0)
  const avgWordLength = englishWords.length > 0 ? letters / englishWords.length : 0

  const unique = new Set(englishWords.map((w) => w.toLowerCase()))
  const uniqueWords = unique.size
  const variety = englishWords.length > 0 ? uniqueWords / englishWords.length : 0

  const syllables = englishWords.reduce((sum, w) => sum + countSyllables(w), 0)
  // Flesch–Kincaid grade level; needs at least one sentence and word to be real.
  let gradeLevel = 0
  if (englishWords.length > 0 && sentences > 0) {
    gradeLevel =
      0.39 * (englishWords.length / sentences) + 11.8 * (syllables / englishWords.length) - 15.59
    if (gradeLevel < 0) gradeLevel = 0
  }

  return {
    words: wordCount,
    characters,
    charactersNoSpaces,
    sentences,
    paragraphs,
    pages: wordCount / WORDS_PER_PAGE,
    avgWordLength,
    uniqueWords,
    variety,
    readingTimeMin: wordCount / WORDS_PER_MINUTE,
    gradeLevel,
  }
}

// gradeBand turns a Flesch–Kincaid grade into a friendly bilingual descriptor —
// far more useful to an ESL writer than a bare number.
export function gradeBand(grade: number): { zh: string; en: string } {
  if (grade <= 5) return { zh: '简单', en: 'Easy' }
  if (grade <= 8) return { zh: '标准', en: 'Standard' }
  if (grade <= 12) return { zh: '偏难', en: 'Fairly hard' }
  return { zh: '较难', en: 'Advanced' }
}
