﻿<script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import deskImage from '../assets/images/desk/desk_01.png'
import wanIcon from '../assets/images/flowerClolor/wan.png'
import tongIcon from '../assets/images/flowerClolor/tong.png'
import tiaoIcon from '../assets/images/flowerClolor/tiao.png'
import robotIcon from '../assets/images/icons/robot.svg'
import exitIcon from '../assets/images/icons/exit.svg'
import '../assets/styles/room.css'
import topBackImage from '../assets/images/tiles/top/tbgs_2.png'
import rightBackImage from '../assets/images/tiles/right/tbgs_1.png'
import bottomBackImage from '../assets/images/tiles/bottom/tdbgs_4.png'
import leftBackImage from '../assets/images/tiles/left/tbgs_3.png'
import TopPlayerCard from '../components/game/TopPlayerCard.vue'
import RightPlayerCard from '../components/game/RightPlayerCard.vue'
import BottomPlayerCard from '../components/game/BottomPlayerCard.vue'
import LeftPlayerCard from '../components/game/LeftPlayerCard.vue'
import type {SeatPlayerCardModel} from '../components/game/seat-player-card'
import type {SeatKey} from '../game/seat'
import type {GameAction} from '../game/actions'
import {dispatchGameAction} from '../game/dispatcher'
import {readStoredAuth} from '../utils/auth-storage'
import type {WsStatus} from '../ws/client'
import {wsClient} from '../ws/client'
import {sendWsMessage} from '../ws/sender'
import {buildWsUrl} from '../ws/url'
import {useGameStore} from '../store/gameStore'
import {useActiveRoomState} from '../store'
import type {PlayerState} from '../types/state'
import type {Tile} from '../types/tile'

const gameStore = useGameStore()
const activeRoom = useActiveRoomState()
const route = useRoute()
const router = useRouter()
const auth = ref(readStoredAuth())

type DisplayPlayer = PlayerState & {
  displayName?: string
  missingSuit?: string | null
}

type GameActionPayload<TType extends GameAction['type']> = Extract<GameAction, { type: TType }>['payload']

interface SeatViewModel {
  key: SeatKey
  player?: DisplayPlayer
  isSelf: boolean
  isTurn: boolean
}

const now = ref(Date.now())
const wsStatus = ref<WsStatus>('idle')
const wsMessages = ref<string[]>([])
const wsError = ref('')
const selectedTile = ref<string | null>(null)
const leaveRoomPending = ref(false)
let clockTimer: number | null = null
let unsubscribe: (() => void) | null = null

const menuOpen = ref(false)
const isTrustMode = ref(false)
const menuTriggerActive = ref(false)
let menuTriggerTimer: number | null = null
let menuOpenTimer: number | null = null

const loggedInUserId = computed(() => {
  const rawId = auth.value?.user?.id
  if (typeof rawId === 'string') {
    return rawId
  }
  if (typeof rawId === 'number') {
    return String(rawId)
  }
  return ''
})

const loggedInUserName = computed(() => {
  return auth.value?.user?.nickname || auth.value?.user?.username || ''
})

const localCachedAvatarUrl = computed(() => {
  const source = auth.value?.user as Record<string, unknown> | undefined
  if (!source) {
    return ''
  }

  const avatarCandidates = [
    source.avatar,
    source.avatar_url,
    source.avatarUrl,
    source.head_img,
    source.headImg,
    source.profile_image,
    source.profileImage,
  ]

  for (const candidate of avatarCandidates) {
    if (typeof candidate === 'string' && candidate.trim()) {
      return candidate
    }
  }

  return ''
})

const myPlayer = computed(() => {
  return gameStore.players[loggedInUserId.value]
})

const myHandTiles = computed(() => {
  return myPlayer.value?.handTiles ?? []
})

const remainingTiles = computed(() => {
  return gameStore.remainingTiles
})

const gamePlayers = computed<DisplayPlayer[]>(() => {
  return Object.values(gameStore.players).sort((a, b) => a.seatIndex - b.seatIndex) as DisplayPlayer[]
})

const roomName = computed(() => {
  const queryRoomName = typeof route.query.roomName === 'string' ? route.query.roomName : ''
  const activeRoomName =
      activeRoom.value && activeRoom.value.roomId === gameStore.roomId
          ? activeRoom.value.roomName
          : ''
  return queryRoomName || activeRoomName || `房间 ${gameStore.roomId || '--'}`
})

