import { onUnmounted } from 'vue'
import { Engine } from '@engine/core/Engine'
import { SaveSystem } from '@engine/systems/SaveSystem'
import type { GameData } from '@engine/types'
import { useGameStore } from '@/stores/gameStore'
import { useI18n } from '@/composables/useI18n'
import { resolveAsset } from '@engine/utils'

export function useGameEngine(videoEls: () => [HTMLVideoElement | null, HTMLVideoElement | null]) {
  const engine = new Engine()
  const saveSystem = new SaveSystem()
  const store = useGameStore()
  const { t } = useI18n()
  let lastThumbnail: string | undefined

  engine.stateManager.onAfterApply.add((vars) => {
    store.syncVariables(vars)
  })

  if (import.meta.env.DEV) {
    ;(window as any).__sm = engine.stateManager
    ;(window as any).__store = store
  }

    engine.setChapterUnlockHandler(async (chapterId) => {
      await saveSystem.unlockChapter(chapterId)
      store.addUnlockedChapter(chapterId)
    })

    engine.setMarkWatchedHandler(async (sceneId) => {
      await saveSystem.markWatched(sceneId)
    })

    engine.setMarkVisitedHandler(async (sceneId) => {
      store.addVisitedSceneId(sceneId)
      await saveSystem.markVisited(sceneId)
    })

    engine.achievementSystem.setUnlockCallback(async (ach) => {
      await saveSystem.unlockAchievement(ach.id)
      store.addUnlockedAchievement(ach.id)
    })

  engine.on('sceneChange', (scene) => {
    store.setScene(scene)
    store.clearChoices()
    store.clearTimer()
    store.clearHotspots()
    store.setIsImageScene(scene.type === 'image')
    store.syncVariables(engine.stateManager.variables)
    saveGame(0)
  })

  engine.on('choiceRequest', (choiceList) => {
    const translated = choiceList.map((c: any) => ({
      ...c,
      text: c.textKey ? t(c.textKey) : c.text,
    }))
    store.setChoices(translated)
  })

  engine.on('choiceTimer', (timerState) => {
    store.setTimer(timerState.total, timerState.remaining)
  })

  engine.on('choiceTimeout', () => {
    store.clearChoices()
    store.clearTimer()
  })

  engine.on('hotspotRequest', (list) => {
    store.setHotspots(list)
  })

  engine.on('hotspotUpdate', (list) => {
    store.setHotspots(list)
  })

  engine.on('videoEnd', () => {
    try {
      const video = engine.videoManager.getActiveVideoElement()
      if (video && video.readyState >= 2) {
        const canvas = document.createElement('canvas')
        canvas.width = 320
        canvas.height = 180
        const ctx = canvas.getContext('2d')
        if (ctx) {
          ctx.drawImage(video, 0, 0, 320, 180)
          lastThumbnail = canvas.toDataURL('image/jpeg', 0.6)
        }
      }
    } catch {
      /* ignore */
    }
  })

  engine.on('gameEnd', () => {
    store.clearQTE()
    store.setGameEnded(true)
    engine.choiceSystem.stop()
  })

  engine.on('qteTrigger', (qte) => {
    store.showQTE(qte)
  })

  engine.on('qteTimer', ({ remaining, total }) => {
    store.qteTotal = total
    store.updateQTE(remaining)
  })

  engine.on('qteResult', ({ success }) => {
    store.resolveQTE(success)
  })

  engine.on('battleResultRequest', (battleResult) => {
    store.setBattleResult(battleResult)
  })

  engine.videoManager.onTimeUpdate((t: number) => {
    store.setVideoTime(t)
  })

  function applyAssetBase(data: GameData) {
    const base = data.assetBase || ''
    if (!base) return
    for (const scene of Object.values(data.scenes)) {
      if (scene.videoUrl) scene.videoUrl = resolveAsset(base, scene.videoUrl)
      if (scene.subtitleUrl) scene.subtitleUrl = resolveAsset(base, scene.subtitleUrl)
      if (scene.imageUrl) scene.imageUrl = resolveAsset(base, scene.imageUrl)
      if (scene.bgmUrl) scene.bgmUrl = resolveAsset(base, scene.bgmUrl)
      if (scene.thumbnail) scene.thumbnail = resolveAsset(base, scene.thumbnail)
      if (scene.subtitles) {
        for (const k of Object.keys(scene.subtitles)) {
          scene.subtitles[k] = resolveAsset(base, scene.subtitles[k])
        }
      }
      if (scene.streamingUrl) {
        for (const k of Object.keys(scene.streamingUrl)) {
          scene.streamingUrl[k] = resolveAsset(base, scene.streamingUrl[k])
        }
      }
    }
    if (data.endings) {
      for (const e of data.endings) {
        if (e.thumbnail) e.thumbnail = resolveAsset(base, e.thumbnail)
      }
    }
    if (data.chapters) {
      for (const c of data.chapters) {
        if (c.thumbnail) c.thumbnail = resolveAsset(base, c.thumbnail)
      }
    }
    if (data.introVideo) data.introVideo = resolveAsset(base, data.introVideo)
    if (data.menuVideo) data.menuVideo = resolveAsset(base, data.menuVideo)
  }

  async function loadGame(dataUrl: string) {
    const resp = await fetch(dataUrl)
    const data: GameData = await resp.json()
    applyAssetBase(data)
    engine.sceneManager.load(data)
    engine.stateManager.init(data.variables)
    store.setChapters(data.chapters || [])
    store.setAchievementDefs(data.achievements || [])

    const unlocked = await saveSystem.getUnlockedChapters()
    store.setUnlockedChapters(unlocked)

    const achieved = await saveSystem.getUnlockedAchievements()
    store.setUnlockedAchievementIds(achieved)
    engine.achievementSystem.init(data.achievements || [], achieved)

    store.setEndings(data.endings || [])
    store.setStoryLocales(data.locales, data.assetBase)

    const visitedIds = await saveSystem.getVisitedSceneIds()
    store.setVisitedSceneIds(visitedIds)

    store.setIntroVideo(data.introVideo || '')
    store.setMenuVideo(data.menuVideo || '')
  }

  function ensureVideo() {
    const [elA, elB] = videoEls()
    if (elA && elB) engine.videoManager.attach(elA, elB)
  }

  function start() {
    ensureVideo()
    engine.start()
  }

  async function resumeAutoSave(): Promise<boolean> {
    ensureVideo()
    store.setGameEnded(false)
    return await loadGameFromSlot(0)
  }

  function startChapter(chapterId: string) {
    ensureVideo()
    store.setGameEnded(false)
    engine.startChapter(chapterId)
  }

  function startAtScene(chapterId: string, sceneId: string) {
    ensureVideo()
    store.setGameEnded(false)
    engine.startAtScene(chapterId, sceneId)
  }

  function jumpToScene(sceneId: string) {
    ensureVideo()
    const scene = engine.sceneManager.getScene(sceneId)
    if (scene) engine.goToScene(scene)
  }

  function skipScene() {
    engine.skipCurrentScene()
  }

  function setSpeed(rate: number) {
    engine.videoManager.setPlaybackRate(rate)
  }

  function getSpeed(): number {
    return engine.videoManager.getPlaybackRate()
  }

  async function isSceneWatched(sceneId: string): Promise<boolean> {
    return await saveSystem.isWatched(sceneId)
  }

  function makeChoice(index: number) {
    const scene = store.currentScene
    if (!scene?.choices) return
    engine.choiceSystem.stop()
    store.clearTimer()
    engine.makeChoice(scene.choices[index])
  }

  function clickHotspot(hotspotId: string) {
    const scene = store.currentScene
    if (!scene?.hotspots) return
    const hs = scene.hotspots.find((h) => h.id === hotspotId)
    if (hs) {
      engine.clickHotspot(hs)
    }
  }

  async function saveGame(slot: number) {
    const state = engine.stateManager
    const currentScene = store.currentScene
    await saveSystem.save(slot, {
      timestamp: Date.now(),
      currentScene: currentScene?.id ?? '',
      variables: state.variables,
      flags: [...state.flags],
      history: state.history,
      thumbnail: lastThumbnail,
    })
    await refreshSaves()
  }

  async function loadGameFromSlot(slot: number): Promise<boolean> {
    const data = await saveSystem.load(slot)
    if (!data) return false
    store.setGameEnded(false)
    engine.resumeScene(data.currentScene, {
      variables: data.variables,
      flags: data.flags,
      history: data.history,
    })
    return true
  }

  async function refreshSaves() {
    const list = await saveSystem.listSlots()
    store.setSaves(list)
  }

  function destroy() {
    engine.destroy()
  }

  onUnmounted(() => {
    destroy()
  })

  return {
    loadGame,
    start,
    resumeAutoSave,
    makeChoice,
    clickHotspot,
    startChapter,
    startAtScene, jumpToScene,
    skipScene,
    setSpeed,
    getSpeed,
    isSceneWatched,
    saveGame,
    loadGameFromSlot,
    refreshSaves,
    engine,
    saveSystem,
  }
}
