<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { assetUrl, cssAssetUrl } from '@/commons/assets.ts'
import MobileStage from '@/components/MobileStage.vue'
import { useGameSocket, sendGameMessage, userJoinStatus, joinSharedRoom, userTop10RankStatus } from '@/composables/useGameSocket'
import LoadingView from './views/LoadingView.vue'
import PlayingView from './views/PlayingView.vue'
import ScoreView from './views/ScoreView.vue'
import { $alreadyRankedToast, $getWechat, $toast } from '@/commons/utils.ts'
import { playGame1Music } from '@/commons/music'

const gameId = 'game5';
type GameView = 'loading' | 'playing' | 'score'
type RankPlayer = {
    rank: number
    nickname: string
    avatar: string
    score: number | string
}

const imageUrls = {
    bg: cssAssetUrl('game5/bg.webp'),
    playingBg: cssAssetUrl('game5/second-bg.webp'),
    bg2: cssAssetUrl('game1/bg2.png'),
    logo: cssAssetUrl('logo.png'),
    title: cssAssetUrl('game5/title.webp'),
    avatar: cssAssetUrl('avatar-yellow.png'),
    gu: cssAssetUrl('game1/gu.png'),
    clock: cssAssetUrl('game1/clock.png'),
    contdown: cssAssetUrl('game5/countdown-title-bg.webp'),
    startBg: cssAssetUrl('game5/loading-start-bg.webp'),
    gu01: cssAssetUrl('game1/gu01.png'),
    gu02: cssAssetUrl('game1/gu02.png'),
    gu03: cssAssetUrl('game1/gu03.png'),
    scoreBg: cssAssetUrl('game1/score-bg.png'),
    back: cssAssetUrl('game1/back.png'),
    replay: cssAssetUrl('game1/replay.png'),
    close: cssAssetUrl('game1/close.png'),
    rank: cssAssetUrl('game6/rank.png'),
    ranklist: cssAssetUrl('game6/ranklist.png'),
    paodao: cssAssetUrl('game5/paodao.webp'),
    circleBg: cssAssetUrl('game5/circle-bg.webp'),
    circle: cssAssetUrl('game5/circle.webp'),
    centerTile: cssAssetUrl('game5/biaoti.webp'),
    rule: cssAssetUrl('game1/rule.png')
}

const playingAssetUrls = [
    assetUrl('game5/second-bg.webp'),
    assetUrl('game5/paodao.webp'),
    assetUrl('game5/circle-bg.webp'),
    assetUrl('game5/circle.webp'),
    assetUrl('game5/biaoti.webp'),
    assetUrl('game5/sprites/circle-animation.png'),
    assetUrl('game5/sprites/horse1-no-circle.png'),
    assetUrl('game5/sprites/horse2-no-circle.png'),
    assetUrl('game5/sprites/horse3-no-circle.png'),
    assetUrl('game5/sprites/horse4-no-circle.png'),
]
let playingAssetsPromise: Promise<void> | undefined

function preloadPlayingAssets() {
    if (!playingAssetsPromise) {
        playingAssetsPromise = Promise.all(playingAssetUrls.map(url => new Promise<void>((resolve) => {
            const image = new Image()
            image.onload = () => resolve()
            image.onerror = () => resolve()
            image.src = url
        }))).then(() => undefined)
    }

    return playingAssetsPromise
}

const wechat = $getWechat()
const currentView = ref<GameView>('loading')
const isGameStarting = ref(false)

const stageBackground = computed(() => {
    if (currentView.value === 'playing' || isGameStarting.value) {
        return `${imageUrls.playingBg} center/cover`
    }
    return `${imageUrls.bg} center/cover`
})
const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const tick = ref(0)  //点击次数
const rank = ref(0)  //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const guFrames: [string, string, string] = [imageUrls.gu01, imageUrls.gu02, imageUrls.gu03]
const currentGu = ref(imageUrls.gu01)
const isGuPlaying = ref(false)
const token = ref(wechat?.token ?? '')
const nickname = ref(wechat?.nickname ?? '')
const avatar = ref(wechat?.avatar ?? '')
const showGameRule = ref(true)
const showGameRank = ref(false)
const rankList = ref<RankPlayer[]>([])
const displayRankList = computed<RankPlayer[]>(() => {
    const list = rankList.value.slice(0, 10)

    return Array.from({ length: 10 }, (_, index) => {
        return list[index] ?? {
            rank: index + 1,
            nickname: '-',
            avatar: '',
            score: '-',
        }
    })
})

