<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import RoleCard from './components/RoleCard.vue'
import RevealModal from './components/RevealModal.vue'
import NewGameModal from './components/NewGameModal.vue'
import { roleImageUrl, backImageUrl } from './logic/roles'
import {
  assignRolesWithPreferences,
  rolesListToDict,
  type Preferences,
} from './logic/assignment'
import {
  loadState,
  saveState,
  addHistory,
  type GameState,
  type HistoryEntry,
} from './logic/storage'

const game = ref<GameState | null>(null)
const history = ref<HistoryEntry[]>([])
const showModal = ref(false)
const revealedSet = ref<Set<number>>(new Set())
const viewing = ref<number | null>(null)
const flipped = ref<Record<number, boolean>>({})

onMounted(() => {
  const s = loadState()
  game.value = s.game
  history.value = s.history
})

watch(
  [game, history],
  () => {
    saveState({ game: game.value, history: history.value })
  },
  { deep: true },
)

const pendingPlayers = computed<number[]>(() => game.value?.pendingConfirm ?? [])
const confirmedPlayers = computed<number[]>(() => {
  if (!game.value) return []
  const all = game.value.playerRoles.length
  const pending = new Set(game.value.pendingConfirm)
  const out: number[] = []
  for (let i = 1; i <= all; i++) if (!pending.has(i)) out.push(i)
  return out
})
const allRevealed = computed(
  () => game.value != null && game.value.playerRoles.length > 0 && game.value.pendingConfirm.length === 0,
)
const allFlipped = computed(() => {
  const roles = game.value?.playerRoles ?? []
  if (roles.length === 0) return false
  return roles.every((_, i) => flipped.value[i + 1] === true)
})

const currentRole = computed<string | null>(() => {
  if (viewing.value == null) return null
  return game.value?.playerRoles[viewing.value - 1] ?? null
})

function isDead(pid: number): boolean {
  return !!game.value?.deadPlayers.includes(pid)
}
function isRevealed(pid: number): boolean {
  return revealedSet.value.has(pid)
}
function onReveal(pid: number) {
  if (isRevealed(pid)) return
  revealedSet.value.add(pid)
  viewing.value = pid
}
function onConfirm(pid: number) {
  if (!game.value) return
  if (!isRevealed(pid)) return
  // remove from pendingConfirm + revealed set
  const next = game.value.pendingConfirm.filter((p) => p !== pid)
  game.value = { ...game.value, pendingConfirm: next }
  revealedSet.value.delete(pid)
  viewing.value = null
}
function onModalConfirm() {
  if (viewing.value != null) onConfirm(viewing.value)
}
function toggleFlip(pid: number) {
  flipped.value = { ...flipped.value, [pid]: !flipped.value[pid] }
}
function toggleAllFlip() {
  const shouldFlip = !allFlipped.value
  const next: Record<number, boolean> = {}
  game.value?.playerRoles.forEach((_, i) => {
    next[i + 1] = shouldFlip
  })
  flipped.value = next
}
function toggleDead(pid: number) {
  if (!game.value) return
  const cur = game.value.deadPlayers
  const next = cur.includes(pid) ? cur.filter((p) => p !== pid) : [...cur, pid]
  game.value = { ...game.value, deadPlayers: next }
}

function startGame(payload: { rolesList: string[]; preferences: Preferences }) {
  const assigned = assignRolesWithPreferences(payload.rolesList, payload.preferences)
  const n = assigned.length
  game.value = {
    playerRoles: assigned,
    pendingConfirm: Array.from({ length: n }, (_, i) => i + 1),
    deadPlayers: [],
    startedAt: Date.now(),
  }
  history.value = addHistory(history.value, {
    playerCount: n,
    roles: rolesListToDict(assigned),
  })
  revealedSet.value = new Set()
  flipped.value = {}
  viewing.value = null
  showModal.value = false
}

function endGame() {
  if (!confirm('确定结束当前游戏？')) return
  game.value = null
  revealedSet.value = new Set()
  flipped.value = {}
  viewing.value = null
}

const lastPlayerCount = computed(() => game.value?.playerRoles.length)
</script>

