import { _decorator, Asset, assetManager, AssetManager,Constructor, SpriteFrame} from 'cc';
import { Singleton } from './Singleton';
import { Logger } from '../../extend/Logger';
import { bundleConfig } from '../configs/BundleConfig';
const { ccclass, property } = _decorator;

/** wws
 * Bundle 中的单个资源定义
 */
/** wws
 * Bundle 中的单个资源定义
 */
export interface BundleAsset {
    /**资源路径（相对于Bundle根目录*/
    path: string;
    /**资源类型（可选）*/
    type?: Constructor<Asset>;
    /** 是否跳过加载（默认false*/
    skipLoading?: boolean;
    /** 是否是目录（默认false）*/
    isDirectory?: boolean;
}

/**Bundle 配置项*/
export interface BundleSetting {
    /**是否在游戏启动时加载 */
    loadAtLaunch: boolean;
    /**需要预加载的资源列表（当autoLoadAll为false时生效*/
    preloadAssets?: BundleAsset[];
    /** 是否自动加载Bundle内所有资源 */
    autoLoadAll?: boolean;
    /**要排除的资源路径前缀（当autoLoadAll为true时生效*/
    excludePaths?: string[];
    /**要排除的文件扩展名（如['.meta']*/
    excludeExtensions?: string[];
}

/**Bundle 配置表类型（Map结构） Key: Bundle名称 Value: Bundle配置*/
export type bundleConfig = Map<string, BundleSetting>;

/**加载进度信息*/
export interface LoadProgress {
    /**总加载步骤数*/
    totalSteps: number;
    /**已完成步骤数*/
    completedSteps: number;
    /**当前正在加载的Bundle名称*/
    currentBundle: string;
    /**当前正在加载的资源路径*/
    currentAsset: string;
    /**当前Bundle的加载进度（0-1）*/
    bundleProgress: number;
    /**总体加载进度（0-1）*/
    totalProgress: number;
}

/**
 * 进度回调函数类型
 * @param progress 当前加载进度信息
 */
export type ProgressCallback = (progress: LoadProgress) => void;

/**
 * 调用实例
 *  bundleMgr.preloadConfigAllRes((progress) => {
        const percent = Math.floor(progress.totalProgress * 100);
        this.loadUI.progressLabel.string = `加载进度: ${percent}%`;
        this.loadUI.tipLabel.string = `${progress.currentBundle} (${progress.completedSteps}/${progress.totalSteps})`;
        // 更新进度条
        this.progressBar.progress = progress.totalProgress;
        this.progressBar.progress = progress.bundleProgress;
        //调试信息（可选）
        Logger.log(`当前加载: ${progress.currentAsset}`);
    });
 */
@ccclass('BundleManager')
class BundleManager extends Singleton {
    //已加载的Bundle缓存（包含引用计数）
    private _bundles: Map<string, { bundle: AssetManager.Bundle; refCount: number }> = new Map();
    //资源结果缓存cache (path -> asset)
    private _assetCache: Map<string, Asset> = new Map();
    //Bundle配置表
    private _settings: bundleConfig = new Map();
    //当前加载进度状态
    private _loadProgress: LoadProgress = {
        totalSteps: 0,
        completedSteps: 0,
        currentBundle: '',
        currentAsset: '',
        bundleProgress: 0,
        totalProgress: 0
    };

    /**
     * 加载配置bundle下的所有资源
     * @param onProgress 加载的进度回调
     * @example 调用事例:
        bundleMgr.preloadConfigAllRes((progress) => {
        //更新UI进度显示
        const percent = Math.floor(progress.totalProgress * 100);
        this.loadUI.progressLabel.string = `加载进度: ${percent}%`;
        this.loadUI.tipLabel.string = `${progress.currentBundle} | ${progress.currentAsset} 
        (${progress.completedSteps}/${progress.totalSteps})`;
        //更新进度条
        this.progressBar.progress = progress.totalProgress;
        //this.progressBar.progress = progress.bundleProgress;
        //调试信息（可选）
        Logger.log(`当前加载: ${progress.currentBundle}` +  `${progress.currentAsset}`);
        if(progress.totalProgress == 1){
            this.loadComplete = true;
        }
    });
     */
    public async preloadConfigAllRes(
        onProgress?: ProgressCallback,
        config: bundleConfig = bundleConfig
    ): Promise<void> {
        //初始化Bundle配置
        this._settings = config;
        await this._calculateTotalSteps();
        //开始加载并显示进度
        await bundleMgr.loadLaunchBundles(onProgress);
    }

