<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
import GamePlayer from '@/components/GamePlayer.vue'
import ChoicePanel from '@/components/ChoicePanel.vue'
import QTEOverlay from '@/components/QTEOverlay.vue'
import Subtitles from '@/components/Subtitles.vue'
import HotspotLayer from '@/components/HotspotLayer.vue'
import SaveLoadMenu from '@/components/SaveLoadMenu.vue'
import PlaybackBar from '@/components/PlaybackBar.vue'
import MainMenu from '@/components/MainMenu.vue'
import PauseMenu from '@/components/PauseMenu.vue'
import AchievementToast from '@/components/AchievementToast.vue'
import AchievementPanel from '@/components/AchievementPanel.vue'
import AccessibilitySettings from '@/components/AccessibilitySettings.vue'
import StoryGallery from '@/components/StoryGallery.vue'
import BattleHUD from '@/components/BattleHUD.vue'
import BattleResult from '@/components/BattleResult.vue'
import { useGameEngine } from '@/composables/useGameEngine'
import { useGameStore } from '@/stores/gameStore'
import { useFullscreen } from '@/composables/useFullscreen'
import { useI18n, initStoryLocales } from '@/composables/useI18n'
import { getVideoMode } from '@engine/core/VideoManager'

const isLocalMode = getVideoMode() === 'local'

const store = useGameStore()
const { t, currentLang } = useI18n()
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen()
const videoElA = ref<HTMLVideoElement | null>(null)
const videoElB = ref<HTMLVideoElement | null>(null)
const loading = ref(true)
const started = ref(false)
const showMenu = ref(false)
const showPauseMenu = ref(false)
const showStoryGallery = ref(false)
const showAchievements = ref(false)
const hasAutoSave = ref(false)
const currentSpeed = ref(1)
const canSkip = ref(false)
const paused = ref(false)
const promptToast = ref('')
const showPromptToast = ref(false)
const showIntro = ref(false)
const introWatched = ref(false)
const introStarted = ref(false)
const introVideoRef = ref<HTMLVideoElement | null>(null)
const menuVideoRef = ref<HTMLVideoElement | null>(null)
const showTopBar = ref(true)
let hideTopBarTimer: ReturnType<typeof setTimeout> | null = null

const { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter,
  skipScene, setSpeed, getSpeed, isSceneWatched, startAtScene, jumpToScene,
  saveGame, loadGameFromSlot, refreshSaves, saveSystem, engine } =
  useGameEngine(() => [videoElA.value, videoElB.value])

async function resolveScenePath(): Promise<string> {
  const params = new URLSearchParams(location.search)
  const queryScene = params.get('scene')
  if (queryScene) return queryScene.startsWith('/') ? queryScene : '/' + queryScene

  try {
    const resp = await fetch('/scenes/config.json')
    if (resp.ok) {
      const config = await resp.json()
      if (config.sceneFile) return '/scenes/' + config.sceneFile
    }
  } catch { /* config.json optional */ }

  return '/scenes/main.json'
}

async function init() {
  const scenePath = await resolveScenePath()
  await loadGame(scenePath)
  const lc = store.storyLocales
  if (lc.path) {
    await initStoryLocales(lc.path)
  }
  loading.value = false
  hasAutoSave.value = (await saveSystem.load(0)) !== null

  if (store.introVideo) {
    introWatched.value = await isSceneWatched('__intro__')
    const params = new URLSearchParams(location.search)
    if (params.get('startScene')) {
      showIntro.value = false
      await nextTick()
      handleStartFromScene(params.get('startScene')!)
      return
    }
    showIntro.value = true
  } else {
    const params = new URLSearchParams(location.search)
    const startSceneId = params.get('startScene')
    if (startSceneId) {
      await nextTick()
      handleStartFromScene(startSceneId)
    }
  }
}

