<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import DesignStage from '../../../components/DesignStage.vue'
import type Player from '../../../commons/player.ts'
import { $format_str, $is_run_local } from '@/commons/utils.ts';
import { assetUrl, cssAssetUrl } from '@/commons/assets.ts';

const props = defineProps<{
    rankList: Player[];
}>();

const emit = defineEmits<{
    start: [];
}>();

const RANK_LIMIT = 10;
const START_COUNTDOWN_SECONDS = 3;
const GU_FRAME_DURATION = 300;
const GU_LOOP_DELAY = 1000;
const MOLE_CELL_COUNT = 9;
const MOLE_LAND_DURATION = 5000;
const MOLE_SHOW_DURATION = 1200;
const MOLE_HIDE_DURATION = 650;
const designStage = ref<InstanceType<typeof DesignStage> | null>(null);
const rankEffects = ref<Record<string, "up" | "down" | "new">>({});
const guFrameIndex = ref(0);
const guBurstVisible = ref(false);
const guBurstKey = ref(0);
let guFramesReady: Promise<void> | undefined;
let guTimer: ReturnType<typeof setTimeout> | undefined;
let guBurstTimer: ReturnType<typeof setTimeout> | undefined;

// const loadGuFrames = (dir: string) => Array.from(
//     { length: 10 },
//     (_, index) => assetUrl(`${dir}/${String(index + 1).padStart(2, '0')}.png`),
// );

// const guFrames = [
//     loadGuFrames('game1/gu'),
//     loadGuFrames('game1/gu2'),
// ];

const imageUrls = {
    bg: cssAssetUrl('game6/bg2.png'),
    logo: cssAssetUrl('game1/logo.png'),
    title: cssAssetUrl('game6/title2.png'),
    fireworks: cssAssetUrl('game1/fireworks.png'),

    // fireworks: cssAssetUrl('game1/fireworks.png'),
    // titleRank: cssAssetUrl('game1/title-rank.png'),
    rankNo4: cssAssetUrl('game6/no4.png'),
    rankNo1: cssAssetUrl('game6/no1.png'),
    rankNo2: cssAssetUrl('game6/no2.png'),
    rankNo3: cssAssetUrl('game6/no3.png'),
    // rankIcon: cssAssetUrl('game1/no-icon.png'),
    // rankNo1Icon: cssAssetUrl('game1/no1-icon.png'),
    // rankNo2Icon: cssAssetUrl('game1/no2-icon.png'),
    // rankNo3Icon: cssAssetUrl('game1/no3-icon.png'),
    avatar: cssAssetUrl('avatar-yellow.png'),
    avatarAnimation: cssAssetUrl('avatar-yellow_animation.png'),
    mole2: cssAssetUrl('game6/mole2.png'),
    rankContainer: cssAssetUrl('game6/rank-container.png'),
    land: cssAssetUrl('game6/land.png'),
    moleShow: cssAssetUrl('game6/mole_show.png'),
    moleHide: cssAssetUrl('game6/mole_hide.png'),
    rabbitShow: cssAssetUrl('game6/rabbit_show.png'),
    rabbitHide: cssAssetUrl('game6/rabbit_hide.png'),
};

const emptyPlayer = (index: number): Player => ({
    wechat_id: `empty-${index}`,
    nickname: '-',
    avatar: '',
    score: 0,
});

const list = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyPlayer(index)));
const playerKey = (player: Player, index = 0) => {
    // 优先使用 userid/wechat_id，没有则用 avatar 作为唯一标识
    const id = player.userid || player.wechat_id;
    if (id && !String(id).startsWith('empty-')) return String(id);
    
    // 如果有头像且昵称有效，用 avatar 作为唯一标识
    if (player.avatar && player.nickname && player.nickname !== '-' && player.nickname !== '虚位以待') {
        return `avt:${player.avatar}`;
    }
    
    return `empty-${index}`;
};
const mockRankStep = ref(0);
type MoleKind = "land" | "mole" | "rabbit";
type MolePhase = "land" | "show" | "hide";
type MoleCell = {
    kind: MoleKind;
    phase: MolePhase;
    animationKey: number;
};
const moleCells = ref<MoleCell[]>(
    Array.from({ length: MOLE_CELL_COUNT }, () => ({
        kind: "land",
        phase: "land",
        animationKey: 0,
    })),
);
const moleTimers: Array<ReturnType<typeof setTimeout> | undefined> = [];