    /**
     * 加载启动时必须的Bundle（根据配置表中loadAtLaunch=true的配置）
     * @param onProgress 进度回调函数（可选）
     */
    public async loadLaunchBundles(onProgress?: ProgressCallback): Promise<void> {
        //重置进度状态
        this._resetProgress();
        //获取需要加载的Bundle配置
        const bundlesToLoad = Array.from(this._settings.entries())
            .filter(([_, setting]) => setting.loadAtLaunch);
        //顺序加载每个Bundle
        for (const [bundleName, setting] of bundlesToLoad) {
            this._loadProgress.currentBundle = bundleName;
            if(setting?.autoLoadAll) {
                await this._loadAutoBundle(bundleName, setting, onProgress);
            }else if (setting?.preloadAssets) {
                await this._loadConfiguredBundle(bundleName, setting.preloadAssets, onProgress);
            }
        }
    }

    async getBundleAssetList(bundleName: string): Promise<string[]> {
        // 远程 Bundle 路径
        const configUrl = `${bundleName}/config.json`;
        // 使用原生 XMLHttpRequest 或 fetch 获取 config.json
        const response = await fetch(configUrl);
        const config = await response.json();
        
        // 从 config.json 中提取资源路径
        if (config && config.packages && config.packages[0] && config.packages[0].pathMap) {
            return Object.keys(config.packages[0].pathMap);
        }
        return [];
    }
    
    /**
     * 计算总加载步骤数（用于进度计算）
     */
    private async _calculateTotalSteps(): Promise<void> {
        let total = 0;
        for (const [bundleName, setting] of this._settings) {
            if(setting?.loadAtLaunch){
                const bundle = await this.getBundle(bundleName);
                if(setting?.autoLoadAll) {//整个bundle包下的资源
                    if(bundle && bundle['config']?.paths?._map) {
                        total += Object.keys(bundle['config'].paths._map).length;
                    }
                }else if (setting?.preloadAssets) {//只统计需要加载的资源（skipLoading=false的）
                    let pathsToLoad = await this._expandDirectoryAssets(bundle, setting.preloadAssets);
                    total += pathsToLoad.length;
                }
            }
        }
        this._loadProgress.totalSteps = total;
    }

    /**
     * 自动加载Bundle内所有资源
     */
    private async _loadAutoBundle(
        bundleName: string,
        setting: BundleSetting,
        onProgress?: ProgressCallback
    ): Promise<void> {
        //加载Bundle
        const bundle = await this.getBundle(bundleName);
        //获取Bundle内所有资源路径（带类型信息）
        const assets = await this._getBundleAssets(bundle, {
            excludeExtensions: setting.excludeExtensions
        });
        //过滤时使用资源的path字段
        let filteredAssets = assets.filter(asset => 
            !setting.excludePaths?.some(exclude => asset.path.startsWith(exclude))
        );
        //直接传递完整资源对象（包含path和type）
        await this._loadBundleAssets(
            bundleName,
            filteredAssets, // 直接传递完整资源对象
            onProgress
        );
    }

    /**
     * 根据配置加载Bundle资源
     */
    private async _loadConfiguredBundle(
        bundleName: string,
        assets: BundleAsset[],
        onProgress?: ProgressCallback
    ): Promise<void> {
        //加载Bundle 处理目录资源
        const bundle = await this.getBundle(bundleName);
        //一个bundle下的目录资源
        let pathsToLoad = await this._expandDirectoryAssets(bundle, assets);
        //加载资源
        await this._loadBundleAssets(
            bundleName,
            pathsToLoad.map(asset => ({
                path: asset.path,
                ...(asset.type && { type: asset.type })
            })),
            onProgress
        );
    }