const mobileStageRef = ref<InstanceType<typeof MobileStage> | null>(null)
const _offSocketMessage = ref<(() => void) | undefined>()
let guFrameTimer: ReturnType<typeof window.setTimeout> | undefined
let gameCountdownTimer: ReturnType<typeof window.setTimeout> | undefined

const confirmRefresh = (event: BeforeUnloadEvent) => {
    event.preventDefault()
    event.returnValue = ''
}

function getServerRank(data: any, fallback?: unknown) {
    const value = typeof data === 'object' && data
        ? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
        : data
    const nextRank = Number(value)
    return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}


function showFinalScoreIfReady() {
    if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
    hasFinalScoreResult.value = false
    showGameOverRank()
}

function markFinalScoreResult() {
    hasFinalScoreResult.value = true
    showFinalScoreIfReady()
}

function syncFinalStatus(data: any) {
    const nextStatus = Number(data?.status)
    if (!Number.isFinite(nextStatus)) return

    roomStatus.value = nextStatus
    if (nextStatus === 3) {
        return
    }
    if (nextStatus !== 4) return

    if (Array.isArray(data?.list)) {
        rankList.value = data.list.map((item: any, index: number) => ({
            rank: getServerRank(item, index + 1),
            nickname: String(item?.nickname ?? ''),
            avatar: String(item?.avatar ?? ''),
            score: Number(item?.score ?? 0),
        }))
    }

    const nextRank = getServerRank(data)
    const nextScore = Number(data?.score)
    if (nextRank > 0) {
        rank.value = nextRank
    }
    if (Number.isFinite(nextScore) && nextScore >= 0) {
        tick.value = nextScore
    }
    showFinalScoreIfReady()
}

function setDivDescVisible(visible: boolean) {
    isDivDescVisible.value = visible
}

function stopGameCountdown() {
    if (gameCountdownTimer) {
        window.clearTimeout(gameCountdownTimer)
        gameCountdownTimer = undefined
    }
}

function submitScore(save: boolean = false, currentTick = tick.value) {
    //最后提交保存在数据库中
    if (save) {
        //item_num 游戏项目(1到6)
        //wechat 原始token
        const data = {
            score: currentTick,
            wechat: wechat.token,
            item_num: 5,
            nickname: wechat.nickname,
            avatar: wechat.avatar,
            rank: rank.value,
        }
        console.log('[WebSocket] send submit_score_save:', JSON.stringify(data, null, 2))
        return sendGameMessage('submit_score_save', data)
    } else {
        const data = { score: currentTick }
        console.log('[WebSocket] send submit_score:', JSON.stringify(data))
        return sendGameMessage('submit_score', data)
    }
}

function startGameCountdown(seconds = 60) {
    stopGameCountdown()
    countdownInterval.value = seconds
    setDivDescVisible(true)

    const tickCountdown = () => {
        countdownInterval.value -= 1

        if (countdownInterval.value <= 0) {
            countdownInterval.value = 0
            gameCountdownTimer = undefined
            setDivDescVisible(false)
            hasFinalScoreResult.value = false
            const sent = submitScore(true)
            if (userTop10RankStatus.value) {
                userJoinStatus.value = false;
                // showGameRank.value = false;
                $alreadyRankedToast()
            } else if (sent) {
                $toast('成绩提交中，请稍候...', 5000)
            }
            return
        }

        gameCountdownTimer = window.setTimeout(tickCountdown, 1000)
    }

    gameCountdownTimer = window.setTimeout(tickCountdown, 1000)
}

function syncGameCountdown(seconds: number) {
    const syncedSeconds = Math.max(0, Math.floor(seconds))
    if (countdownInterval.value - syncedSeconds <= 1) return

    if (syncedSeconds === 0) {
        countdownInterval.value = 0
        return
    }

    startGameCountdown(syncedSeconds)
}

function showLoadingView() {
    stopGameCountdown()
    isGameStarting.value = false
    currentView.value = 'loading'
    setDivDescVisible(false)
    // alert('管理员关闭了游戏房间')
    $toast('管理员关闭了游戏房间')
}

function resetToLoadingView() {
    stopGameCountdown()
    isGameStarting.value = false
    if (guFrameTimer) {
        window.clearTimeout(guFrameTimer)
        guFrameTimer = undefined
    }
    tick.value = 0
    rank.value = 0
    roomStatus.value = 0
    hasFinalScoreResult.value = false
    rankList.value = []
    roomStatus.value = 0
    hasFinalScoreResult.value = false
    countdownInterval.value = 60
    currentGu.value = guFrames[0]
    isGuPlaying.value = false
    userJoinStatus.value = false
    showGameRank.value = false
    currentView.value = 'loading'
    setDivDescVisible(false)
}