<template>
  <main>
    <header class="topbar">
      <h1>🐺 狼人杀</h1>
      <div class="actions">
        <button v-if="game" class="ghost" @click="endGame">结束游戏</button>
        <button class="primary" @click="showModal = true">{{ game ? '新一局' : '开始游戏' }}</button>
      </div>
    </header>

    <section v-if="!game" class="empty">
      <p>点击"开始游戏"配置角色 → 一台手机轮流传，每人 swipe 查看自己的角色</p>
    </section>

    <section v-else-if="!allRevealed" class="board">
      <div v-if="pendingPlayers.length" class="group">
        <h3>待翻阅</h3>
        <div class="cards">
          <RoleCard
            v-for="pid in pendingPlayers"
            :key="pid"
            :player-id="pid"
            :is-dead="isDead(pid)"
            :is-revealed="isRevealed(pid)"
            :confirmed="false"
            @reveal="onReveal"
            @confirm="onConfirm"
          />
        </div>
      </div>

      <div v-if="confirmedPlayers.length" class="group">
        <h3>已翻阅</h3>
        <div class="cards">
          <RoleCard
            v-for="pid in confirmedPlayers"
            :key="pid"
            :player-id="pid"
            :is-dead="isDead(pid)"
            :is-revealed="true"
            :confirmed="true"
            @reveal="onReveal"
            @confirm="onConfirm"
          />
        </div>
      </div>
    </section>

    <section v-else class="board final">
      <div class="controls">
        <button class="primary" @click="toggleAllFlip">{{ allFlipped ? '全部翻回' : '全部翻开' }}</button>
      </div>
      <div class="cards final-cards">
        <div
          v-for="(role, idx) in game.playerRoles"
          :key="idx + 1"
          :class="['flip-card', { dead: isDead(idx + 1), flipped: flipped[idx + 1] }]"
        >
          <div class="flip-inner" @click="toggleFlip(idx + 1)">
            <div class="flip-side front">
              <img :src="backImageUrl()" alt="back" />
              <div class="label">玩家 {{ idx + 1 }}</div>
            </div>
            <div class="flip-side back">
              <img :src="roleImageUrl(role)" :alt="role" />
              <div class="label">玩家 {{ idx + 1 }} · {{ role }}</div>
            </div>
          </div>
          <button class="dead-btn" :class="{ alive: isDead(idx + 1) }" @click.stop="toggleDead(idx + 1)">
            {{ isDead(idx + 1) ? '标记存活' : '标记死亡' }}
          </button>
        </div>
      </div>
    </section>

    <RevealModal :role="currentRole" @confirm="onModalConfirm" />

    <NewGameModal
      :show="showModal"
      :history="history"
      :initial-player-count="lastPlayerCount"
      @close="showModal = false"
      @start="startGame"
    />
  </main>
</template>

<style scoped>
main {
  max-width: 960px;
  margin: 0 auto;
  padding: 16px 16px 40px;
}
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}
h1 {
  font-size: 1.6rem;
  margin: 0;
  background: linear-gradient(135deg, #fff, var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.actions { display: flex; gap: 8px; }
button.primary {
  background: var(--accent);
  border: none;
  color: white;
  padding: 10px 16px;
  border-radius: 8px;
  font-weight: bold;
}
button.ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  padding: 10px 14px;
  border-radius: 8px;
}
.empty {
  text-align: center;
  padding: 60px 20px;
  color: var(--fg-dim);
  background: var(--bg-soft);
  border-radius: 12px;
}
.group { margin-bottom: 28px; }
.group h3 {
  font-size: 1.1rem;
  margin: 0 0 12px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--border);
}
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
.controls {
  display: flex;
  justify-content: center;
  margin-bottom: 16px;
}
.final-cards {
  grid-template-columns: repeat(3, 1fr);
}

/* 3D flip */
.flip-card {
  perspective: 1000px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.flip-card.dead .flip-side img { filter: grayscale(100%); }
.flip-inner {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  transform-style: preserve-3d;
  cursor: pointer;
}
.flip-side {
  position: absolute;
  inset: 0;
  border-radius: 8px;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transition: transform 0.6s;
}
.flip-side img { width: 100%; height: 100%; object-fit: cover; display: block; }
.flip-side .label {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.7);
  text-align: center;
  padding: 6px 4px;
  font-weight: bold;
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.flip-side.front { transform: rotateY(0deg); }
.flip-side.back  { transform: rotateY(180deg); }
.flip-card.flipped .flip-side.front { transform: rotateY(-180deg); }
.flip-card.flipped .flip-side.back  { transform: rotateY(0deg); }

.dead-btn {
  padding: 6px 0;
  border-radius: 6px;
  border: none;
  background: rgba(239, 68, 68, 0.25);
  color: white;
  font-size: 0.8rem;
}
.dead-btn.alive { background: rgba(76, 175, 80, 0.3); }

@media (max-width: 540px) {
  .final-cards { grid-template-columns: repeat(2, 1fr); }
}
</style>
