<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 { $is_run_local, $is_local_mode } from '@/commons/utils.ts';
import { cssAssetUrl } from '@/commons/assets.ts';

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

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

const HORSE_COUNT = 6;
const HORSE_FRAME_COUNT = 91;
const HORSE_FRAME_SIZE = 150;
const HORSE_SPRITE_COLUMNS = 10;
const HORSE_FRAME_DURATION = 15;
const RACE_TICK_DURATION = 50;
const RACE_DURATION = 60_000;
const RANK_RUNNING_GAP = 135;
const RANK_FINISH_PROGRESS = [1, 0.86, 0.8, 0.74, 0.69, 0.65];
const RANK_SPEED_FACTORS = [1.08, 1.04, 1.01, 0.98, 0.95, 0.92];
const RACE_START_X = -225;
const TRACK_MAX_X = 1595;
const BASE_SPEED_PER_TICK = (TRACK_MAX_X - RACE_START_X) / (RACE_DURATION / RACE_TICK_DURATION);
const MAX_SPEED_PER_TICK = BASE_SPEED_PER_TICK * 1.25;
const MAX_ACCELERATION_PER_TICK = 0.035;

const designStage = ref<InstanceType<typeof DesignStage> | null>(null);
const horseFrameIndex = ref(0);
const raceElapsed = ref(0);
const raceStarted = ref(false);
const rankedPlayerKeys = ref<string[]>([]);
const testRankList = ref<Player[]>([]);
let raceAnimationFrame: number | undefined;
let raceStartTimer: ReturnType<typeof setTimeout> | undefined;
let previousAnimationTime = 0;
let horseFrameElapsed = 0;

type Racer = {
    key: string;
    lane: number;
    horseType: number;
    player: Player;
    x: number;
    speed: number;
    paceAmplitude: number;
    paceFrequency: number;
    pacePhase: number;
};
const START_COUNTDOWN_SECONDS = 3;
const racers = ref<Racer[]>([]);

