import { AudioClip, AudioSource, director, Node, resources } from 'cc';

/** Unity Assets/Art/Audio → resources/audio/ */
const CLIPS = {
    background: 'audio/Backgroud',
    move: 'audio/Move',
    jump: 'audio/Jump',
    vehicleMove: 'audio/FlyingCarpetMove',
    fail: 'audio/Fail',
    success: 'audio/Success',
    coins: 'audio/GetCoins',
} as const;

type SfxKey = Exclude<keyof typeof CLIPS, 'background'>;

/**
 * 对齐 Unity：关卡加载时 curLevel 上循环播放 Backgroud.mp3；
 * 音效路径与 Player.prefab 一致。
 */
export class GameAudio {
    private static readonly cache = new Map<string, AudioClip>();
    private static readonly loading = new Map<string, Promise<AudioClip | null>>();
    private static sfxHost: Node | null = null;

    static async preload(): Promise<void> {
        await Promise.all(Object.values(CLIPS).map((p) => GameAudio.loadClip(p)));
    }

    static loadClip(path: string): Promise<AudioClip | null> {
        const hit = GameAudio.cache.get(path);
        if (hit) return Promise.resolve(hit);

        const pending = GameAudio.loading.get(path);
        if (pending) return pending;

        const task = new Promise<AudioClip | null>((resolve) => {
            resources.load(path, AudioClip, (err, clip) => {
                GameAudio.loading.delete(path);
                if (err || !clip) {
                    console.warn(`[GameAudio] 加载失败: ${path}`, err);
                    resolve(null);
                    return;
                }
                GameAudio.cache.set(path, clip);
                resolve(clip);
            });
        });
        GameAudio.loading.set(path, task);
        return task;
    }

    /** 在关卡根节点播放循环背景音乐（对齐 Unity createNewLevel） */
    static async playBackground(levelRoot: Node): Promise<void> {
        if (!levelRoot?.isValid) return;
        const clip = await GameAudio.loadClip(CLIPS.background);
        if (!clip || !levelRoot.isValid) return;

        let host = levelRoot.getChildByName('_BGM');
        if (!host) {
            host = new Node('_BGM');
            host.parent = levelRoot;
        }
        const src = host.getComponent(AudioSource) ?? host.addComponent(AudioSource);
        src.clip = clip;
        src.loop = true;
        src.playOnAwake = false;
        src.volume = 1;
        if (!src.playing) {
            src.play();
        }
    }

    static playSfx(key: SfxKey, host?: Node) {
        void GameAudio.playSfxAsync(key, host);
    }

    /** 对齐 Unity：同一 AudioSource 播放中则不重复触发移动/跳跃音效 */
    static async playSfxOnSource(src: AudioSource, key: SfxKey): Promise<boolean> {
        if (!src?.node?.isValid || src.playing) return false;
        const clip = await GameAudio.loadClip(CLIPS[key]);
        if (!clip) return false;
        src.clip = clip;
        src.loop = false;
        src.volume = 1;
        src.play();
        return true;
    }

    private static async playSfxAsync(key: SfxKey, host?: Node) {
        const clip = await GameAudio.loadClip(CLIPS[key]);
        if (!clip) return;

        const root = host?.isValid ? host : GameAudio.ensureSfxHost();
        if (!root?.isValid) return;

        const src = root.getComponent(AudioSource) ?? root.addComponent(AudioSource);
        src.playOneShot(clip, 1);
    }

    private static ensureSfxHost(): Node | null {
        if (GameAudio.sfxHost?.isValid) return GameAudio.sfxHost;

        const scene = director.getScene();
        if (!scene) return null;

        let host = scene.getChildByName('_GameSFX');
        if (!host) {
            host = new Node('_GameSFX');
            host.parent = scene;
        }
        GameAudio.sfxHost = host;
        return host;
    }

    /** 浏览器需用户交互后才能播放音频，首次点击 HUD 时恢复 */
    static resumeAll() {
        const scene = director.getScene();
        if (!scene) return;
        for (const src of scene.getComponentsInChildren(AudioSource)) {
            if (src.clip && !src.playing) src.play();
        }
    }
}