    /**
     * 加载指定Bundle
     * @param bundleName Bundle名称
     * @param onComplete 加载完成回调
     */
    public loadBundle(
        bundleName: string,
        onComplete?: (err: Error | null, bundle?: AssetManager.Bundle) => void
    ): void {
        //检查是否已加载
        const bundleInfo = this._bundles.get(bundleName);
        if (bundleInfo) {
            bundleInfo.refCount++;
            onComplete?.(null, bundleInfo.bundle);
            return;
        }
        //加载新Bundle
        assetManager.loadBundle(bundleName, (err, bundle) => {
            if (err) {
                console.error(`加载Bundle失败 [${bundleName}]:`, err);
                onComplete?.(err);
                return;
            }
            //缓存Bundle并设置引用计数
            this._bundles.set(bundleName, {
                bundle: bundle!,
                refCount: 1,
            });
            onComplete?.(null, bundle);
        });
    }

    /**
     * 获取Bundle内资源路径列表
     */
    private  _getBundleAssets(
        bundle: AssetManager.Bundle,
        options: {
            excludeFolders?: boolean;
            excludeExtensions?: string[];
        } = {}
    ): { path: string, type?: Constructor<Asset> }[] {
        const { excludeFolders = true, excludeExtensions = [] } = options;
        let assets: Array<{ path: string, type?: Constructor<Asset> }> = [];
        //更安全的类型断言和路径过滤
        const pathMap = bundle['_config']?.assetInfos?._map as Record<string, { 
            path: string;
            ctor?: Constructor<Asset>;
        }> | undefined;
        if(pathMap) {
            //过滤无效路径和带的衍生资源
            Object.values(pathMap).forEach(info => {
                if (info.path && !info.path.includes('@')) {
                    assets.push({
                        path: info.path,
                        type: info.ctor
                    });
                }
            });
        }
        //过滤处理 排除文件夹路径
        let result = assets.filter(asset => {
            //排除文件夹
            if(excludeFolders && asset.path.endsWith('/')) return false;
            //排除指定扩展名
            if (excludeExtensions?.length && excludeExtensions.some(ext => 
                asset.path.endsWith(ext)
            )) return false;
            return true;
        });
        return result;
    }

    /**
     * 加载Bundle内的多个资源
     */
    private async _loadBundleAssets(
        bundleName: string,
        assets: {path: string, type?: Constructor<Asset>}[],
        onProgress?: ProgressCallback,
    ): Promise<void> {
        const totalAssets = assets.length;
        if (totalAssets == 0) return;
        let loadedAssets = 0;
        //创建所有资源的加载Promise数组
        const loadPromises = assets.map(asset => {
            return new Promise<void>((resolve, reject) => {
                this.loadAsset(bundleName, asset.path, asset.type, (err) => {
                    if (err) {
                        console.error(`[${bundleName}] 加载资源失败: ${asset.path}`, err);
                        reject(err);
                        return;
                    }
                    loadedAssets++;
                    this._loadProgress.completedSteps++;
                    this._loadProgress.currentBundle = bundleName;
                    this._loadProgress.currentAsset = asset.path;
                    this._loadProgress.bundleProgress = loadedAssets / totalAssets;
                    this._loadProgress.totalProgress = Math.min(
                        this._loadProgress.completedSteps / this._loadProgress.totalSteps, 
                        1// 避免提前显示100%
                    );
                    this._updateProgress(onProgress);
                    resolve();
                });
            });
        });
        //等待所有资源加载完成
        await Promise.all(loadPromises)
            .catch(err => {
                console.error(`[${bundleName}] 部分资源加载失败`, err);
                throw err; // 重新抛出错误让上层处理
        });
    }