const makeMockPlayer = (id: number, score: number): Player => ({
    wechat_id: `mock-${id}`,
    nickname: `Player ${id}`,
    avatar: '',
    score,
});

const mockRankRounds: Player[][] = [
    [
        makeMockPlayer(1, 980),
        makeMockPlayer(2, 900),
        makeMockPlayer(3, 860),
        makeMockPlayer(4, 820),
        makeMockPlayer(5, 780),
        makeMockPlayer(6, 720),
        makeMockPlayer(7, 680),
        makeMockPlayer(8, 640),
        makeMockPlayer(9, 600),
        makeMockPlayer(10, 560),
    ],
    [
        makeMockPlayer(4, 1040),
        makeMockPlayer(1, 1010),
        makeMockPlayer(7, 960),
        makeMockPlayer(2, 940),
        makeMockPlayer(11, 880),
        makeMockPlayer(3, 850),
        makeMockPlayer(5, 830),
        makeMockPlayer(8, 790),
        makeMockPlayer(6, 760),
        makeMockPlayer(10, 700),
    ],
    [
        makeMockPlayer(7, 1180),
        makeMockPlayer(11, 1090),
        makeMockPlayer(4, 1060),
        makeMockPlayer(12, 1000),
        makeMockPlayer(1, 980),
        makeMockPlayer(8, 930),
        makeMockPlayer(2, 890),
        makeMockPlayer(5, 850),
        makeMockPlayer(13, 810),
        makeMockPlayer(3, 780),
    ],
];

const updateRankList = (rankList: Player[]) => {
    const oldRanks = new Map(list.value.map((player, index) => [playerKey(player, index), index]));
    const next = Array.from({ length: RANK_LIMIT }, (_, index) => rankList[index] ?? emptyPlayer(index));
    const effects: Record<string, "up" | "down" | "new"> = {};

    next.forEach((player, index) => {
        const key = playerKey(player, index);
        if (!key || key.startsWith("empty-")) return;

        const oldIndex = oldRanks.get(key);
        if (oldIndex === undefined) effects[key] = "new";
        else if (oldIndex > index) effects[key] = "up";
        else if (oldIndex < index) effects[key] = "down";
    });

    list.value = next;
    rankEffects.value = {};
    requestAnimationFrame(() => {
        rankEffects.value = effects;
    });
};

watch(
    () => props.rankList,
    (rankList) => updateRankList(rankList),
    { immediate: true, deep: true },
);

const playMockRankAnimation = () => {
    if ($is_run_local()) {
        updateRankList(mockRankRounds[mockRankStep.value] ?? mockRankRounds[0] ?? []);
        mockRankStep.value = (mockRankStep.value + 1) % mockRankRounds.length;
    }
};

const startGame = () => {
    if ($is_run_local()) {
        emit('start');
    }
};

const randomMoleKind = (includeLand = true): MoleKind => {
    const kinds: MoleKind[] = includeLand ? ["land", "mole", "rabbit"] : ["mole", "rabbit"];
    return kinds[Math.floor(Math.random() * kinds.length)] ?? "land";
};

const clearMoleTimer = (index: number) => {
    const timer = moleTimers[index];
    if (timer) {
        clearTimeout(timer);
        moleTimers[index] = undefined;
    }
};

const setMoleCell = (index: number, kind: MoleKind, phase: MolePhase) => {
    const next = [...moleCells.value];
    const current = next[index];
    next[index] = {
        kind,
        phase,
        animationKey: (current?.animationKey ?? 0) + 1,
    };
    moleCells.value = next;
};

const scheduleMoleCell = (index: number, kind = randomMoleKind()) => {
    clearMoleTimer(index);

    if (kind === "land") {
        setMoleCell(index, "land", "land");
        moleTimers[index] = setTimeout(() => scheduleMoleCell(index, randomMoleKind(false)), MOLE_LAND_DURATION);
        return;
    }

    setMoleCell(index, kind, "show");
    moleTimers[index] = setTimeout(() => {
        setMoleCell(index, kind, "hide");
        moleTimers[index] = setTimeout(() => scheduleMoleCell(index, "land"), MOLE_HIDE_DURATION);
    }, MOLE_SHOW_DURATION);
};

const moleCellClass = (cell: MoleCell) => [
    `mole-${cell.kind}`,
    `mole-${cell.phase}`,
];

onMounted(() => {
    moleCells.value.forEach((_, index) => {
        moleTimers[index] = setTimeout(
            () => scheduleMoleCell(index, randomMoleKind()),
            Math.floor(Math.random() * MOLE_LAND_DURATION),
        );
    });
});

