import { Application, BackendSession, HandlerService, } from "pinus";
import { resResult, shouldRefresh, getRandSingleEelm, getGachaRemainFloor } from "../../../pubUtils/util";
import { STATUS, GACHA_TYPE, HERO_QUALITY_TYPE, TASK_TYPE, ITEM_CHANGE_REASON, } from "../../../consts";
import { gameData } from "../../../pubUtils/data";
import { UserGachaModel } from "../../../db/UserGacha";
import { refreshGacha, getGachaList, getVisitedHeroList, GachaPull, GachaResults, getDicGachaByGachaCnt, getNormalGachaId } from "../../../services/activity/gachaService";
import { RoleModel } from "../../../db/Role";
import { HeroModel } from "../../../db/Hero";
import { handleCost, addItems } from "../../../services/role/rewardService";
import { getZeroPointD, getTimeFun } from "../../../pubUtils/timeUtil";
import { UserGachaRecModel } from "../../../db/UserGachaRec";
import { RECRUIT } from "../../../pubUtils/dicParam";
import { getActivityById } from "../../../services/activity/activityService";
import { checkTaskInGacha } from "../../../services/task/taskService";
import { createHeroes } from "../../../services/role/createHero";
import { getGuideGachaData } from "../../../services/activity/gachaService";
import { getPlayerNewHeroGachaData } from "../../../services/activity/newHeroService";
import { isHeroHidden } from "../../../services/dataService";

export default function (app: Application) {
    new HandlerService(app, {});
    return new GachaHandler(app);
}

export class GachaHandler {
    constructor(private app: Application) {
    }