const roomState = computed(() => {
  const status = gameStore.phase === 'waiting' ? 'waiting' : gameStore.phase === 'settlement' ? 'finished' : 'playing'
  const wall = Array.from({length: remainingTiles.value}, (_, index) => `wall-${index}`)
  const maxPlayers =
      activeRoom.value && activeRoom.value.roomId === gameStore.roomId
          ? activeRoom.value.maxPlayers
          : 4

  return {
    roomId: gameStore.roomId,
    name: roomName.value,
    playerCount: gamePlayers.value.length,
    maxPlayers,
    status,
    game: {
      state: {
        wall,
        dealerIndex: gameStore.dealerIndex,
        currentTurn: gameStore.currentTurn,
        phase: gameStore.phase,
      },
    },
  }
})

const seatViews = computed<SeatViewModel[]>(() => {
  const players = gamePlayers.value
  const tableOrder: SeatKey[] = ['bottom', 'right', 'top', 'left']
  const selfSeatIndex = myPlayer.value?.seatIndex ?? players.find((player) => player.playerId === loggedInUserId.value)?.seatIndex ?? 0
  const currentTurn = gameStore.currentTurn

  return players.slice(0, 4).map((player) => {
    const relativeIndex = (player.seatIndex - selfSeatIndex + 4) % 4
    const seatKey = tableOrder[relativeIndex] ?? 'top'
    return {
      key: seatKey,
      player,
      isSelf: player.playerId === loggedInUserId.value,
      isTurn: player.seatIndex === currentTurn,
    }
  })
})

const rightMessages = computed(() => wsMessages.value.slice(-16).reverse())

const currentPhaseText = computed(() => {
  const map: Record<string, string> = {
    waiting: '等待中',
    dealing: '发牌中',
    playing: '对局中',
    action: '操作中',
    settlement: '已结算',
  }
  return map[gameStore.phase] ?? gameStore.phase
})

const roomStatusText = computed(() => {
  const map: Record<string, string> = {
    waiting: '等待玩家',
    playing: '游戏中',
    finished: '已结束',
  }
  const status = roomState.value.status
  return map[status] ?? status ?? '--'
})

const networkLabel = computed(() => {
  const map: Record<WsStatus, string> = {
    connected: '已连接',
    connecting: '连接中',
    error: '连接异常',
    idle: '未连接',
    closed: '未连接',
  }

  return map[wsStatus.value] ?? '未连接'
})

