import { assetManager, Prefab, resources } from 'cc';
import { PREVIEW } from 'cc/env';
import { ensureResourcesBundle } from '../core/ResourcesBundle';
import {
    getLevelPrefabUuid,
    loadLevelPrefabUuidIndex,
    parseLevelIdFromPrefabPath,
    shouldTryEditorUuidLoad,
} from './LevelPrefabUuidIndex';

const LEVEL_PREFAB_BUNDLE = 'level-prefabs';

let cachedLevelBundle: assetManager.Bundle | null = null;
let levelBundlePromise: Promise<assetManager.Bundle> | null = null;

function loadLevelPrefabBundle(): Promise<assetManager.Bundle> {
    if (cachedLevelBundle) return Promise.resolve(cachedLevelBundle);
    if (levelBundlePromise) return levelBundlePromise;
    levelBundlePromise = new Promise((resolve, reject) => {
        assetManager.loadBundle(LEVEL_PREFAB_BUNDLE, (err, bundle) => {
            levelBundlePromise = null;
            if (err || !bundle) {
                reject(err ?? new Error(`bundle "${LEVEL_PREFAB_BUNDLE}" unavailable`));
                return;
            }
            cachedLevelBundle = bundle;
            resolve(bundle);
        });
    });
    return levelBundlePromise;
}

function loadPrefabFromBundle(bundle: assetManager.Bundle, path: string): Promise<Prefab> {
    return new Promise((resolve, reject) => {
        bundle.load(path, Prefab, (err, prefab) => {
            if (!err && prefab) resolve(prefab);
            else reject(err ?? new Error(`missing prefab: ${path}`));
        });
    });
}

function loadPrefabFromResources(path: string): Promise<Prefab> {
    return new Promise((resolve, reject) => {
        resources.load(path, Prefab, (err, prefab) => {
            if (!err && prefab) resolve(prefab);
            else reject(err ?? new Error(`missing prefab: ${path}`));
        });
    });
}

function loadPrefabByUuid(uuid: string): Promise<Prefab> {
    return new Promise((resolve, reject) => {
        assetManager.loadAny({ uuid }, (err: Error | null, asset: Prefab) => {
            if (err || !asset) {
                reject(err ?? new Error(`missing prefab uuid: ${uuid}`));
                return;
            }
            resolve(asset);
        });
    });
}

/** bundle / resources 内可能的路径（子目录 level-prefabs/ 或 bundle 根下 LevelN） */
function prefabPathCandidates(path: string): string[] {
    const trimmed = path.trim();
    const base = trimmed.replace(/^level-prefabs\//, '');
    // bundle config 路径键为 level-prefabs/LevelN，裸 LevelN 会触发 loadAny 解析错误
    return [...new Set([trimmed, `level-prefabs/${base}`])];
}

async function loadFirstAvailable(
    loader: (p: string) => Promise<Prefab>,
    paths: string[],
): Promise<Prefab> {
    let lastErr: unknown;
    for (const p of paths) {
        try {
            return await loader(p);
        } catch (e) {
            lastErr = e;
        }
    }
    throw lastErr ?? new Error(`missing prefab: ${paths[0]}`);
}

async function tryLoadFromEditorUuid(path: string): Promise<Prefab | null> {
    if (!shouldTryEditorUuidLoad()) return null;
    await loadLevelPrefabUuidIndex();
    const levelId = parseLevelIdFromPrefabPath(path);
    if (levelId === undefined) return null;
    const uuid = getLevelPrefabUuid(levelId);
    if (!uuid) return null;
    try {
        const prefab = await loadPrefabByUuid(uuid);
        console.log(`[LevelPrefabLoader] 编辑器 uuid 加载 Level${levelId}`);
        return prefab;
    } catch (e) {
        console.warn(`[LevelPrefabLoader] uuid 加载 Level${levelId} 失败`, e);
        return null;
    }
}

/** 进关前由 loader 注入：按 levelId 下载对应关卡包 */
async function ensureLevelPackForPath(path: string): Promise<void> {
    const levelId = parseLevelIdFromPrefabPath(path);
    if (levelId === undefined) return;
    const hook = (globalThis as { __tfrhEnsureLevelPack?: (id: number) => Promise<void> }).__tfrhEnsureLevelPack;
    if (typeof hook === 'function') await hook(levelId);
    cachedLevelBundle = null;
    levelBundlePromise = null;
}

/** 优先从 level-prefabs 分包加载；编辑器预览可回退 uuid / resources */
export async function loadLevelPrefab(path: string): Promise<Prefab> {
    await ensureResourcesBundle();
    await ensureLevelPackForPath(path);
    const candidates = prefabPathCandidates(path);

    if (PREVIEW) {
        const byUuid = await tryLoadFromEditorUuid(path);
        if (byUuid?.isValid) return byUuid;
    }

    let bundleErr: unknown = null;
    try {
        const bundle = await loadLevelPrefabBundle();
        return await loadFirstAvailable((p) => loadPrefabFromBundle(bundle, p), candidates);
    } catch (err) {
        bundleErr = err;
        console.warn('[LevelPrefabLoader] level-prefabs bundle 加载失败', err);
    }

    if (!PREVIEW) {
        const byUuid = await tryLoadFromEditorUuid(path);
        if (byUuid?.isValid) return byUuid;
    }

    console.error(
        '[LevelPrefabLoader] 关卡预制体未找到。请确认 bundle-level-prefabs 已标记为 Asset Bundle '
        + `"${LEVEL_PREFAB_BUNDLE}"，并运行: python3 tools/bake_cocos_level_prefabs.py`,
        bundleErr,
    );
    throw bundleErr instanceof Error
        ? bundleErr
        : new Error(`missing prefab: ${path}`);
}