    /**
    * @description 获取抽卡列表 元宝招募、友情点招募、求贤若渴
    * @param {{}} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async getGachaList(msg: {}, session: BackendSession) {
        const { } = msg;
        const roleId: string = session.get('roleId');
        let role = await RoleModel.findByRoleId(roleId, 'gachaHasGuide');
        const list = await getGachaList(roleId);

        return resResult(STATUS.SUCCESS, { hasInit: !!role.gachaHasGuide, list });
    }

    /**
    * @description 抽卡，所有抽卡类型都
    * @param {{ gachaId: number, activityId: number, count: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async pull(msg: { gachaId: number, activityId: number, count: number }, session: BackendSession) {
        const { gachaId, activityId, count } = msg;
        const roleId: string = session.get('roleId');
        const roleName: string = session.get('roleName');
        const sid: string = session.get('sid');
        const serverId: number = session.get('serverId');
        
        let dicGacha = gameData.gacha.get(gachaId);
        if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let userGacha = await UserGachaModel.findByRole(roleId, gachaId, activityId);
        userGacha = await refreshGacha(dicGacha, userGacha);
        let { freeCount, point, pickHero, refFreeTime, count: historyCount } = userGacha;
        if ((dicGacha.gachaType == GACHA_TYPE.ASSIGN || dicGacha.gachaType == GACHA_TYPE.ACTIVITY) && !pickHero) return resResult(STATUS.GACHA_NOT_ASSIGN);

        dicGacha = getDicGachaByGachaCnt(dicGacha, historyCount);
        if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        if (!dicGacha.count.includes(count)) return resResult(STATUS.GACHA_IS_LOCK);
        
        let gachaPull = new GachaPull(dicGacha, userGacha);
        let userHeroes = await HeroModel.findByRole(roleId);
        let { resultList, heroInfo, items } = gachaPull.pull(count, userHeroes);
        let { hope, floor } = gachaPull.getUserGachaParam();

        let costNum = count;
        if (count == 1 && dicGacha.free.count > 0) { // 单抽的时候免费
            if (count > dicGacha.free.count - freeCount) {
                costNum = count - dicGacha.free.count + freeCount;
                freeCount = dicGacha.free.count;
            } else {
                costNum = 0;
                freeCount += count;
            }
        }
        // 消耗东西
        if (costNum > 0) {
            let cost = dicGacha.cost.map(cur => { return { id: cur.id, count: cur.count * costNum } });
            let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.GACHA_PULL);
            if (!costResult) return resResult(STATUS.GACHA_COST_NOT_ENOUGH);
        }
        // 给东西
        // console.log('****', heroInfo)
        let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo);
        await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS);
        // 更新数据
        userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, {
            freeCount, hope, floor, count: historyCount + count
        });
        let newDicGacha = getDicGachaByGachaCnt(dicGacha, userGacha.count);

        await UserGachaRecModel.createRec(roleId, gachaId, activityId, count, resultList);
        if(dicGacha.isTask) await checkTaskInGacha(serverId, roleId, sid, count, resultHeroes, resultList, false);
        return resResult(STATUS.SUCCESS, {
            gachaId, activityId,
            isFree: freeCount < newDicGacha.free.count, cost: newDicGacha.cost,
            count: userGacha.count, point: userGacha.point, floor, hope,
            // heroes: resultHeroes, 
            addHeros: heroes,
            result: resultList,
            remainFloor: getGachaRemainFloor(gachaId, floor)
        });
    }

    /**
    * @description 设置心愿单
    * @param {{ gachaId: number, hope: { id: number, hid: number }[] }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async setHope(msg: { gachaId: number, hope: { id: number, hid: number }[] }, session: BackendSession) {
        const { gachaId, hope } = msg;
        const roleId: string = session.get('roleId');

        for (let { hid } of hope) {
            if (hid != 0) {
                let dicRecruit = gameData.recruit.get(hid);
                if (!dicRecruit || !dicRecruit.canHope) {
                    return resResult(STATUS.GACHA_HOPE_NOT_GOLD);
                }
            }
        }
        let userGacha = await UserGachaModel.findByRole(roleId, gachaId, 0);
        let { hope: userHope = []} = await refreshGacha(gameData.gacha.get(gachaId), userGacha);
        for (let { id, hid } of hope) {
            let curHope = userHope.find(cur => cur.id == id);
            if(curHope) {
                if(curHope.hasGet) {
                    continue;
                } else {
                    curHope.hid = hid;
                    curHope.hasGet = false;
                }
            } else {
                userHope.push({ id, hid, hasGet: false })
            }
        }
        userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { hope: userHope });

        return resResult(STATUS.SUCCESS, {
            gachaId,
            hope: userGacha.hope
        });
    }

    /**
    * @description 转盘
    * @param {{ gachaId: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async drawTurnTable(msg: { gachaId: number }, session: BackendSession) {
        const { gachaId } = msg;
        const roleId: string = session.get('roleId');
        const roleName: string = session.get('roleName');
        const sid: string = session.get('sid');
        const serverId: number = session.get('serverId');
        

        let userGacha = await UserGachaModel.findByRole(roleId, gachaId, 0);
        let { point, turntable, costPoint } = userGacha;
        if (point < RECRUIT.RECRUIT_BONUS_RECRUIT) return resResult(STATUS.GACHA_TURNTABLE_POINT_NOT_ENOUGH);

        let turntablePool: { quality: number, count: number, planId: number }[] = [];
        for (let { quality, count, planId } of gameData.gachaTurntable) {
            let myTurntable = turntable.find(cur => cur.quality == quality);
            if (!myTurntable || !myTurntable.hasGet) {
                turntablePool.push({ quality, count, planId });
            }
        }
        if (turntablePool.length <= 0) { // 重置
            turntable = []; turntablePool = gameData.gachaTurntable;
        }

        let randTurntable = getRandSingleEelm(turntablePool);
        let { quality, count, planId } = randTurntable;

        let userHeroes = await HeroModel.findByRole(roleId);
        let gachaResults = new GachaResults();
        gachaResults.addPlan(planId);
        gachaResults.processDetail(null, count);
        let { items, heroInfo, resultList } = gachaResults.transferToFinalResult(userHeroes);
        
        // 给东西
        // console.log('****', heroInfo)
        let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo);
        await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS);
        // 更新数据
        await UserGachaRecModel.createRec(roleId, gachaId, 0, count, resultList);
        
        // 记录
        let myTurntable = turntable.find(cur => cur.quality == quality);
        if (!myTurntable) {
            myTurntable = { quality, hasGet: false };
            turntable.push(myTurntable);
        }
        myTurntable.hasGet = true;
        // 扣除积分
        userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { turntable, costPoint: costPoint + 1 });

        return resResult(STATUS.SUCCESS, {
            gachaId,
            point: userGacha.point,
            turntable: userGacha.turntable,
            result: resultList,
            addHeros: heroes,
            // heroes: resultHeroes
        });
    }


    /**
    * @description 求贤若渴&限时招募里面设置pick武将
    * @param {{ gachaId: number, pickHero: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async setPickHero(msg: { gachaId: number, activityId: number, pickHero: number }, session: BackendSession) {
        const { gachaId, activityId = 0, pickHero } = msg;
        const roleId: string = session.get('roleId');
        const serverId: number = session.get('serverId');

        if(isHeroHidden(pickHero)) return resResult(STATUS.HERO_IS_HIDDEN);

        let dicHero = gameData.hero.get(pickHero);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        if(activityId > 0) {
            let data = await getPlayerNewHeroGachaData(activityId, serverId, roleId);
            if(!data.isPickHero(pickHero)) return resResult(STATUS.GACHA_CAN_NOT_PICK);
        }

        let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { pickHero });

        return resResult(STATUS.SUCCESS, {
            gachaId,
            activityId,
            pickHero: userGacha.pickHero
        });

    }

    /**
    * @description 今天拜访过的武将
    * @param {{ }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async getVisitedHero(msg: {}, session: BackendSession) {
        const roleId: string = session.get('roleId');

        let visitedHero = await getVisitedHeroList(roleId);

        return resResult(STATUS.SUCCESS, {
            hids: visitedHero
        });
    }

    /**
    * @description 拜访武将
    * @param {{ hid: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
    async visitHero(msg: { hid: number }, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const roleName: string = session.get('roleName');
        const sid: string = session.get('sid');
        const { hid } = msg;

        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);
        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let dicRecruit = gameData.recruit.get(hid);
        if (!dicRecruit || !dicRecruit.canVisit) return resResult(STATUS.GACHA_CAN_NOT_PICK);
        let { pieceId } = dicHero;

        let gachaId = getNormalGachaId();
        let { visitedHero, refVisitedTime } = await UserGachaModel.findByRole(roleId, gachaId, 0);
        if (shouldRefresh(refVisitedTime, new Date())) {
            visitedHero = [];
            refVisitedTime = getZeroPointD();
        }
        if (visitedHero.includes(hid)) {
            return resResult(STATUS.GACHA_HAS_VISITED);
        }
        if (visitedHero.length >= RECRUIT.RECRUIT_DAILY_RECRUIT_SHARD) {
            return resResult(STATUS.GACHA_VISITED_COUNT_OVER);
        }

        visitedHero.push(hid);
        let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { visitedHero, refVisitedTime });
        let goods = await addItems(roleId, roleName, sid, [{ id: pieceId, count: RECRUIT.RECRUIT_SHARD_LIMIT }], ITEM_CHANGE_REASON.VISIT_HERO);

        return resResult(STATUS.SUCCESS, {
            hids: userGacha.visitedHero,
            goods
        });
    }

    /**
    * @description 获取特殊抽卡数据
    * @param {{}} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
     async getGuideGachaData(msg: { activityId: number }, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = session.get('serverId');

        const { activityId } = msg;
        if(!activityId) return resResult(STATUS.SUCCESS);

        let playerData = await getGuideGachaData(serverId, activityId, roleId);

        return resResult(STATUS.SUCCESS, { playerData });
    }

    /**
    * @description 预抽卡
    * @param {{}} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
     async guidePull(msg: { activityId: number; }, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = session.get('serverId');
        const sid: string = session.get('sid');
        const count = 10;

        const { activityId } = msg;
        let playerData = await getGuideGachaData(serverId, activityId, roleId);
        if(!playerData) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK);

        const gachaId = playerData.gachaId;
        const dicGacha = gameData.gacha.get(gachaId);
        if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        if(!playerData.canPull()) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK);

        if(playerData.pullCnt == 0) {
            // 消耗东西
            let cost = dicGacha.cost.map(cur => { return { id: cur.id, count: cur.count * count } });
            let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.GACHA_PULL);
            if (!costResult) return resResult(STATUS.GACHA_COST_NOT_ENOUGH);
        }

        let userHeroes = await HeroModel.findByRole(roleId);
        let gachaPull = new GachaPull(dicGacha);
        let { resultList } = gachaPull.pull(count, userHeroes);

        let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { guideResultList: resultList }, { pullCnt: 1 });
        playerData.setPlayerData(userGacha);
        if(playerData.autoSave()) { // 最后几次自动保存
            await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates });
        }

        return resResult(STATUS.SUCCESS, { 
            activityId,
            pullCnt: playerData.pullCnt,
            result: playerData.latest,
            candidates: playerData.candidates
        });
    }

    /**
    * @description 将结果保存到候选列表
    * @param {{ id: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
     async saveToCandidates(msg: { activityId: number, id: number }, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = session.get('serverId');

        const { activityId, id } = msg;
        let playerData = await getGuideGachaData(serverId, activityId, roleId);
        if(!playerData) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK);
        let gachaId = playerData.gachaId;
        const dicGacha = gameData.gacha.get(gachaId);
        if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let result = playerData.setCandidate(id);
        if(!result) return resResult(STATUS.GACHA_GUIDE_NOT_DO)

        let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates });
        playerData.setPlayerData(userGacha);

        return resResult(STATUS.SUCCESS, { 
            activityId,
            pullCnt: playerData.pullCnt,
            candidates: playerData.candidates
        });
    }

    /**
    * @description 确定使用某个结果
    * @param {{ id: number }} msg
    * @param {BackendSession} session
    * @memberof GachaHandler
    */
     async decide(msg: { activityId: number, id: number }, session: BackendSession) {
        const { activityId, id } = msg;
        const roleId: string = session.get('roleId');
        const roleName: string = session.get('roleName');
        const serverId: number = session.get('serverId');
        const sid: string = session.get('sid');
        const count = 10;

        let playerData = await getGuideGachaData(serverId, activityId, roleId);
        if(!playerData || !playerData.canDecide()) return resResult(STATUS.GACHA_GUIDE_HAS_DONE);
        let gachaId = playerData.gachaId;
        const dicGacha = gameData.gacha.get(gachaId);
        if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let candidate = playerData.chooseCandidate(id);
        if(!candidate) return resResult(STATUS.GACHA_GUIDE_NO_CANDIDATE);

        let userHeroes = await HeroModel.findByRole(roleId);
        let results = new GachaResults();
        results.addBySimpleResult(candidate.list);
        let { items, heroInfo, resultList } = results.transferToFinalResult(userHeroes);
        
        let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates });
        playerData.setPlayerData(userGacha);

        // 给东西
        let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo);
        await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS);
        // 更新数据
        await UserGachaRecModel.createRec(roleId, gachaId, 0, count, resultList);

        // 任务
        if(dicGacha.isTask) await checkTaskInGacha(serverId, roleId, sid, count, resultHeroes, resultList, true);

        return resResult(STATUS.SUCCESS, {
            activityId,
            pullCnt: playerData.pullCnt,
            addHeros: heroes,
            result: resultList
        });
    }

    async debugClearGachaRecord(msg: {}, session: BackendSession) {
        const roleId = session.get('roleId');
        let serverId = session.get('serverId');
        await UserGachaModel.deleteMany({ roleId });

       return resResult(STATUS.SUCCESS);
    }

    async test(msg: { gachaId: number, count: number, isGuide: boolean }, session: BackendSession) {
       const { gachaId, count, isGuide } = msg;
       const roleId: string = session.get('roleId');
       const sid: string = session.get('sid');
       const dicGacha = gameData.gacha.get(gachaId);
       console.log(`====== map of content ${gachaId} ====== start`)

       let mapOfContent = new Map<number, number>();

        let hope = [];
        let floor = [];

       let n = Math.ceil(count/10/200);
       let sum = 0;

        function cal(num: number) {
            for(let i = 0; i < 200; i++) {
                console.log(num, i)
                let gachaPull = new GachaPull(dicGacha, { ...new UserGachaModel().toJSON(), floor, hope, pickHero: 1 });
                 let { resultList } = gachaPull.pull(10, []);
                let { hope: _hope, floor: _floor } = gachaPull.getUserGachaParam();
                floor = _floor;
                hope = _hope;

                 gachaPull = undefined;
         
                 for(let { planId } of resultList ) {
                     if(!mapOfContent.has(planId)) {
                         mapOfContent.set(planId, 1);
                     } else {
                         mapOfContent.set(planId, mapOfContent.get(planId) + 1);
                     }
                     sum++;
                 }
            }
        }

        let num = 0;
        cal(num);
        let interval = setInterval(() => {
            if (++num < n) {
                cal(num);
            } else {
                console.log(`====== map of content ${gachaId} sum: ${sum} ======`)
                console.log('planId \t count')
                for(let [ planId, count ] of mapOfContent) {
                    console.log(`${planId} \t ${count}`);
                }
                clearInterval(interval);
            }
        }, 50)
 
      return resResult(STATUS.SUCCESS);
   }
}