onBeforeUnmount(() => {
    moleTimers.forEach((_, index) => clearMoleTimer(index));
});

// const stopPlayerGu = () => {
//     if (guTimer) {
//         clearTimeout(guTimer);
//         guTimer = undefined;
//     }
//     if (guBurstTimer) {
//         clearTimeout(guBurstTimer);
//         guBurstTimer = undefined;
//     }
//     guBurstVisible.value = false;
//     guFrameIndex.value = 0;
// }

// const preloadGuFrames = () => {
//     if (!guFramesReady) {
//         guFramesReady = Promise.all(guFrames.flat().map((src) => {
//             const img = new Image();
//             img.src = src;
//             return img.decode
//                 ? img.decode().catch(() => undefined)
//                 : new Promise<void>((resolve) => {
//                     img.onload = () => resolve();
//                     img.onerror = () => resolve();
//                 });
//         })).then(() => undefined);
//     }

//     return guFramesReady;
// }

// const playGuLoopDelayAnimation = () => {
//     if (guBurstTimer) {
//         clearTimeout(guBurstTimer);
//         guBurstTimer = undefined;
//     }

//     guBurstVisible.value = false;
//     requestAnimationFrame(() => {
//         guBurstKey.value += 1;
//         guBurstVisible.value = true;
//         guBurstTimer = setTimeout(() => {
//             guBurstVisible.value = false;
//             guBurstTimer = undefined;
//         }, GU_LOOP_DELAY);
//     });
// }

// const playerGu = async () => {
//     stopPlayerGu();
//     const frameCount = Math.min(...guFrames.map((frames) => frames.length));
//     if (frameCount <= 0) return;

//     await preloadGuFrames();

//     let frameIndex = 0;
//     const tick = () => {
//         guFrameIndex.value = frameIndex;

//         if (frameIndex >= frameCount - 1) {
//             frameIndex = 0;
//             playGuLoopDelayAnimation();
//             guTimer = setTimeout(tick, GU_LOOP_DELAY);
//             return;
//         }

//         frameIndex += 1;
//         guTimer = setTimeout(tick, GU_FRAME_DURATION);
//     }

//     tick();
// }

const startIntro = async () => {
    // playerGu();
    await designStage.value?.startCountdown(START_COUNTDOWN_SECONDS, 60);
    // stopPlayerGu();
};

defineExpose({
    startIntro,
});

const renderAvatar = (item: any | null) => {
    if (item && item.avatar) {
        return `background: url("${String(item.avatar).replace(/"/g, '\\"')}") center / cover no-repeat;`
    }
    return `background: ${imageUrls.avatar} center / cover no-repeat;`
}

const rankIcon = (index: number) => {
    const icons = [
        imageUrls.rankNo1,
        imageUrls.rankNo2,
        imageUrls.rankNo3,
    ];
    let w = 42;
    let h = 47;
    let l = -20;
    let t = -20;
    if (index <= 2) {
        w = 65;
        h = 48;
        l = -30;
        t = -20;
    }
    return {
        background: `${icons[index] ?? imageUrls.rankNo4} center / cover no-repeat`,
        width: w + 'px',
        height: h + 'px',
        left: l + 'px',
        top: t + 'px',
        lineHeight: h + 'px',
        color: index <= 2 ? 'white' : '#CC7F36',
    };
}
</script>

<template>
    <DesignStage ref="designStage">
        <div class="bg">
            <div class="img-logo"></div>
            <div class="img-title"></div>
            <div class="img-mole"></div>
            <div class="rank-container">
                <div v-for="(item, index) in list" :key="playerKey(item, index)">
                    <div>
                        <div class="rank-icon" :style="rankIcon(index)">{{ index + 1 }}</div>
                        <div class="rank-item">
                            <div>
                                <div class="img-avatar-container">
                                    <div class="img-avatar" :style="`${renderAvatar(item)}`"></div>
                                </div>
                                <div class="txt-nickname">{{ $format_str(item.nickname, 8) }}</div>
                                <div class="txt-score">{{ item.score }}</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="mole-container">
                <div v-for="(cell, index) in moleCells" :key="index" class="mole-hole" :class="moleCellClass(cell)">
                    <div :key="cell.animationKey" class="mole-sprite"></div>
                </div>
            </div>
        </div>
    </DesignStage>
</template>

<style scoped>
.bg {
    width: 1920px;
    height: 1080px;
    background: v-bind('imageUrls.bg') center / cover no-repeat;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
}