const imageUrls = {
    bg: cssAssetUrl('game2/bg2.png'),
    logo: cssAssetUrl('game1/logo.png'),
    title: cssAssetUrl('game2/title.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 horseSprites = [
    cssAssetUrl('game2/horse1.png'),
    cssAssetUrl('game2/horse2.png'),
    cssAssetUrl('game2/horse3.png'),
];

const preloadHorseSprites = () => {
    horseSprites.forEach((sprite) => {
        const image = new Image();
        image.src = sprite.slice(5, -2);
    });
};

const playerKey = (player: Player, index = 0) => {
    // 优先使用 userid/wechat_id，没有则用 avatar 作为唯一标识
    const id = player.userid || player.wechat_id || player.telephone;
    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 hashKey = (key: string) => Array.from(key).reduce(
    (hash, character) => ((hash << 5) - hash + character.charCodeAt(0)) | 0,
    0,
);

const createPaceProfile = (key: string) => {
    const hash = Math.abs(hashKey(key));

    return {
        paceAmplitude: 25 + hash % 35,
        paceFrequency: 1.5 + (hash % 5) * 0.2,
        pacePhase: (hash % 360) * Math.PI / 180,
    };
};

const isRealPlayer = (player: Player) => {
    if (!player) return false;
    
    // 检查 wechat_id 是否为空占位
    if (String(player.wechat_id || '').startsWith('empty-')) return false;
    
    // 检查昵称是否有效
    if (!player.nickname || player.nickname === '-' || player.nickname === '虚位以待') return false;
    
    // 如果有 userid/wechat_id/telephone，认为是真实玩家
    if (player.userid || player.wechat_id || player.telephone) return true;
    
    // 如果有头像，也认为是真实玩家（WebSocket 数据可能没有 ID 字段）
    if (player.avatar) return true;
    
    return false;
};


const targetXForRacer = (racer: Racer, rankIndex: number) => {
    const safeRankIndex = Math.max(0, Math.min(rankIndex, HORSE_COUNT - 1));
    const progress = Math.min(raceElapsed.value / RACE_DURATION, 1);
    const runningGap = RANK_RUNNING_GAP * Math.sin(Math.PI * progress);
    const rankedDistance = (TRACK_MAX_X - RACE_START_X)
        * RANK_FINISH_PROGRESS[safeRankIndex]!
        * progress;
    const paceFade = Math.sin(Math.PI * progress);
    const paceOffset = Math.sin(
        racer.pacePhase + progress * Math.PI * 2 * racer.paceFrequency,
    ) * racer.paceAmplitude * paceFade;

    return RACE_START_X
        + rankedDistance
        - safeRankIndex * runningGap
        + paceOffset;
};

const createRandomLanesByRank = () => {
    // 每项表示从顶部到底部的名次索引，均为明显的前后交替排列。
    const ranksByLaneTemplates = [
        [5, 0, 4, 1, 3, 2],
        [4, 1, 5, 0, 3, 2],
        [3, 0, 5, 1, 4, 2],
        [5, 1, 3, 0, 4, 2],
        [4, 0, 3, 1, 5, 2],
        [2, 5, 0, 4, 1, 3],
    ];
    const ranksByLane = ranksByLaneTemplates[
        Math.floor(Math.random() * ranksByLaneTemplates.length)
    ]!;

    return Array.from(
        { length: HORSE_COUNT },
        (_, rankIndex) => ranksByLane.indexOf(rankIndex),
    );
};

let lanesByRank = createRandomLanesByRank();

const syncRacers = (rankList: Player[]) => {
    console.log('[game2 Rank1View] syncRacers 收到 rankList:', rankList.length, '条', rankList.slice(0, 3).map(p => ({ nickname: p.nickname, userid: p.userid, wechat_id: p.wechat_id })));
    const topPlayers = rankList.filter(isRealPlayer).slice(0, HORSE_COUNT);
    console.log('[game2 Rank1View] syncRacers 过滤后 topPlayers:', topPlayers.length, '条', topPlayers.map(p => p.nickname));
    const rankedEntries = topPlayers.map((player, index) => ({
        key: playerKey(player, index),
        player,
    }));
    const topKeys = rankedEntries.map(({ key }) => key);
    const topPlayerMap = new Map(
        rankedEntries.map(({ key, player }) => [key, player]),
    );
    const previousRacersByLane = new Map(
        racers.value.map((racer) => [racer.lane, racer]),
    );

    const retained = racers.value
        .filter((racer) => topPlayerMap.has(racer.key))
        .map((racer) => ({ ...racer, player: topPlayerMap.get(racer.key)! }));
    const retainedKeys = new Set(retained.map((racer) => racer.key));
    const availableLanes = Array.from(
        { length: HORSE_COUNT },
        (_, lane) => lane,
    ).filter((lane) => !retained.some((racer) => racer.lane === lane));
    const freeLanes = racers.value.length === 0 ? lanesByRank : availableLanes;
    const newcomers = rankedEntries
        .filter(({ key }) => !retainedKeys.has(key))
        .map(({ key, player }, index) => {
            const previousRacer = previousRacersByLane.get(freeLanes[index]!);

            return {
                key,
                player,
                lane: freeLanes[index]!,
                horseType: previousRacer?.horseType ?? freeLanes[index]! % horseSprites.length,
                x: previousRacer?.x ?? RACE_START_X,
                speed: previousRacer?.speed ?? 0,
                ...createPaceProfile(key),
            };
        });

    racers.value = [...retained, ...newcomers]
        .sort((a, b) => a.lane - b.lane);
    rankedPlayerKeys.value = topKeys;
    console.log('[game2 Rank1View] syncRacers 最终 racers:', racers.value.length, '匹马');
};

const updateRacePositions = (tickScale = 1) => {
    if (raceElapsed.value >= RACE_DURATION) {
        racers.value.forEach((racer) => {
            racer.speed = 0;
        });
        return;
    }

    racers.value.forEach((racer) => {
        const rankIndex = rankedPlayerKeys.value.indexOf(racer.key);
        const effectiveRank = rankIndex >= 0 ? rankIndex : HORSE_COUNT - 1;
        const targetX = targetXForRacer(racer, effectiveRank);
        const speedFactor = RANK_SPEED_FACTORS[effectiveRank] ?? RANK_SPEED_FACTORS.at(-1)!;
        const finishProgress = RANK_FINISH_PROGRESS[effectiveRank] ?? RANK_FINISH_PROGRESS.at(-1)!;
        const positionCorrection = Math.max(
            -0.35,
            Math.min((targetX - racer.x) * 0.008, 0.35),
        );
        const desiredSpeed = Math.max(
            0,
            Math.min(
                BASE_SPEED_PER_TICK * finishProgress * speedFactor + positionCorrection,
                MAX_SPEED_PER_TICK,
            ),
        );
        const speedDifference = desiredSpeed - racer.speed;
        const accelerationLimit = MAX_ACCELERATION_PER_TICK * tickScale;

        racer.speed += Math.max(
            -accelerationLimit,
            Math.min(speedDifference, accelerationLimit),
        );
        racer.x = Math.min(racer.x + Math.max(racer.speed, 0) * tickScale, TRACK_MAX_X);
    });
};

const animateRace = (time: number) => {
    if (!previousAnimationTime) {
        previousAnimationTime = time;
    }

    const delta = Math.min(time - previousAnimationTime, 100);
    previousAnimationTime = time;
    horseFrameElapsed += delta;

    if (horseFrameElapsed >= HORSE_FRAME_DURATION) {
        const elapsedFrames = Math.floor(horseFrameElapsed / HORSE_FRAME_DURATION);
        horseFrameIndex.value = (horseFrameIndex.value + elapsedFrames) % HORSE_FRAME_COUNT;
        horseFrameElapsed %= HORSE_FRAME_DURATION;
    }

    if (raceStarted.value) {
        raceElapsed.value = Math.min(raceElapsed.value + delta, RACE_DURATION);
        updateRacePositions(delta / RACE_TICK_DURATION);
    }

    raceAnimationFrame = window.requestAnimationFrame(animateRace);
};

const resetRace = (startImmediately = true) => {
    raceElapsed.value = 0;
    raceStarted.value = startImmediately;
    lanesByRank = createRandomLanesByRank();

    racers.value.forEach((racer) => {
        const rankIndex = rankedPlayerKeys.value.indexOf(racer.key);
        racer.lane = lanesByRank[Math.max(rankIndex, 0)]!;
        racer.x = RACE_START_X;
        racer.speed = 0;
    });
    racers.value.sort((a, b) => a.lane - b.lane);
};

const renderHorseFrame = (racer: Racer) => {
    const column = horseFrameIndex.value % HORSE_SPRITE_COLUMNS;
    const row = Math.floor(horseFrameIndex.value / HORSE_SPRITE_COLUMNS);

    return {
        backgroundImage: horseSprites[racer.horseType],
        backgroundPosition: `${-column * HORSE_FRAME_SIZE}px ${-row * HORSE_FRAME_SIZE}px`,
        transform: `translate3d(${racer.x}px, 0, 0) scale(1.5)`,
    };
};

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

const createTestPlayers = () => Array.from({ length: 7 }, (_, index): Player => ({
    userid: `test-player-${index + 1}`,
    wechat_id: `test-player-${index + 1}`,
    nickname: `测试玩家${index + 1}`,
    avatar: '',
    score: 700 - index * 100,
}));

const updateTestRank = () => {
    if (!$is_run_local()) return;

    if (testRankList.value.length === 0) {
        testRankList.value = createTestPlayers();
        syncRacers(testRankList.value);
        resetRace();
        return;
    }

    const player = testRankList.value[Math.floor(Math.random() * testRankList.value.length)];
    if (player) {
        player.score = Number(player.score || 0) + 80 + Math.floor(Math.random() * 180);
    }

    testRankList.value = [...testRankList.value].sort(
        (a, b) => Number(b.score || 0) - Number(a.score || 0),
    );
    syncRacers(testRankList.value);
};

onMounted(() => {
    preloadHorseSprites();
    raceAnimationFrame = window.requestAnimationFrame(animateRace);
    
    // 本地模拟已关闭，如需开启请取消以下注释
    // if ($is_local_mode()) {
    //     const mockAvatars = [
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Felix',
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Aneka',
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Salem',
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Midnight',
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Lucky',
    //         'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Bandit',
    //     ];
    //     const mockData: Player[] = [
    //         { userid: 'local-1', nickname: '疾风骑士', avatar: mockAvatars[0], score: 45 },
    //         { userid: 'local-2', nickname: '闪电侠', avatar: mockAvatars[1], score: 38 },
    //         { userid: 'local-3', nickname: '追风者', avatar: mockAvatars[2], score: 30 },
    //         { userid: 'local-4', nickname: '千里马', avatar: mockAvatars[3], score: 22 },
    //         { userid: 'local-5', nickname: '飞毛腿', avatar: mockAvatars[4], score: 15 },
    //         { userid: 'local-6', nickname: '烈焰驹', avatar: mockAvatars[5], score: 52 },
    //     ];
    //     syncRacers(mockData);
    //     
    //     const growthRates: Record<string, number> = {};
    //     mockData.forEach((p, i) => {
    //         growthRates[p.userid!] = 0.4 + (i / (mockData.length - 1)) * 1.4;
    //     });
    //     
    //     let simInterval: ReturnType<typeof setInterval> | undefined;
    //     watch(raceStarted, (running) => {
    //         if (simInterval) { clearInterval(simInterval); simInterval = undefined; }
    //         if (running) {
    //             simInterval = setInterval(() => {
    //                 if (!raceStarted.value) { clearInterval(simInterval); simInterval = undefined; return; }
    //                 const updatedPlayers = racers.value.map(r => ({
    //                     ...r.player,
    //                     score: Number(r.player.score || 0) + Math.floor(Math.random() * 10 + 5),
    //                 }));
    //                 updatedPlayers.sort((a, b) => Number(b.score) - Number(a.score));
    //                 syncRacers(updatedPlayers);
    //             }, 1500);
    //         }
    //     }, { immediate: true });
    // }
});

onBeforeUnmount(() => {
    if (raceAnimationFrame !== undefined) {
        window.cancelAnimationFrame(raceAnimationFrame);
    }
    if (raceStartTimer) {
        clearTimeout(raceStartTimer);
    }
});

const startIntro = async () => {
    resetRace(false);
    const countdown = designStage.value?.startCountdown(START_COUNTDOWN_SECONDS, 60);

    raceStartTimer = setTimeout(() => {
        raceStarted.value = true;
        raceStartTimer = undefined;
    }, START_COUNTDOWN_SECONDS * 1000);

    await countdown;
};

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;`
}

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

</script>

<template>
    <DesignStage ref="designStage">
        <div class="screen-container" :class="{ paused: !raceStarted }">
            <div class="bg-track">
                <div class="bg-inner"></div>
                <div class="bg-inner"></div>
            </div>
            <div v-if="$is_run_local()" class="test-click-layer"></div>
            <div class="img-logo"></div>
            <div class="img-title"></div>

            <div class="horse-container">
                <div v-for="racer in racers" :key="racer.lane" class="horse-lane">
                    <div class="horse" :style="renderHorseFrame(racer)">
                        <div class="name-container">
                            <div class="nickname">{{ racer.player.nickname }}</div>
                            <div class="img-avatar" :style="renderAvatar(racer.player)"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </DesignStage>
</template>

<style scoped>
.screen-container {
    width: 1920px;
    height: 1080px;
    position: relative;
    overflow: hidden;
}

.screen-container.paused .bg-track {
    animation-play-state: paused;
}

.bg-track {
    width: 4716px;
    height: 1080px;
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    animation: bg-scroll-left 20s linear infinite;
    z-index: 0;
}

.bg-inner {
    width: 2358px;
    height: 1080px;
    background: v-bind('imageUrls.bg') center / cover no-repeat;
    flex-shrink: 0;
}

@keyframes bg-scroll-left {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(-2358px);
    }
}

.test-click-layer {
    position: absolute;
    inset: 0;
    z-index: 1;
}

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

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

.horse-container {
    position: absolute;
    bottom: 0px;
    left: 0;
    display: flex;
    flex-direction: column;
    width: 1920px;
    height: 500px;
    z-index: 2;
    pointer-events: none;
}

.horse-lane {
    position: relative;
    flex: 1;
    min-height: 0;
}

.horse {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 150px;
    height: 150px;
    background-repeat: no-repeat;
    background-size: 1500px 1500px;
    transform-origin: left bottom;
    backface-visibility: hidden;
    will-change: transform, background-position;
    transform-style: preserve-3d;

    .name-container {
        display: flex;
        align-items: center;
        gap: 8px;
        width: 150px;
        height: 42px;
        position: absolute;
        left: -70px;
        top: 35px;

        .nickname {
            max-width: 150px;
            padding: 4px 8px;
            color: #fff;
            background: rgb(0 0 0 / 55%);
            border-radius: 12px;
            text-overflow: ellipsis;
            white-space: nowrap;
            font-size: 20px;
        }

        .img-avatar {
            flex: 0 0 42px;
            width: 42px;
            height: 42px;
            border: 2px solid #fff;
            border-radius: 50%;
        }
    }
}
</style>