function handleStartFromScene(sceneId: string) {
  started.value = true
  applyQteParams()
  const ch = engine.sceneManager.chapters.find(c => {
    const reachable = new Set<string>()
    const queue = [c.startScene]
    while (queue.length > 0) {
      const id = queue.shift()!
      if (reachable.has(id)) continue
      const sc = engine.sceneManager.getScene(id)
      if (!sc) continue
      reachable.add(id)
      if (sc.choices) for (const ch2 of sc.choices) if (ch2.targetScene) queue.push(ch2.targetScene)
      if (sc.nextScene) {
        if (Array.isArray(sc.nextScene)) for (const r of sc.nextScene) if (r.targetScene) queue.push(r.targetScene)
        else queue.push(sc.nextScene as unknown as string)
      }
      if (sc.qte) { if (sc.qte.successScene) queue.push(sc.qte.successScene); if (sc.qte.failScene) queue.push(sc.qte.failScene) }
      if (sc.hotspots) for (const h of sc.hotspots) if (h.targetScene) queue.push(h.targetScene)
    }
    return reachable.has(sceneId)
  })
  if (ch) {
    startAtScene(ch.id, sceneId)
  } else {
    jumpToScene(sceneId)
  }
}

function onIntroEnded() {
  saveSystem.markWatched('__intro__')
  showIntro.value = false
}

function onIntroClick() {
  if (!introStarted.value) {
    introStarted.value = true
    introVideoRef.value?.play().catch(() => {})
  } else if (introWatched.value) {
    skipIntro()
  }
}

function skipIntro() {
  saveSystem.markWatched('__intro__')
  showIntro.value = false
}

function handleStart() {
  started.value = true
  store.setGameEnded(false)
  applyQteParams()
  start()
}

async function handleResume() {
  started.value = true
  applyQteParams()
  await resumeAutoSave()
}

function applyQteParams() {
  engine.qteSystem.timeLimitMultiplier = store.qteTimeRelax ? 1.5 : 1
  engine.qteSystem.singleKeyMode = store.qteSingleKey
}

watch([() => store.qteTimeRelax, () => store.qteSingleKey], () => {
  applyQteParams()
})

watch(() => store.preferredQuality, (q) => {
  engine.videoManager.streamingQuality = q
  const scene = store.currentScene
  if (scene?.streamingUrl && scene.streamingUrl[q]) {
    const active = engine.videoManager.getActiveVideoElement()
    if (active && !active.ended) {
      engine.videoManager.switchQuality(
        engine.videoManager.resolveVideoUrl(scene, q),
        engine.videoManager.getCurrentTime()
      )
    }
  }
}, { immediate: true })

watch(
  () => [
    showPauseMenu.value, showMenu.value,
    showAchievements.value, showStoryGallery.value,
    paused.value, store.showSettings,
  ],
  () => { resetTopBarTimer() },
)

function onVideoReady(elA: HTMLVideoElement, elB: HTMLVideoElement) {
  videoElA.value = elA
  videoElB.value = elB
}

function onChoose(index: number) {
  makeChoice(index)
}

function onPrompt(text: string) {
  promptToast.value = text
  showPromptToast.value = true
  setTimeout(() => { showPromptToast.value = false }, 2500)
}

function toggleMenu() {
  showMenu.value = !showMenu.value
  if (showMenu.value) {
    refreshSaves()
  }
}

async function onSave(slot: number) {
  await saveGame(slot)
}

async function onLoad(slot: number) {
  await loadGameFromSlot(slot)
  showMenu.value = false
}

async function onStartChapter(chapterId: string) {
  started.value = true
  applyQteParams()
  startChapter(chapterId)
}

function handleSkip() {
  skipScene()
}

function handleSpeedChange(rate: number) {
  setSpeed(rate)
  currentSpeed.value = rate
}