    /**调用事例
    await this.loadMultipleAssets("levels",
        [{ path: "map", type: Prefab },
        { path: "textures/trees", type: SpriteFrame },
        ],
        (progress) => {//直接更新UI进度条（0~1）
          this.loadingBar.progress = progress;
        }
        (err, asset, path) => {
            if (err) {
                Logger.error(`资源 ${path} 加载失败:`, err);
            } else {
                Logger.log(`资源 ${path} 加载成功:`, asset);
            }
        }
    );
    * 加载Bundle内的多个资源（仅计算当前Bundle的进度）
    * @param bundleName Bundle名称
    * @param assets 资源列表
    * @param onProgress 进度回调（当前Bundle的进度，范围0~1）
    */
    public async loadMultipleAssets(
        bundleName: string,
        assets: { path: string; type?: Constructor<Asset> }[],
        onProgress?: (progress: number) => void,
        onComplete?: (err: Error | null, asset?: Asset, path?: string) => void,
    ): Promise<void> {
        const totalAssets = assets.length;
        let loadedAssets = 0;
        for (const asset of assets) {
            await new Promise<void>((resolve) => {
                this.loadAsset(bundleName,asset.path,asset.type,(err, loadedAsset) => {
                    if (err) {
                        console.error(`加载资源失败 [${bundleName}] ${asset.path}:`, err);
                        onComplete?.(err, undefined, asset.path);
                    } else {
                        loadedAssets++;
                        const bundleProgress = loadedAssets / totalAssets;
                        onProgress?.(bundleProgress);
                        onComplete?.(null, loadedAsset, asset.path);
                    }
                    resolve();
                })
            });
        }
    }

    /**调用事例 
     await bundleMgr.loadAsset(bundleName,path,AudioClip,(err: Error, clip: AudioClip) => {
        if(err) {
            Logger.error("加载资源失败:", err);
            return;
        }});
     * 加载Bundle内的指定资源
     * @param bundleName Bundle名称
     * @param assetPath 资源路径
     * @param type 资源类型（可选）
     * @param onComplete 加载完成回调
     */
    public loadAsset<T extends Asset>(
        bundleName: string,
        assetPath: string,
        type: Constructor<T> | null,
        onComplete?: (err: Error | null, asset?: T) => void
    ): Promise<T> | void {
        const cacheKey = `${assetPath}-${type?.name}`;
        //从资源缓存中caches取
        const cachedAsset = this._assetCache.get(cacheKey);
        if (cachedAsset) {
            //console.log(`[Cache Hit] Using cached asset: ${cacheKey}`);
            onComplete?.(null, cachedAsset as T);
            return Promise.resolve(cachedAsset as T);
        }
        return new Promise((resolve, reject) => {
            this.loadBundle(bundleName, (err, bundle) => {
                if (err) {
                    onComplete?.(err);
                    reject(err);
                    return;
                }
                //特殊处理SpriteFrame的路径提示
                const isSpriteFrame = type && (type as any).name === 'SpriteFrame';
                if(isSpriteFrame && !assetPath.endsWith('/spriteFrame')) {
                    console.warn(
                        `SpriteFrame路径建议: ${assetPath} -> ${assetPath}/spriteFrame`,
                        `\n(请确认是否使用完整SpriteFrame路径)`
                    );
                }
                bundle!.load(assetPath, type,(err, asset) => {
                    if(err || !asset) {
                        console.error(`加载失败 [${bundleName}] ${assetPath}:`, err.message);
                        const warning = isSpriteFrame
                            ? `\n可能原因:\n1. 使用完整路径如 ${assetPath}/spriteFrame 类型为SpriteFrame\n2. 或者加载Texture: ${assetPath}/texture 类型为Texture`
                            : `\n请检查资源路径是否正确`;
                        console.warn(`空资源 [${bundleName}] ${assetPath}`, warning);
                        onComplete?.(err);
                        reject(err ?? new Error("Asset is null"));
                        return;
                    }
                    this._assetCache.set(cacheKey, asset);
                    console.log(`加载资源 ${assetPath} 成功: ${cacheKey} + 类型为: ${asset?.constructor.name}`);
                    onComplete?.(err, asset as T);
                    resolve(asset as T);
                });
            });
        });
    }