.img-logo {
    background: v-bind('imageUrls.logo');
    background-size: cover;
    width: 428px;
    height: 77px;
    position: absolute;
    left: 37px;
    top: 82px;
}

.img-title {
    background: v-bind('imageUrls.title') center / cover no-repeat;
    width: 675px;
    height: 281px;
    margin: 0 auto;
    margin-top: 37px;
}

.img-mole {
    background: v-bind('imageUrls.mole2') center / cover no-repeat;
    width: 189px;
    height: 228px;
    position: absolute;
    left: 144px;
    top: 141px;
}

.rank-container {
    background: v-bind('imageUrls.rankContainer') center / cover no-repeat;
    display: grid;
    grid-template-columns: repeat(10, minmax(0, 1fr));
    align-items: center;
    column-gap: 42px;
    box-sizing: border-box;
    padding: 30px;
    width: 1832px;
    height: 244px;
    position: absolute;
    left: 44px;
    top: 348px;

    >div {
        box-sizing: border-box;
        text-align: center;
        width: 100%;
        height: 100%;

        >div {
            box-sizing: border-box;
            width: 100%;
            height: 100%;
            background: #A10A06;
            border-radius: 5px;
            position: relative;

            .rank-icon {
                position: absolute;
                color: #CC7F36;
                font-weight: bold;
            }

            .rank-icon:nth-child(1),
            .rank-icon:nth-child(2),
            .rank-icon:nth-child(3) {
                color: white;
            }
        }
    }



    .img-avatar-container {
        position: relative;
        margin: 0 auto;
        width: 90px;
        height: 90px;
        border-radius: 36px;
        /* transform: scale(1.3); */
        top: 18px
    }

    .img-avatar-container::before {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        background: v-bind('imageUrls.avatarAnimation');
        animation: avatar-rotate 10s linear infinite;
    }

    .img-avatar {
        width: 80px;
        height: 80px;
        border-radius: 40px;
        position: absolute;
        left: 6px;
        top: 5px;
    }

    .txt-nickname {
        position: absolute;
        bottom: 40px;
        text-align: center;
        width: 100%;
        color: #F9EBCB;
    }

    .txt-score {
        position: absolute;
        bottom: 5px;
        text-align: center;
        width: 100%;
        color: #F9EBCB;
    }
}

.mole-container {
    position: absolute;
    top: 200px;
    /* inset: 0; */
    pointer-events: none;

    .mole-hole {
        position: absolute;
        width: 500px;
        height: 500px;
        transform: translate(-125px, -125px);
        overflow: hidden;

        .mole-sprite {
            width: 500px;
            height: 500px;
            background-repeat: no-repeat;
            background-position: 0 0;
            background-size: auto 500px;
        }

        &.mole-land .mole-sprite {
            background: v-bind('imageUrls.land') center / cover no-repeat;
        }

        &.mole-mole.mole-show .mole-sprite {
            background-image: v-bind('imageUrls.moleShow');
            animation: mole-show 1.2s steps(30) both;
        }

        &.mole-mole.mole-hide .mole-sprite {
            background-image: v-bind('imageUrls.moleHide');
            animation: mole-hide 0.65s steps(15) both;
        }

        &.mole-rabbit.mole-show .mole-sprite {
            background-image: v-bind('imageUrls.rabbitShow');
            animation: mole-show 1.2s steps(30) both;
        }

        &.mole-rabbit.mole-hide .mole-sprite {
            background-image: v-bind('imageUrls.rabbitHide');
            animation: mole-hide 0.65s steps(15) both;
        }

        &:nth-child(1) {
            left: 247px;
            top: 312px;
        }

        &:nth-child(2) {
            left: 547px;
            top: 300px;
        }

        &:nth-child(3) {
            left: 847px;
            top: 312px;
        }

        &:nth-child(4) {
            left: 1147px;
            top: 300px;
        }

        &:nth-child(5) {
            left: 1447px;
            top: 312px;
        }

        &:nth-child(6) {
            left: 397px;
            top: 480px;
        }

        &:nth-child(7) {
            left: 697px;
            top: 460px;
        }

        &:nth-child(8) {
            left: 997px;
            top: 460px;
        }

        &:nth-child(9) {
            left: 1297px;
            top: 480px;
        }
    }
}

@keyframes mole-show {
    from {
        background-position-x: 0;
    }

    to {
        background-position-x: -15000px;
    }
}

@keyframes mole-hide {
    from {
        background-position-x: 0;
    }

    to {
        background-position-x: -7500px;
    }
}
</style>
