import { Asset, AssetManager, assetManager, resources } from 'cc';

/**
 * 资源管理器
 * 职责:
 * - 统一管理资源加载
 * - 提供从bundle中按路径加载资源的接口
 * - 支持资源预加载
 * - 管理资源缓存和释放
 */
export class ResMgr {
    private static _instance: ResMgr | null = null;
    
    /**
     * 资源缓存
     * key: bundleName:path
     * value: Asset
     */
    private _cache: Map<string, Asset>;
    
    /**
     * Bundle缓存
     * key: bundleName
     * value: AssetManager.Bundle
     */
    private _bundles: Map<string, AssetManager.Bundle>;
    
    /**
     * 构造函数(私有)
     */
    private constructor() {
        this._cache = new Map<string, Asset>();
        this._bundles = new Map<string, AssetManager.Bundle>();
        
        // 默认添加resources bundle
        this._bundles.set('resources', resources);
    }
    
    /**
     * 获取单例
     */
    static getInstance(): ResMgr {
        if (!this._instance) {
            this._instance = new ResMgr();
        }
        return this._instance;
    }
    
    /**
     * 获取Bundle
     * @param bundleName bundle名称
     */
    private getBundle(bundleName: string): AssetManager.Bundle | null {
        // 从缓存中获取
        if (this._bundles.has(bundleName)) {
            return this._bundles.get(bundleName)!;
        }
        
        // 使用 assetManager.getBundle() 方法获取已加载的 Bundle
        const bundle = assetManager.getBundle(bundleName);
        if (bundle) {
            this._bundles.set(bundleName, bundle);
            return bundle;
        }
        
        console.error(`[ResMgr] Bundle "${bundleName}" 不存在`);
        return null;
    }
    
    /**
     * 生成缓存key
     */
    private getCacheKey(bundleName: string, path: string): string {
        return `${bundleName}:${path}`;
    }
    
    /**
     * 加载单个资源
     * @param bundleName bundle名称
     * @param path 资源路径
     * @param type 资源类型
     * @returns Promise<资源>
     */
    load<T extends Asset>(
        bundleName: string,
        path: string,
        type: new (...args: any[]) => T
    ): Promise<T> {
        return new Promise((resolve, reject) => {
            const cacheKey = this.getCacheKey(bundleName, path);
            
            // 检查缓存
            if (this._cache.has(cacheKey)) {
                const cached = this._cache.get(cacheKey) as T;
                console.log(`[ResMgr] 从缓存加载资源: ${path}`);
                resolve(cached);
                return;
            }
            
            // 获取bundle
            const bundle = this.getBundle(bundleName);
            if (!bundle) {
                reject(new Error(`Bundle "${bundleName}" 不存在`));
                return;
            }
            
            // 加载资源
            console.log(`[ResMgr] 开始加载资源: ${bundleName}/${path}`);
            bundle.load(path, type, (err, asset) => {
                if (err) {
                    console.error(`[ResMgr] 加载资源失败: ${bundleName}/${path}`, err);
                    reject(err);
                    return;
                }
                
                // 缓存资源
                this._cache.set(cacheKey, asset as Asset);
                console.log(`[ResMgr] 资源加载成功: ${bundleName}/${path}`);
                resolve(asset as T);
            });
        });
    }
    
    /**
     * 预加载资源
     * @param bundleName bundle名称
     * @param path 资源路径
     * @param type 资源类型
     * @param onProgress 进度回调
     * @returns Promise<void>
     */
    preload<T extends Asset>(
        bundleName: string,
        path: string,
        type: new (...args: any[]) => T,
        onProgress?: (finished: number, total: number) => void
    ): Promise<void> {
        return new Promise((resolve, reject) => {
            // 获取bundle
            const bundle = this.getBundle(bundleName);
            if (!bundle) {
                reject(new Error(`Bundle "${bundleName}" 不存在`));
                return;
            }
            
            console.log(`[ResMgr] 开始预加载资源: ${bundleName}/${path}`);
            
            // 预加载资源
            bundle.preload(
                path,
                type,
                (finished, total) => {
                    if (onProgress) {
                        onProgress(finished, total);
                    }
                },
                (err) => {
                    if (err) {
                        console.error(`[ResMgr] 预加载资源失败: ${bundleName}/${path}`, err);
                        reject(err);
                        return;
                    }
                    
                    console.log(`[ResMgr] 资源预加载成功: ${bundleName}/${path}`);
                    resolve();
                }
            );
        });
    }
    