const formattedClock = computed(() => {
  return new Date(now.value).toLocaleTimeString('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  })
})

const wallBacks = computed<Record<SeatKey, string[]>>(() => {
  const wallSize = remainingTiles.value
  const perSide = Math.max(6, Math.ceil((wallSize || 48) / 4 / 2))

  return {
    top: Array.from({length: perSide}, (_, index) => `top-${index}`),
    right: Array.from({length: perSide}, (_, index) => `right-${index}`),
    bottom: Array.from({length: perSide}, (_, index) => `bottom-${index}`),
    left: Array.from({length: perSide}, (_, index) => `left-${index}`),
  }
})

const seatDecor = computed<Record<SeatKey, SeatPlayerCardModel>>(() => {
  const dealerIndex = roomState.value.game?.state?.dealerIndex ?? -1
  const defaultMissingSuitLabel = missingSuitLabel(null)

  const emptySeat = (): SeatPlayerCardModel => ({
    avatarUrl: '',
    name: '空位',
    dealer: false,
    isTurn: false,
    missingSuitLabel: defaultMissingSuitLabel,
  })

  const result: Record<SeatKey, SeatPlayerCardModel> = {
    top: emptySeat(),
    right: emptySeat(),
    bottom: emptySeat(),
    left: emptySeat(),
  }

  for (const seat of seatViews.value) {
    if (!seat.player) {
      continue
    }

    const displayName = seat.player.displayName || `玩家${seat.player.seatIndex + 1}`
    const avatarUrl = seat.isSelf
      ? (localCachedAvatarUrl.value || seat.player.avatarURL || '')
      : (seat.player.avatarURL || '')

    result[seat.key] = {
      avatarUrl,
      name: seat.isSelf ? '你自己' : displayName,
      dealer: seat.player.seatIndex === dealerIndex,
      isTurn: seat.isTurn,
      missingSuitLabel: missingSuitLabel(seat.player.missingSuit),
    }
  }

  return result
})

const floatingMissingSuit = computed(() => {
  const suitMap: Record<string, string> = {
    万: wanIcon,
    筒: tongIcon,
    条: tiaoIcon,
  }

  const topLabel = seatDecor.value.top?.missingSuitLabel ?? ''
  const leftLabel = seatDecor.value.left?.missingSuitLabel ?? ''
  const rightLabel = seatDecor.value.right?.missingSuitLabel ?? ''

  return {
    top: suitMap[topLabel] ?? '',
    left: suitMap[leftLabel] ?? '',
    right: suitMap[rightLabel] ?? '',
  }
})

function missingSuitLabel(value: string | null | undefined): string {
  const suitMap: Record<string, string> = {
    wan: '万',
    tong: '筒',
    tiao: '条',
  }

  if (!value) {
    return ''
  }
  return suitMap[value] ?? value
}

function getBackImage(seat: SeatKey): string {
  const imageMap: Record<SeatKey, string> = {
    top: topBackImage,
    right: rightBackImage,
    bottom: bottomBackImage,
    left: leftBackImage,
  }

  return imageMap[seat]
}

function toggleMenu(): void {
  menuTriggerActive.value = true
  if (menuTriggerTimer !== null) {
    window.clearTimeout(menuTriggerTimer)
  }
  menuTriggerTimer = window.setTimeout(() => {
    menuTriggerActive.value = false
    menuTriggerTimer = null
  }, 180)

  if (menuOpen.value) {
    menuOpen.value = false
    return
  }

  if (menuOpenTimer !== null) {
    window.clearTimeout(menuOpenTimer)
  }
  menuOpenTimer = window.setTimeout(() => {
    menuOpen.value = true
    menuOpenTimer = null
  }, 85)
}

function toggleTrustMode(): void {
  isTrustMode.value = !isTrustMode.value
  menuOpen.value = false
}

function selectTile(tile: string): void {
  selectedTile.value = selectedTile.value === tile ? null : tile
}

function formatTile(tile: Tile): string {
  return `${tile.suit}${tile.value}`
}

function toGameAction(message: unknown): GameAction | null {
  if (!message || typeof message !== 'object') {
    return null
  }

  const source = message as Record<string, unknown>
  if (typeof source.type !== 'string') {
    return null
  }

  const type = source.type.replace(/[-\s]/g, '_').toUpperCase()
  const payload = source.payload

  switch (type) {
    case 'GAME_INIT':
      if (payload && typeof payload === 'object') {
        return {type: 'GAME_INIT', payload: payload as GameActionPayload<'GAME_INIT'>}
      }
      return null
    case 'GAME_START':
      if (payload && typeof payload === 'object') {
        return {type: 'GAME_START', payload: payload as GameActionPayload<'GAME_START'>}
      }
      return null
    case 'DRAW_TILE':
      if (payload && typeof payload === 'object') {
        return {type: 'DRAW_TILE', payload: payload as GameActionPayload<'DRAW_TILE'>}
      }
      return null
    case 'PLAY_TILE':
      if (payload && typeof payload === 'object') {
        return {type: 'PLAY_TILE', payload: payload as GameActionPayload<'PLAY_TILE'>}
      }
      return null
    case 'PENDING_CLAIM':
      if (payload && typeof payload === 'object') {
        return {type: 'PENDING_CLAIM', payload: payload as GameActionPayload<'PENDING_CLAIM'>}
      }
      return null
    case 'CLAIM_RESOLVED':
      if (payload && typeof payload === 'object') {
        return {type: 'CLAIM_RESOLVED', payload: payload as GameActionPayload<'CLAIM_RESOLVED'>}
      }
      return null
    case 'ROOM_PLAYER_UPDATE':
      if (payload && typeof payload === 'object') {
        return {type: 'ROOM_PLAYER_UPDATE', payload: payload as GameActionPayload<'ROOM_PLAYER_UPDATE'>}
      }
      return null
    case 'ROOM_MEMBER_JOINED':
      if (payload && typeof payload === 'object') {
        return {type: 'ROOM_PLAYER_UPDATE', payload: payload as GameActionPayload<'ROOM_PLAYER_UPDATE'>}
      }
      return null
    default:
      return null
  }
}

function ensureWsConnected(): void {
  const token = auth.value?.token
  if (!token) {
    wsError.value = '未找到登录凭证，无法建立连接'
    return
  }

  wsError.value = ''
  wsClient.connect(buildWsUrl(token), token)
}

function reconnectWs(): void {
  const token = auth.value?.token
  if (!token) {
    wsError.value = '未找到登录凭证，无法建立连接'
    return
  }

  wsError.value = ''
  wsClient.reconnect(buildWsUrl(token), token)
}

function backHall(): void {
  leaveRoomPending.value = true
  const roomId = gameStore.roomId
  sendWsMessage({
    type: 'leave_room',
    roomId,
    payload: {
      room_id: roomId,
    },
  })
  wsClient.close()
  void router.push('/hall').finally(() => {
    leaveRoomPending.value = false
  })
}

function handleLeaveRoom(): void {
  menuOpen.value = false
  backHall()
}

function handleGlobalClick(event: MouseEvent): void {
  const target = event.target as HTMLElement | null
  if (!target) {
    return
  }

  if (target.closest('.menu-trigger-wrap')) {
    return
  }

  menuOpen.value = false
}

function handleGlobalEsc(event: KeyboardEvent): void {
  if (event.key === 'Escape') {
    menuOpen.value = false
  }
}

function hydrateFromActiveRoom(routeRoomId: string): void {
  const room = activeRoom.value
  if (!room) {
    return
  }

  const targetRoomId = routeRoomId || room.roomId
  if (!targetRoomId || room.roomId !== targetRoomId) {
    return
  }

  gameStore.roomId = room.roomId

  const phaseMap: Record<string, typeof gameStore.phase> = {
    waiting: 'waiting',
    playing: 'playing',
    finished: 'settlement',
  }
  gameStore.phase = phaseMap[room.status] ?? gameStore.phase

  const nextPlayers: Record<string, PlayerState> = {}
  for (const player of room.players) {
    if (!player.playerId) {
      continue
    }

    const previous = gameStore.players[player.playerId]
    nextPlayers[player.playerId] = {
      playerId: player.playerId,
      seatIndex: player.index,
      displayName: player.displayName || player.playerId,
      avatarURL: previous?.avatarURL,
      missingSuit: player.missingSuit ?? previous?.missingSuit,
      isReady: player.ready,
      handTiles: previous?.handTiles ?? [],
      melds: previous?.melds ?? [],
      discardTiles: previous?.discardTiles ?? [],
      score: previous?.score ?? 0,
    }
  }
  gameStore.players = nextPlayers
}


onMounted(() => {
  const routeRoomId = typeof route.params.roomId === 'string' ? route.params.roomId : ''
  hydrateFromActiveRoom(routeRoomId)
  if (routeRoomId) {
    gameStore.roomId = routeRoomId
  }

  const handler = (status: WsStatus) => {
    wsStatus.value = status
  }

  wsClient.onMessage((msg: unknown) => {
    const text = typeof msg === 'string' ? msg : JSON.stringify(msg)
    wsMessages.value.push(`[server] ${text}`)
    const gameAction = toGameAction(msg)
    if (gameAction) {
      dispatchGameAction(gameAction)
    }
  })
  wsClient.onError((message: string) => {
    wsError.value = message
    wsMessages.value.push(`[error] ${message}`)
  })

  unsubscribe = wsClient.onStatusChange(handler)
  ensureWsConnected()

  clockTimer = window.setInterval(() => {
    now.value = Date.now()
  }, 1000)

  window.addEventListener('click', handleGlobalClick)
  window.addEventListener('keydown', handleGlobalEsc)
})

onBeforeUnmount(() => {
  if (unsubscribe) {
    unsubscribe()
    unsubscribe = null
  }

  if (clockTimer !== null) {
    window.clearInterval(clockTimer)
    clockTimer = null
  }

  window.removeEventListener('click', handleGlobalClick)
  window.removeEventListener('keydown', handleGlobalEsc)

  if (menuTriggerTimer !== null) {
    window.clearTimeout(menuTriggerTimer)
    menuTriggerTimer = null
  }

  if (menuOpenTimer !== null) {
    window.clearTimeout(menuOpenTimer)
    menuOpenTimer = null
  }
})
</script>

<template>
  <section class="picture-scene">
    <div class="picture-layout">
      <section class="table-stage">
        <img class="table-desk" :src="deskImage" alt=""/>

        <div class="table-felt">
          <div class="table-surface"></div>
          <div class="inner-outline outer"></div>
          <div class="inner-outline mid"></div>

          <div class="top-left-tools">
            <div class="menu-trigger-wrap">
              <button
                  class="metal-circle menu-trigger"
                  :class="{ 'is-feedback': menuTriggerActive }"
                  type="button"
                  :disabled="leaveRoomPending"
                  @click.stop="toggleMenu"
              >
                <span class="menu-trigger-icon">☰</span>
              </button>
              <transition name="menu-pop">
                <div v-if="menuOpen" class="menu-popover" @click.stop>
                  <div class="menu-list">
                    <button class="menu-item menu-item-delay-1" type="button" @click="toggleTrustMode">
                      <span class="menu-item-icon">
                        <img :src="robotIcon" alt=""/>
                      </span>
                      <span>{{ isTrustMode ? '取消托管' : '托管' }}</span>
                    </button>
                    <button
                        class="menu-item menu-item-danger menu-item-delay-2"
                        type="button"
                        :disabled="leaveRoomPending"
                        @click="handleLeaveRoom"
                    >
                      <span class="menu-item-icon">
                        <img :src="exitIcon" alt=""/>
                      </span>
                      <span>{{ leaveRoomPending ? '退出中...' : '退出' }}</span>
                    </button>
                  </div>
                </div>
              </transition>
            </div>
            <div class="left-counter">
              <span class="counter-light"></span>
              <strong>{{ roomState.game?.state?.wall?.length ?? 48 }}</strong>
            </div>
            <span v-if="isTrustMode" class="trust-chip">托管中</span>
          </div>

          <div class="top-right-clock">
            <div class="signal-chip">
              <span class="wifi-dot" :class="`is-${wsStatus}`"></span>
              <strong>{{ networkLabel }}</strong>
            </div>
            <span>{{ formattedClock }}</span>
          </div>


          <TopPlayerCard :player="seatDecor.top"/>
          <RightPlayerCard :player="seatDecor.right"/>
          <BottomPlayerCard :player="seatDecor.bottom"/>
          <LeftPlayerCard :player="seatDecor.left"/>

          <div class="wall wall-top">
            <img v-for="key in wallBacks.top" :key="key" :src="getBackImage('top')" alt=""/>
          </div>
          <div class="wall wall-right">
            <img v-for="key in wallBacks.right" :key="key" :src="getBackImage('right')" alt=""/>
          </div>
          <div class="wall wall-bottom">
            <img v-for="key in wallBacks.bottom" :key="key" :src="getBackImage('bottom')" alt=""/>
          </div>
          <div class="wall wall-left">
            <img v-for="key in wallBacks.left" :key="key" :src="getBackImage('left')" alt=""/>
          </div>

          <div class="floating-status top">
            <img v-if="floatingMissingSuit.top" :src="floatingMissingSuit.top" alt=""/>
            <span>{{ seatDecor.top.missingSuitLabel }}</span>
          </div>
          <div class="floating-status left">
            <img v-if="floatingMissingSuit.left" :src="floatingMissingSuit.left" alt=""/>
            <span>{{ seatDecor.left.missingSuitLabel }}</span>
          </div>
          <div class="floating-status right">
            <img v-if="floatingMissingSuit.right" :src="floatingMissingSuit.right" alt=""/>
            <span>{{ seatDecor.right.missingSuitLabel }}</span>
          </div>


          <div class="bottom-control-panel">

            <div class="player-hand" v-if="myHandTiles.length > 0">
              <button
                  v-for="tile in myHandTiles"
                  :key="tile.id"
                  class="tile-chip"
                  :class="{ selected: selectedTile === formatTile(tile) }"
                  type="button"
                  @click="selectTile(formatTile(tile))"
              >
                {{ formatTile(tile) }}
              </button>
            </div>
          </div>
        </div>
      </section>

      <aside class="ws-sidebar">
        <div class="sidebar-head">
          <div>
            <p class="sidebar-title">WebSocket 消息</p>
            <small>{{ networkLabel }} · {{ loggedInUserName || '未登录昵称' }}</small>
          </div>
          <button class="sidebar-btn" type="button" @click="reconnectWs">重连</button>
        </div>

        <div class="sidebar-stats">
          <div class="sidebar-stat">
            <span>房间</span>
            <strong>{{ roomState.name || roomName || '未命名' }}</strong>
          </div>
          <div class="sidebar-stat">
            <span>阶段</span>
            <strong>{{ currentPhaseText }}</strong>
          </div>
          <div class="sidebar-stat">
            <span>人数</span>
            <strong>{{ roomState.playerCount }}/{{ roomState.maxPlayers }}</strong>
          </div>
          <div class="sidebar-stat">
            <span>状态</span>
            <strong>{{ roomStatusText }}</strong>
          </div>
        </div>

        <p v-if="wsError" class="sidebar-error">{{ wsError }}</p>

        <div class="sidebar-log">
          <p v-if="rightMessages.length === 0" class="sidebar-empty">等待服务器消息...</p>
          <p v-for="(line, index) in rightMessages" :key="index" class="sidebar-line">{{ line }}</p>
        </div>
      </aside>
    </div>
  </section>
</template>