function startGameView() {
    stopGameCountdown()
    tick.value = 0
    rank.value = 0
    roomStatus.value = 0
    hasFinalScoreResult.value = false
    rankList.value = []
    roomStatus.value = 0
    hasFinalScoreResult.value = false
    currentGu.value = guFrames[0]
    showGameRank.value = false
    roomStatus.value = 2
    hasFinalScoreResult.value = false
    currentView.value = 'playing'
    startGameCountdown()
}

function showGameOverRank() {
    stopGameCountdown()
    setDivDescVisible(false)
    currentView.value = 'score'
    showGameRank.value = false
}


function handleRoomBack() {

}

const hitHandler = (score: number) => {
    const nextTick = tick.value + score
    if (!submitScore(false, nextTick)) return
    tick.value = nextTick
    console.log('[WebSocket] 圈中马！得分:', score, '当前总分:', tick.value)
    // console.log('[Audio] playGame1Music() 被调用 - 播放 game1.mp3 背景音乐')
    // playGame1Music()
}

if (wechat) {
    const { offSocketMessage } = useGameSocket({
        gameId: 'game5',
        auth: {
            userid: wechat.token,
        },
        onConnect: () => {
            window.setTimeout(() => {
                const joined = joinSharedRoom(nickname.value, token.value, avatar.value, gameId)
            }, 200)
        },
        onGameStart: async () => {
            showGameRule.value = false;
            isGameStarting.value = true
            await Promise.all([
                mobileStageRef.value?.startCountdown(),
                preloadPlayingAssets(),
            ])
            startGameView()
            isGameStarting.value = false
        },
        onScoreSubmitted: (is_save, data) => {
            if (!is_save) {//非保存状态下
                const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
                const nextRank = getServerRank(data)
                const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
                if (Number.isFinite(nextScore) && nextScore >= 0) {
                    tick.value = nextScore
                }
                if (nextRank > 0) {
                    rank.value = nextRank
                }
                if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
                    syncGameCountdown(remainingSeconds)
                }
            } else if (typeof data === 'object' && data) {
                if (Array.isArray(data.list)) {
                    rankList.value = data.list.map((item: any, index: number) => ({
                        rank: getServerRank(item, index + 1),
                        nickname: String(item?.nickname ?? ''),
                        avatar: String(item?.avatar ?? ''),
                        score: Number(item?.score ?? 0),
                    }))
                }

                const nextRank = getServerRank(data)
                if (nextRank > 0) {
                    rank.value = nextRank
                }
                markFinalScoreResult()
            }
            return { code: 'QCNF', score: tick.value, rank_no: rank.value }
        },
        onStatus: syncFinalStatus,
        onRescoreSubmitted: (_recount) => {
            submitScore(true)
        },
        onRoomClosed: showLoadingView,
        onRoomBack: handleRoomBack,
    })
    _offSocketMessage.value = offSocketMessage
}

onMounted(() => {
    document.title = '互动游戏 - 圈彩纳福'
    preloadPlayingAssets()
    if (token.value) {
        window.addEventListener('beforeunload', confirmRefresh)
    }
})

onBeforeUnmount(() => {
    _offSocketMessage.value?.()
    if (guFrameTimer) {
        window.clearTimeout(guFrameTimer)
    }
    stopGameCountdown()
    if (token.value) {
        window.removeEventListener('beforeunload', confirmRefresh)
    }
})

const showGameRuleHandler = () => {
    showGameRule.value = !showGameRule.value;
}

const rankCloseHandler = () => {
    showGameRank.value = false
}
</script>

