import type { QTEDefinition } from '../types'

type QTEUpdateCallback = (remaining: number, total: number) => void
type QTEResultCallback = (success: boolean) => void

export class QTESystem {
  private timerId: ReturnType<typeof setInterval> | null = null
  private timeoutId: ReturnType<typeof setTimeout> | null = null
  private keyHandler: ((e: KeyboardEvent) => void) | null = null
  private tickMs = 50
  private active = false
  timeLimitMultiplier = 1
  singleKeyMode = false

  trigger(
    qte: QTEDefinition,
    onUpdate: QTEUpdateCallback,
    onResult: QTEResultCallback,
  ) {
    if (this.active) return
    this.active = true

    const startTime = Date.now()
    const adjustedLimit = qte.timeLimit * this.timeLimitMultiplier
    const total = adjustedLimit * 1000

    this.keyHandler = (e: KeyboardEvent) => {
      if (!this.active) return
      const targetKeys = this.singleKeyMode ? [' '] : qte.keys
      const matched = targetKeys.some(
        (k) => k.toLowerCase() === e.key.toLowerCase()
      )
      if (matched) {
        this.clear()
        onResult(true)
      }
    }
    document.addEventListener('keydown', this.keyHandler)

    this.timerId = setInterval(() => {
      if (!this.active) return
      const elapsed = Date.now() - startTime
      const remaining = Math.max(0, total - elapsed)
      onUpdate(remaining / 1000, adjustedLimit)
      if (remaining <= 0) {
        this.clear()
        onResult(false)
      }
    }, this.tickMs)

    this.timeoutId = setTimeout(() => {
      if (!this.active) return
      this.clear()
      onResult(false)
    }, total)
  }

  cancel() {
    this.clear()
  }

  private clear() {
    this.active = false
    if (this.timerId !== null) {
      clearInterval(this.timerId)
      this.timerId = null
    }
    if (this.timeoutId !== null) {
      clearTimeout(this.timeoutId)
      this.timeoutId = null
    }
    if (this.keyHandler !== null) {
      document.removeEventListener('keydown', this.keyHandler)
      this.keyHandler = null
    }
  }

  destroy() {
    this.clear()
  }
}