function onBattleResultContinue() {
  store.setShowBattleResult(false)
  const scene = store.currentScene
  if (!scene) return
  if (scene.nextScene) {
    if (Array.isArray(scene.nextScene)) {
      for (const route of scene.nextScene) {
        if (!route.conditions || engine.stateManager.evaluate(route.conditions)) {
          const next = engine.sceneManager.getScene(route.targetScene)
          if (next) { engine.goToScene(next) }
          return
        }
      }
    } else {
      const next = engine.sceneManager.getScene(scene.nextScene)
      if (next) { engine.goToScene(next) }
      return
    }
  }
  if (scene.choices && scene.choices.length > 0) {
    const first = scene.choices[0]
    const next = engine.sceneManager.getScene(first.targetScene)
    if (next) { engine.goToScene(next) }
  }
}

watch(() => store.currentScene?.id, async (newId) => {
  if (!newId) { canSkip.value = false; return }
  const scene = store.currentScene
  if (scene?.skippable === false) { canSkip.value = false; return }
  canSkip.value = await isSceneWatched(newId)
})

function onGlobalKeydown(e: KeyboardEvent) {
  const key = e.key

  if (key === 'p' && !store.qteActive && store.pauseEnabled && started.value && !store.gameEnded) {
    const activeEl = document.activeElement
    if (!activeEl || activeEl.tagName === 'BODY' || activeEl === document.body) {
      e.preventDefault()
      togglePause()
      return
    }
  }

  if (key === 'ArrowDown' || key === 'ArrowUp' || key === 'ArrowLeft' || key === 'ArrowRight' ||
      key === 'Enter' || key === ' ' || key === 'Tab' || key === 'w' || key === 'a' || key === 's' || key === 'd') {
    store.setInputMode('keyboard')
  }
  if (key === 'Escape') {
    if (store.showSettings) {
      store.showSettings = false
    } else if (showStoryGallery.value) {
      showStoryGallery.value = false
    } else if (showMenu.value) {
      showMenu.value = false
    } else if (showPauseMenu.value) {
      showPauseMenu.value = false
    } else if (showAchievements.value) {
      showAchievements.value = false
    } else if (started.value && !store.gameEnded) {
      showPauseMenu.value = true
    }
  }
}

function togglePause() {
  if (!started.value || store.gameEnded) return
  if (paused.value) {
    paused.value = false
    engine.videoManager.getActiveVideoElement()?.play().catch(() => {})
  } else {
    paused.value = true
    engine.videoManager.getActiveVideoElement()?.pause()
  }
}

function onPauseResume() {
  showPauseMenu.value = false
}

function onPauseSaveLoad() {
  showPauseMenu.value = false
  showMenu.value = true
  refreshSaves()
}

function onPauseSettings() {
  showPauseMenu.value = false
  store.setShowSettings(true)
}

function onQuitToMenu() {
  showPauseMenu.value = false
  store.setGameEnded(true)
  engine.qteSystem.cancel()
  engine.audioSystem.stop(2.0)
}

function onGlobalMouseMove() {
  store.setInputMode('mouse')
  resetTopBarTimer()
}

function anyOverlayOpen(): boolean {
  return showPauseMenu.value || showMenu.value || showStoryGallery.value
    || showAchievements.value || paused.value || store.showSettings
}

function resetTopBarTimer() {
  showTopBar.value = true
  if (hideTopBarTimer) clearTimeout(hideTopBarTimer)
  if (!anyOverlayOpen()) {
    hideTopBarTimer = setTimeout(() => { showTopBar.value = false }, 3000)
  }
}

onMounted(() => {
  document.addEventListener('keydown', onGlobalKeydown)
  document.addEventListener('mousemove', onGlobalMouseMove)
})

onUnmounted(() => {
  document.removeEventListener('keydown', onGlobalKeydown)
  document.removeEventListener('mousemove', onGlobalMouseMove)
  if (hideTopBarTimer) clearTimeout(hideTopBarTimer)
})

init()
</script>

