import { _decorator, Asset, Component, Constructor, director, Node, ParticleSystem, Prefab, SceneAsset, setPropertyEnumType, SkeletalAnimation, Sprite, SpriteFrame, tween, UIOpacity, UITransform, utils, v2, v3, Vec3 } from 'cc';
import { Utils } from './Utils';
import { Constants } from '../data/Constants';
import { bundleMgr } from '../core/manager/BundleManager';
import { Logger } from '../extend/Logger';
import { PoolManager } from '../core/manager/PoolManager';
import { uiMgr } from '../core/manager/UIManager';
const { ccclass, property } = _decorator;
/**
 * 游戏道具类型枚举
 * @description 道具类型标识
 */
export enum ITEM_TYPE {
    Diamond = "diamond",//钻石
    Coin = "coin",//金币
    Boomerang = "boomerang",//飞镖
}

/**
 * 游戏特效类型枚举
 * @description 所有特效资源分类标识
 */
export enum EffectType {
    Bleeding = "effects/Prefabs/blood",    //流血效果（子弹命中敌人）
    BarrelExplosion = "effects/Prefabs/OilBoom", //油桶爆炸（基础爆炸）
    RockDebris = "effects/Prefabs/StoneBoom",// 沙堆/石礅碎裂烟雾
    VehicleSmoke = "effects/Prefabs/HeavySmoke",       // 坦克/汽车受损烟雾（浓烟）
    VehicleExplosion = "effects/Prefabs/TankBoom" // 坦克/汽车爆炸（强烈爆炸）
}

@ccclass('ResUtil')
export class ResUtil {
    /**
     * 设置精灵贴图 只能prefabs包 texture目录下
     * @param path 资源路径
     * 只能是在prefabs这个bundle目录下、然后如果使用
     * @param target 精灵或者节点
     * @param cb 回调函数
     */
    public static setSpriteFrame(name: string, target: Sprite | Node) {
        if(Utils.isNull(name))return;
        const path:string = `texture/${name}/spriteFrame`;
        bundleMgr.loadAsset(Constants.bundleName.prefabs,path,SpriteFrame, (err: Error, clip: SpriteFrame) => {
            if(!err) {
                let sprite: Sprite = target instanceof Node ? target.getComponent(Sprite) : target;
                if (!sprite) {
                    console.error('Target node does not have a Sprite component!');
                }else{
                    sprite.spriteFrame = clip;
                }
            }
        });
    }

    /**
     * 设置精灵贴图 指定自定义的包
     * @param bundle 包名如果传空 默认prefabs包名
     * @param path 路径 如果在prefabs包下，就可以直接传递传图片名称或者path路径(hit_m or texture/hit_m/spriteFrame)
     * 如果不在prefabs包下就只能传递完成的路径(texture/hit_m/spriteFrame)
     * @param type 类型
     * @param target 图片或者节点
     * @param onComplete 设置完成的回调
     */
    public static setSpriteFrameAsset(
        bundle: string,
        path: string,
        type: Constructor<Asset>,
        target: Sprite | Node,
        onComplete?: (err: Error | null, asset?: any) => void){
        if(Utils.isNull(bundle)){
            bundle = Constants.bundleName.prefabs;
            if(!path.startsWith("texture/")){
               path = `texture/${path}/spriteFrame`;
            }
        }
        if(Utils.isNull(path))return;
        bundleMgr.loadAsset(bundle,path,type, (err: Error, clip: SpriteFrame) => {
            if(!err) {
                let sprite: Sprite = target instanceof Node ? target.getComponent(Sprite) : target;
                if (!sprite) {
                    console.error('Target node does not have a Sprite component!');
                }else{
                    sprite.spriteFrame = clip;
                }
            }
            onComplete?.(err,clip);
        });
    }

    /**
     * 预加载场景资源（支持进度回调）
     * @param {string} sceneName - 场景名称（如 "main" 或者"level1"）
     * @param {Function} [onComplete] - 完成回调
     * @param {Function} [onProgress] - 加载进度回调
     * @param {Error | null} onProgress.err - 错误信息（成功时为 null）
     * @param {number} onProgress.progress - 加载进度（0~1）
     */
    public static preloadScene(
        sceneName: string,
        onComplete?: () => void,
        onProgress?: (err: Error | null, progress: number) => void
    ): void {
        if (Utils.isNull(sceneName)) {
            Logger.warn("[SceneLoader] Scene name cannot be null or empty!");
            return;
        }//开始预加载场景
        director.preloadScene(
            sceneName,
            (loadedCount: number, totalCount: number) => {//进度更新回调
                const progress = totalCount > 0 ? loadedCount / totalCount : 0;
                onProgress?.(null, progress); // 通知进度（未完成）
            },//加载完成回调
            (error: Error | null, sceneAsset?: SceneAsset) => {
                if (error) {
                    Logger.error(`[SceneLoader] Failed to preload scene "${sceneName}":`, error);
                    onProgress?.(error,0); // 通知失败
                    return;
                }
                onComplete?.(); 
                //预加载成功
                Logger.log(`[SceneLoader] Scene "${sceneName}" preloaded successfully`);
                onProgress?.(null, 1); 
            }
        );
    }

    /**
     * 只加载prefabs资源ui和页面 预制体
     * @param name 名字 只能是预知体
     * @param parent 父节点
     * @returns 返回一个节点
     */
    public static loadRes(path: string, parent?: Node): Promise<Node | null> {
        return new Promise(async (resolve, reject) => {
            try {
                if (Utils.isNull(path)) {
                    resolve(null);
                    return;
                }
                //加载资源 从对象池获取实例
                const res = await bundleMgr.loadAsset(Constants.bundleName.prefabs, path, Prefab) as Prefab;
                const n = PoolManager.getNode(res,parent) as Node;
                //返回结果
                resolve(n);
            } catch (error) {
                console.error(`加载资源失败: ${path}`, error);
                reject(new Error(`加载资源失败:: ${path}`));
            }
        });
    }