    /**
     * 释放Bundle
     * @param bundleName Bundle名称
     * @param force 是否强制释放（即使还有引用）
     */
    public releaseBundle(bundleName: string, force: boolean = false): void {
        const bundleInfo = this._bundles.get(bundleName);
        if (!bundleInfo) return;
        //减少引用计数
        bundleInfo.refCount--;
        // 当引用计数归零或强制释放时
        if (bundleInfo.refCount <= 0 || force) {
            assetManager.removeBundle(bundleInfo.bundle);
            this._bundles.delete(bundleName);
            console.log(`已释放Bundle: ${bundleName}`);
        }
    }

    /**
     * 获取已加载的Bundle实例
     * @param bundleName Bundle名称
     * @returns Bundle实例或null
     */
    public async getBundle(bundleName: string): Promise<AssetManager.Bundle | null> {
        //先从缓存中查找
        const cachedBundle = this._bundles.get(bundleName)?.bundle;
        if(cachedBundle) {
           return cachedBundle;
        }
        try {//如果缓存中没有，则加载bundle
            const bundle = await new Promise<AssetManager.Bundle>((resolve, reject) => {
                assetManager.loadBundle(bundleName, (err, bundle) => {
                    err ? reject(err) : resolve(bundle);
                });
            });
            return bundle;
        } catch (error) {
            console.error(`加载Bundle ${bundleName} 失败:`, error);
            return null;
        }
    }

    /**
     * 检查Bundle是否已加载
     * @param bundleName Bundle名称
     */
    public hasBundle(bundleName: string): boolean {
        return this._bundles.has(bundleName);
    }
    
    /**
     * 释放所有Bundle
     * @param force 是否强制释放
     */
    public releaseAll(force: boolean = false): void {
        this._bundles.forEach((_, name) => this.releaseBundle(name, force));
    }

    /**
     * 展开目录资源为具体资源列表
     * @param bundle Bundle实例
     * @param assets 原始资源列表
     */
    private async _expandDirectoryAssets(
        bundle: AssetManager.Bundle,
        assets: BundleAsset[]
    ): Promise<BundleAsset[]> {
        let result: BundleAsset[] = [];
        for(let idx = 0; idx < assets.length; idx++) {
            const asset = assets[idx];
            if (!asset?.isDirectory) {
                if(!asset?.skipLoading){
                    result.push(asset);
                }
            }else{//获取目录下所有资源路径
                const dirPath = asset.path.endsWith('/') ? asset.path : `${asset.path}/`;
                //获取带类型信息的资源列表
                const allPaths = await this._getBundleAssets(bundle, {
                    excludeFolders: true,
                    excludeExtensions: ['.meta']
                });
                //保留原始类型信息
                const dirResources = allPaths.filter(res => 
                    res.path.startsWith(dirPath) &&
                    !res.path.substring(dirPath.length).includes('/')
                );
                //转换时携带类型信息
                dirResources.forEach(res => {
                    result.push({
                        path: res.path,
                        type: res.type, // 保留类型信息
                        skipLoading: false
                    });
                });
            }
        }
        result = result.filter(asset => !asset?.skipLoading);
        return result;
    }

    /**
     * 触发进度回调
     */
    private _updateProgress(onProgress?: ProgressCallback): void {
        //传递进度状态的副本，避免外部修改
        onProgress?.({...this._loadProgress});
    }

    
    /**
     * 重置加载进度状态
     */
    private _resetProgress(): void {
        this._loadProgress = {
            totalSteps: this._loadProgress.totalSteps, // 保持总步骤数
            completedSteps: 0,
            currentBundle: '',
            currentAsset: '',
            bundleProgress: 0,
            totalProgress: 0
        };
    }

    
    /**
     * 明确指定元组类型
     * @param asset 未使用这个方法 之前在测试
     * { path: 'texture/msg_hint/spriteFrame', type: SpriteFrame },
       { path: 'texture/test_01/texture', type: Texture2D },
        这两种的时候有写到
     */
    private _getLoadArguments(asset: {
        path: string, 
        type?: Constructor<Asset>
    }): [string, Constructor<Asset>?] { // 返回元组类型
        if (asset.type === SpriteFrame) {
            return [asset.path,asset.type];
        }
        return asset.type ? [asset.path, asset.type] : [asset.path];
    }
}

//全局单例
export const bundleMgr = BundleManager.ins();