<template>
  <div class="app-container">
    <div v-if="loading" class="loading">{{ t('ui.loading') }}</div>
    <template v-else>
      <div v-if="showIntro" class="intro-overlay" @click="onIntroClick">
        <video ref="introVideoRef" :src="store.introVideo" class="intro-video" @ended="onIntroEnded"></video>
        <div v-if="!introStarted" class="intro-start-prompt">点击开始</div>
        <button v-if="introWatched && introStarted" class="intro-skip-btn" @click.stop="skipIntro">{{ t('ui.skip') }}</button>
      </div>

      <div v-if="store.menuVideo && (!started || store.gameEnded)" class="menu-bg">
        <video ref="menuVideoRef" :src="store.menuVideo" class="menu-bg-video" autoplay loop muted></video>
      </div>

      <div class="game-screen" :class="{ idle: !showTopBar }" v-show="started && !store.gameEnded">
        <GamePlayer v-show="!store.isImageScene" @video-ready="onVideoReady" />
        <HotspotLayer
          :hotspots="store.hotspots"
          :is-image-scene="store.isImageScene"
          :image-url="store.currentScene?.imageUrl"
          :content-size="store.currentScene?.contentSize ?? null"
          @click-hotspot="clickHotspot"
        />
        <Subtitles
          :current-time="store.videoTime"
          :subtitle-url="store.currentScene?.subtitleUrl ?? null"
          :subtitles="store.currentScene?.subtitles ?? null"
          :lang="currentLang"
        />
        <QTEOverlay
          :visible="store.qteActive"
          :prompt="store.qteDef ? t(store.qteDef.promptKey || store.qteDef.prompt) : ''"
          :keys="store.qteDef?.keys ?? []"
          :total="store.qteTotal"
          :remaining="store.qteRemaining"
          :result="store.qteResult"
        />
        <ChoicePanel
          v-if="!store.qteActive"
          :choices="store.choices"
          :timer-total="store.timerTotal"
          :timer-remaining="store.timerRemaining"
          @choose="onChoose"
          @prompt="onPrompt"
        />
        <Transition name="prompt-toast">
          <div v-if="showPromptToast" class="prompt-toast">{{ promptToast }}</div>
        </Transition>
        <BattleHUD
          v-if="store.currentScene?.battleHUD"
          :entries="store.currentScene!.battleHUD"
        />
        <div v-if="started && !store.gameEnded && store.choices.length === 0" class="top-bar" :class="{ hidden: !showTopBar }">
          <button class="top-btn" @click="toggleFullscreen" :title="t('ui.fullscreen')">⛶</button>
          <button class="top-btn" @click="showPauseMenu = true" :title="t('ui.menu')">≡</button>
        </div>
      </div>
      <PlaybackBar
        v-if="started && !store.gameEnded"
        :can-skip="canSkip"
        :current-speed="currentSpeed"
        :visible="showTopBar"
        :show-quality="!isLocalMode"
        :hide="store.choices.length > 0"
        @skip="handleSkip"
        @speed-change="handleSpeedChange"
      />
      <Transition name="battle-result">
        <BattleResult
          v-if="store.showBattleResult"
          :result="store.battleResultData"
          @continue="onBattleResultContinue"
        />
      </Transition>
      <MainMenu
        v-if="!started || store.gameEnded"
        :show-resume="!store.gameEnded && hasAutoSave"
        :show-story="store.chapters.length > 0"
        :show-achievements="store.achievementDefs.length > 0"
        :is-game-end="store.gameEnded"
        @start="handleStart"
        @resume="handleResume"
        @story="showStoryGallery = true"
        @achievements="showAchievements = true"
        @settings="store.setShowSettings(true)"
      />
      <PauseMenu
        v-if="showPauseMenu"
        @resume="onPauseResume"
        @save-load="onPauseSaveLoad"
        @settings="onPauseSettings"
        @quit-to-menu="onQuitToMenu"
      />
      <SaveLoadMenu
        v-if="showMenu"
        :saves="store.saves"
        @save="onSave"
        @load="onLoad"
        @close="showMenu = false"
      />
      <StoryGallery
        v-if="showStoryGallery"
        :chapters="store.chapters"
        :endings="store.endings"
        :scenes="engine.sceneManager.getScenes()"
        :visited-ids="store.visitedSceneIds"
        :unlocked-chapter-ids="store.unlockedChapterIds"
        @start-chapter="(chId: string) => { showStoryGallery = false; onStartChapter(chId) }"
        @start-at-scene="(chId: string, sceneId: string) => { showStoryGallery = false; started = true; startAtScene(chId, sceneId) }"
        @close="showStoryGallery = false"
      />
      <AchievementPanel
        v-if="showAchievements"
        :definitions="store.achievementDefs"
        :unlocked-ids="store.unlockedAchievementIds"
        @close="showAchievements = false"
      />
      <AccessibilitySettings
        v-if="store.showSettings"
        @close="store.setShowSettings(false)"
      />
      <div v-if="paused" class="pause-overlay" @click="togglePause">
        <div class="pause-text">已暂停</div>
        <div class="pause-hint">点击或按 P 继续</div>
      </div>
      <AchievementToast
        :achievement-id="store.toastAchievementId"
        :definitions="store.achievementDefs"
        @done="store.clearToastAchievement()"
      />
    </template>
  </div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  color: #fff;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