    /**
     * 购买金币和钻石的动画
     * @param type 
     * @param starNode 
     * @param targetNode 
     * @param count 
     * @param radius 
     * @param callback 
     */
    public static async flyAnim(type: ITEM_TYPE, starNode: Node, targetNode: Node, count: number, radius: number, callback: Function) {
        let getPoint = (r, ox, oy, count) => {
            var point = []; //结果
            var radians = (Math.PI / 180) * Math.round(360 / count), //弧度
                i = 0;
            for (; i < count; i++) {
                var x = ox + r * Math.sin(radians * i),
                    y = oy + r * Math.cos(radians * i);
                point.unshift(v2(x, y)); //为保持数据顺时针
            }
            return point;
        }
        let start = starNode.worldPosition;
        var array = getPoint(radius, start.x, start.y, count);
        var nodeArray = new Array();
        for (var i = 0; i < array.length; i++) {
            let ret: any = await bundleMgr.loadAsset(Constants.bundleName.prefabs, `items/${type}`, Prefab);
            var gold = PoolManager.getNode(ret);
            gold.setWorldScale(v3(0.7, 0.7, 0.7));
            gold.parent = uiMgr.getCurentSceneRoot();
            var randPos = v3(array[i].x + Utils.getRandomInt(0, 50), array[i].y + Utils.getRandomInt(0, 50), 1);
            gold.setWorldPosition(start);
            nodeArray.push({ gold, randPos });
        }
        var notPlay = false;
        let dstPos = targetNode.worldPosition.clone();
        for (let i = 0; i < nodeArray.length; i++) {
            let pos = nodeArray[i].randPos;
            let node = nodeArray[i].gold;
            nodeArray[i].gold.id = i;
            tween(node)
                .to(0.2, { worldPosition: pos })
                .delay(i * 0.03)
                .to(0.7, { worldPosition: v3(dstPos.x, dstPos.y, 1) })
                .call(() => {
                    if(!notPlay) {
                        notPlay = true;
                        tween(targetNode)
                            .to(0.1, { scale: v3(2, 2, 2) })
                            .to(0.1, { scale: v3(1, 1, 1) })
                            .call(() => {
                                notPlay = false;
                            }).start()
                    }
                    let idx: number = node["_id"];
                    callback(idx == nodeArray.length - 1);
                    PoolManager.putNode(node);
                })
                .start()
        }
    }

    /**
     * 加载特效
     * @param type 路径
     * @param worldPos 位置
     * @param parent 父节点
     * @param callback 回调函数
     */
    public static async loadEffect(type: EffectType,worldPos:Vec3,removeTime: number = 0, parent?: Node, callback?: Function){
        let res: Node = await ResUtil.loadRes(type,parent);
        if(worldPos){
           res.worldPosition = worldPos;
        }
        if(parent){
            res.getComponentsInChildren(ParticleSystem)
                .forEach(e => {
                    e.play();
                });
        }
        if(removeTime > 0){
            res.getComponent(UIOpacity).scheduleOnce(function(){
                this.getComponentsInChildren(ParticleSystem)
                    .forEach(e => {
                        e.stop();
                    });
                PoolManager.putNode(this);
            }.bind(res),removeTime,);
        }
    }

    /**
     * 播放骨骼动画
     * @param skeletal 骨骼动画
     * @param name 动画名称
     * @param time 播放时间 播放的时长会控制播放的速度快慢
     * @param cb 播放完成回调
     */
    public static playSkeletalAnim(skeletal: SkeletalAnimation,name: string,time: number = 0, cb: Function = null){
        if(!skeletal)return;
        //查找目标动画剪辑
        const clip = skeletal.clips.find(c => c.name == name);
        if (!clip) {
            Logger.error(`[ResUtil.playSkeletalAnim] 未找到名为 "${name}" 的动画剪辑`);
            return;
        }
        //核心逻辑：通过调整播放速度，使动画总时长等于传入的time
        //公式推导：原始时长 = 播放速度 × 实际播放时间 → 播放速度 = 原始时长 / 目标时长
        if(time > 0) {
            clip.speed = clip.duration / time; 
        }else{//恢复默认速度（避免之前的修改影响后续播放）
            clip.speed = 1;
        }
        skeletal.play(name);
        if(cb){
           skeletal.scheduleOnce(cb,time);
        }
    }
   
    /**
     *播放粒子 
     * @param path 粒子路径
     * @param time 粒子持续时间（自动回收时间，单位秒）
     * @param onSetup 加载完成后外部设置粒子属性的回调（接收粒子节点参数）
     * @param cb 播放完成回调
     */
    public static playParticle(
        path: string,
        time: number = 0, 
        onSetup?: (particle: Node) => void,
        cb?: Function
    ): void {
        if(Utils.isNull(path))return;
        ResUtil.loadRes(path)  // 使用传入的path参数加载资源
            .then((particle: Node | null) => {
                if(!particle)return;
                //触发外部设置回调（由外部设置parent、position等属性）
                onSetup?.(particle);
                //播放粒子（外部设置完成后执行）
                particle.getComponentsInChildren(ParticleSystem)
                    .forEach(e => {
                        e.stop();
                        e.play();
                    });
                //定时回收 外部设置完成后启动计时
                if(time > 0){
                    setTimeout(() => {
                        if(particle.parent){
                            PoolManager.putNode(particle);
                        }
                    }, time * 1000);
                }
                cb?.();  // 执行最终回调
            });
    }
}