    /**
     * 加载目录
     * @param bundleName bundle名称
     * @param dir 目录路径
     * @param type 资源类型
     * @returns Promise<资源数组>
     */
    loadDir<T extends Asset>(
        bundleName: string,
        dir: string,
        type: new (...args: any[]) => T
    ): Promise<T[]> {
        return new Promise((resolve, reject) => {
            // 获取bundle
            const bundle = this.getBundle(bundleName);
            if (!bundle) {
                reject(new Error(`Bundle "${bundleName}" 不存在`));
                return;
            }
            
            console.log(`[ResMgr] 开始加载目录: ${bundleName}/${dir}`);
            
            // 加载目录
            bundle.loadDir(dir, type, (err, assets) => {
                if (err) {
                    console.error(`[ResMgr] 加载目录失败: ${bundleName}/${dir}`, err);
                    reject(err);
                    return;
                }
                
                // 缓存所有资源
                assets.forEach((asset) => {
                    const path = `${dir}/${asset.name}`;
                    const cacheKey = this.getCacheKey(bundleName, path);
                    this._cache.set(cacheKey, asset);
                });
                
                console.log(`[ResMgr] 目录加载成功: ${bundleName}/${dir}, 共 ${assets.length} 个资源`);
                resolve(assets as T[]);
            });
        });
    }
    
    /**
     * 释放资源
     * @param bundleName bundle名称
     * @param path 资源路径
     */
    release(bundleName: string, path: string): void {
        const cacheKey = this.getCacheKey(bundleName, path);
        
        // 从缓存中移除
        if (this._cache.has(cacheKey)) {
            const asset = this._cache.get(cacheKey)!;
            this._cache.delete(cacheKey);
            
            // 释放资源
            const bundle = this.getBundle(bundleName);
            if (bundle) {
                bundle.release(path);
                console.log(`[ResMgr] 释放资源: ${bundleName}/${path}`);
            }
        }
    }
    
    /**
     * 释放目录资源
     * @param bundleName bundle名称
     * @param dir 目录路径
     */
    releaseDir(bundleName: string, dir: string): void {
        // 释放所有以dir开头的缓存资源
        const prefix = `${bundleName}:${dir}`;
        const keysToDelete: string[] = [];
        
        this._cache.forEach((_, key) => {
            if (key.startsWith(prefix)) {
                keysToDelete.push(key);
            }
        });
        
        keysToDelete.forEach((key) => {
            this._cache.delete(key);
        });
        
        // 释放目录
        const bundle = this.getBundle(bundleName);
        if (bundle) {
            bundle.releaseAll();
            console.log(`[ResMgr] 释放目录资源: ${bundleName}/${dir}, 共 ${keysToDelete.length} 个资源`);
        }
    }
    
    /**
     * 释放所有资源
     */
    releaseAll(): void {
        console.log(`[ResMgr] 释放所有资源, 共 ${this._cache.size} 个`);
        this._cache.clear();
        
        // 释放所有bundle的资源
        this._bundles.forEach((bundle, name) => {
            if (name !== 'resources') {
                bundle.releaseAll();
            }
        });
    }
    
    /**
     * 获取缓存大小
     */
    getCacheSize(): number {
        return this._cache.size;
    }
    
    /**
     * 清空缓存(不释放资源)
     */
    clearCache(): void {
        console.log(`[ResMgr] 清空缓存, 共 ${this._cache.size} 个`);
        this._cache.clear();
    }
}