#app {
  width: 100%;
  height: 100%;
}
</style>

<style scoped>
.app-container {
  width: 100%;
  height: 100%;
}

.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 18px;
  color: #888;
}

.game-screen {
  position: relative;
  width: 100%;
  height: 100%;
}

.top-bar {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 20;
  display: flex;
  gap: 6px;
  transition: opacity 0.3s ease;
}

.top-bar.hidden {
  opacity: 0;
  pointer-events: none;
}

.game-screen.idle {
  cursor: none;
}

.top-btn {
  padding: 8px 16px;
  font-size: 13px;
  color: #aaa;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

.top-btn:hover {
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
}

.pause-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 150;
  cursor: pointer;
}

.pause-text {
  font-size: 36px;
  color: #fff;
  letter-spacing: 4px;
  margin-bottom: 12px;
}

.pause-hint {
  font-size: 14px;
  color: #888;
}

.intro-overlay {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.intro-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.intro-skip-btn {
  position: absolute;
  bottom: 40px;
  right: 40px;
  padding: 10px 24px;
  font-size: 14px;
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 2px;
  z-index: 10;
}

.intro-skip-btn:hover {
  background: rgba(0, 0, 0, 0.7);
}

.intro-start-prompt {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 18px 48px;
  font-size: 22px;
  color: #fff;
  background: rgba(0, 0, 0, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 6px;
  letter-spacing: 4px;
  cursor: pointer;
  transition: background 0.2s;
}

.intro-start-prompt:hover {
  background: rgba(0, 0, 0, 0.8);
}

.menu-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
}

.menu-bg-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.prompt-toast {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 16px 36px;
  font-size: 20px;
  color: #ffc107;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 193, 7, 0.4);
  border-radius: 6px;
  letter-spacing: 3px;
  text-align: center;
  white-space: nowrap;
  pointer-events: none;
  z-index: 50;
}

.prompt-toast-enter-active { transition: opacity 0.3s ease; }
.prompt-toast-leave-active { transition: opacity 0.8s ease; }
.prompt-toast-enter-from,
.prompt-toast-leave-to { opacity: 0; }
</style>

<style>
.battle-result-enter-active { transition: all 0.3s ease-out; }
.battle-result-leave-active { transition: all 0.2s ease-in; }
.battle-result-enter-from { opacity: 0; }
.battle-result-leave-to { opacity: 0; transform: scale(0.95); }
</style>
