<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { playCountdownMusic, stopCountdownMusic } from '@/commons/music'

const props = withDefaults(defineProps<{
    background?: string
    width?: number
    height?: number
    mode?: 'contain' | 'cover'
    backgroundMode?: 'contain' | 'cover',
    showGameRule?: boolean,
    showGameRank?: boolean,
}>(), {
    background: 'linear-gradient(180deg, #fbf7ef 0%, #ffe7dc 100%)',
    width: 750,
    height: 1624,
    mode: 'contain',
    backgroundMode: 'cover',
    showGameRule: true, //玩家要有操作动作才能播放音乐,这里会默认显示游戏规则,让玩家有关闭操作
    showGameRank: false
})

const emit = defineEmits<{
    'game-countdown-change': [value: number]
    'update:gameCountdown': [value: number]
}>()

const scale = ref(1)
const offsetLeft = ref(0)
const offsetTop = ref(0)
const backgroundScale = ref(1)
const backgroundOffsetLeft = ref(0)
const backgroundOffsetTop = ref(0)
const viewportWidth = ref(0)
const viewportHeight = ref(0)
const countdown = ref(3)
const countdownVisible = ref(false)
const countdownKey = ref(0)
const gameCountdown = ref(60)
const gameCountdownVisible = ref(false)
let countdownTimer: ReturnType<typeof setTimeout> | undefined
let countdownResolve: (() => void) | undefined

function setGameCountdown(value: number) {
    gameCountdown.value = value
    emit('game-countdown-change', value)
    emit('update:gameCountdown', value)
}

function getScale(scaleX: number, scaleY: number, mode: 'contain' | 'cover') {
    return mode === 'cover'
        ? Math.max(scaleX, scaleY)
        : Math.min(scaleX, scaleY)
}

const updateScale = () => {
    viewportWidth.value = window.visualViewport?.width ?? window.innerWidth
    viewportHeight.value = window.visualViewport?.height ?? window.innerHeight
    const scaleX = viewportWidth.value / props.width
    const scaleY = viewportHeight.value / props.height

    scale.value = getScale(scaleX, scaleY, props.mode)
    backgroundScale.value = getScale(scaleX, scaleY, props.backgroundMode)

    offsetLeft.value = Math.max((viewportWidth.value - props.width * scale.value) / 2, 0)
    offsetTop.value = Math.max((viewportHeight.value - props.height * scale.value) / 2, 0)
    backgroundOffsetLeft.value = (viewportWidth.value - props.width * backgroundScale.value) / 2
    backgroundOffsetTop.value = (viewportHeight.value - props.height * backgroundScale.value) / 2
}

onMounted(() => {
    updateScale()
    window.addEventListener('resize', updateScale)
    window.visualViewport?.addEventListener('resize', updateScale)
})

onBeforeUnmount(() => {
    stopCountdown()
    window.removeEventListener('resize', updateScale)
    window.visualViewport?.removeEventListener('resize', updateScale)
})

const stageStyle = computed(() => ({
    width: `${props.width}px`,
    height: `${props.height}px`,
    left: `${offsetLeft.value}px`,
    top: `${offsetTop.value}px`,
    '--stage-viewport-left': `${scale.value === 0 ? 0 : -offsetLeft.value / scale.value}px`,
    '--stage-viewport-top': `${scale.value === 0 ? 0 : -offsetTop.value / scale.value}px`,
    '--stage-viewport-width': `${scale.value === 0 ? props.width : viewportWidth.value / scale.value}px`,
    '--stage-viewport-height': `${scale.value === 0 ? props.height : viewportHeight.value / scale.value}px`,
    transform: `scale(${scale.value})`,
}))

const backgroundStyle = computed(() => ({
    width: `${props.width}px`,
    height: `${props.height}px`,
    left: `${backgroundOffsetLeft.value}px`,
    top: `${backgroundOffsetTop.value}px`,
    background: props.background,
    transform: `scale(${backgroundScale.value})`,
}))
const clearCountdown = () => {
    if (countdownTimer) {
        clearTimeout(countdownTimer)
        countdownTimer = undefined
    }
}
const stopCountdown = () => {
    clearCountdown()
    stopCountdownMusic()
    countdownVisible.value = false
    gameCountdownVisible.value = false
    countdownResolve?.()
    countdownResolve = undefined
}
const startCountdown = (startSeconds = 3) => {
    stopCountdown()
    playCountdownMusic()

    return new Promise<void>((resolve) => {
        countdownResolve = resolve
        countdown.value = startSeconds
        countdownVisible.value = true
        countdownKey.value += 1

        const tick = () => {
            if (countdown.value <= 1) {
                countdownVisible.value = false
                countdownTimer = undefined
                countdownResolve?.()
                countdownResolve = undefined
                return
            }

            countdown.value -= 1
            countdownKey.value += 1
            countdownTimer = setTimeout(tick, 1000)
        }

        countdownTimer = setTimeout(tick, 1000)
    })
}

defineExpose({
    startCountdown,
    stopCountdown,
})
</script>

<template>
    <main class="mobile-stage-viewport">        
        <div class="mobile-stage-background" :style="backgroundStyle" />
        <div class="mobile-stage" :style="stageStyle">
            <slot />
        </div>
        <div v-if="countdownVisible" class="modal-container">
            <div :key="countdownKey" class="countdown-number">{{ countdown }}</div>
        </div>
        <Transition name="game-rule-fade">
            <div v-if="!countdownVisible && showGameRule" class="modal-container">
                <slot name="gameRule"></slot>
            </div>
        </Transition>
        <!-- <Transition name="game-rule-fade">
            <div v-if="!countdownVisible && showGameRank" class="modal-container">
                <slot name="gameRank"></slot>
            </div>
        </Transition> -->
    </main>
</template>

<style scoped>
.mobile-stage-viewport {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
    overscroll-behavior: none;
}

.mobile-stage-background {
    position: absolute;
    transform-origin: left top;
    z-index: 0;
}

.mobile-stage {
    position: absolute;
    transform-origin: left top;
    overflow: visible;
    z-index: 1;
}

.modal-container {
    /* position:fixed + 显式 100vw/100vh 确保在 iOS Safari 中全屏蒙层，
       避免因父级 overflow:hidden / transform:scale 导致 inset:0 计算异常 */
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(0, 0, 0, 0.6);
}

.game-rule-fade-enter-active,
.game-rule-fade-leave-active {
    transition: opacity 0.25s ease;
}

.game-rule-fade-enter-from,
.game-rule-fade-leave-to {
    opacity: 0;
}

.countdown-number {
    color: white;
    font-size: 200pt;
    font-weight: 800;
    line-height: 1;
    text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
    animation: countdown-pop 1s ease both;
}

@keyframes countdown-pop {
    0% {
        opacity: 0;
        transform: scale(0.45);
    }

    18% {
        opacity: 1;
        transform: scale(1.14);
    }

    58% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: scale(0.84);
    }
}
</style>