<template>
    <MobileStage v-if="token" :showGameRule="showGameRule" :showGameRank="showGameRank" ref="mobileStageRef"
        :background="stageBackground">
        <template #gameRank>
            <div class="ranklist-container">
                <!-- <div class="txt-title">排行</div>
                <div class="list-container">
                    <div class="row row-1">
                        <div class="col-1">排名</div>
                        <div class="col-2">用户</div>
                        <div class="col-3">成绩</div>
                    </div>
                    <div class="row-2">
                        <div class="row rank-row" v-for="(item, index) in displayRankList"
                            :key="`${item.rank}-${index}`">
                            <div class="col-1">
                                <div>{{ item.rank }}</div>
                            </div>
                            <div class="col-2">
                                <div class="avatar"
                                    :style="item.avatar ? { backgroundImage: `url(${item.avatar})` } : {}"></div>
                                <div>{{ item.nickname }}</div>
                            </div>
                            <div class="col-3">{{ item.score }}</div>
                        </div>
                    </div>
                    <div class="row row-3">
                        <div class="col-1"></div>
                        <div class="col-2">
                            <div class="avatar" :style="{ backgroundImage: `url(${avatar})`, top: '27px' }"></div>
                            <div>{{ nickname }}</div>
                        </div>
                        <div class="col-3">{{ tick }}</div>
                    </div>
                </div> -->
                <div class="img-close" @click="rankCloseHandler"></div>
            </div>
        </template>
        <template #gameRule>
            <div class="rule-container">
                <div class="rule-txt-container">
                    <div class="rule-title">游戏规则</div>
                    <div class="rule-txt">
                        1.主持人开赛后点击套圈，套中小马得 10 分，落空不得分;<br />
                        2.限时比拼准度，结束后按积分排名;<br />
                        3.高分用户有机会领取新疆福彩定制周边;<br />
                        4.禁止外挂作弊，违规账号取消成绩与领奖资格;<br /><br />
                        祝您游戏愉快！
                    </div>
                    <div class="rule-close" @click="showGameRuleHandler"></div>
                </div>
            </div>
        </template>
        <LoadingView v-if="currentView === 'loading'" :image-urls="imageUrls" :user-join-status="userJoinStatus"
            @touchGameRule="showGameRuleHandler" />
        <!-- <PlayingView v-else :image-urls="imageUrls" :tick="tick" :rank="rank" -->
        <PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
            :current-gu="currentGu" :countdown-interval="countdownInterval" :is-div-desc-visible="isDivDescVisible"
            @hit="hitHandler" />
        <ScoreView v-else :image-urls="imageUrls" :rank="rank" :tick="tick" />
    </MobileStage>
    <div v-else style="text-align: center; width: 100vw; height: 100vh; line-height: 30; font-size: 20px;">
        请使用微信扫码进入游戏
    </div>
</template>
<style scoped>
.rule-container {
    .rule-txt-container {
        background: v-bind('imageUrls.rule') center / cover no-repeat;
        width: 692px;
        height: 797px;
        margin-top: -20%;
        transform: scale(0.5);

        .rule-title {
            text-align: center;
            color: white;
            font-weight: bold;
            font-size: 26pt;
            line-height: 50pt;
            letter-spacing: 4px;
        }

        .rule-txt {
            font-size: 26pt;
            padding: 40px;
            margin-top: 10px;
            color: #AA0000;
        }
    }

    .rule-close {
        width: 57px;
        height: 57px;
        background: v-bind('imageUrls.close') center / cover no-repeat;
        transform: translateX(-50%) scale(1.5);
        position: absolute;
        top: -73px;
        left: 100%;
        margin-left: -48px;
    }
}

.ranklist-container {
    width: 747px;
    height: 1084px;
    background: v-bind('imageUrls.ranklist');
    transform: translate(-50%, -50%) scale(0.45);
    left: 50%;
    top: 50%;
    position: absolute;

    .txt-title {
        width: 100%;
        text-align: center;
        font-size: 50px;
        margin-top: 35px;
        font-weight: bold;
        color: #AA0000;
        letter-spacing: 2px;
    }

    .list-container {
        width: calc(100% - 90px);
        height: calc(100% - 210px);
        position: absolute;
        left: 40px;
        top: 140px;
        text-align: center;
        height: 73px;
        line-height: 73px;
        font-size: 32px;

        .row {
            display: grid;
            grid-template-columns: 150px auto 150px;
            align-items: center;
        }

        .row-1 {
            color: #AA0000;
            font-weight: bold;
            background: linear-gradient(-84deg, #FFD396 0%, #FFF3D1 53%, #FFD59C 100%);
            border-radius: 20px 20px 0px 0px;
        }

        .row-2 {
            color: #633911;

            .rank-row:nth-child(odd) {
                background: #F2E2AE;
            }

            .rank-row:nth-child(even) {
                background: #FCEEBF;
            }

            .col-1 {
                position: relative;
            }
        }

        .row-3 {
            background: linear-gradient(0deg, #F4B64C 0%, #F7C96F 100%);
            border-radius: 0px 0px 20px 20px;
        }

        .col-2 {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 18px;

            .avatar {
                width: 48px;
                height: 48px;
                flex: 0 0 auto;
                border-radius: 50%;
                background-position: center;
                background-size: cover;
            }
        }
    }
}

.img-close {
    width: 57px;
    height: 57px;
    background: v-bind('imageUrls.close') center / cover no-repeat;
    transform: translateX(-50%) scale(1.5);
    position: absolute;
    top: -35px;
    left: 100%;
    margin-left: -58px;
}
</style>
