import { HeroModel } from './../db/Hero';
import { HangUpRecordModel } from './../db/HangUpRecord';
import { pinus } from 'pinus';
import { HANG_UP_CONSTS, TOWER_TASK_CONST, REDIS_KEY, TASK_TYPE, TIME_OUTPUT_TYPE, GOLD_COST_RATIO } from './../consts';
import { BattleRecordModel } from './../db/BattleRecord';
import { TowerRecordModel } from './../db/TowerRecord';
import { RoleModel, RoleType } from './../db/Role';
import { shouldRefresh, resResult, cal, getRandEelmWithWeight, calculateNum, genCode } from '../pubUtils/util';
import { STATUS } from '../consts/statusCode';
import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec';
import { TowerTaskRecModel } from '../db/TowerTaskRec';
import { cloneDeep } from 'lodash';
import { Rank } from './rankService';
import { checkActivityTask, checkTask } from './taskService';
import { getRandExpedition, gameData } from '../pubUtils/data';
import { ItemInter, RewardInter } from '../pubUtils/interface';
import { getTimeFunM } from '../pubUtils/timeUtil';

/**
 * 获取当前镇念塔状态
 * @param role 
 */
export async function getTowerStatus(role: RoleType) {
    let { towerLv, roleId, serverId } = role;
    if (!towerLv) {
        towerLv = 1;
        let role = await RoleModel.towerLvUp(roleId);
        // 更新redis
        let r = new Rank(REDIS_KEY.TOWER_RANK, { serverId });
        await r.setRankWithRoleInfo(roleId, towerLv, role.towerUpTime.getTime(), role);
    }
    let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv);
    if (!towerRec) {
        const towerInfo = gameData.tower.get(towerLv);
        const { warArray } = towerInfo;
        const sts = warArray.map(id => {
            return {warId: id, status: false};
        });
        towerRec = await TowerRecordModel.createRecord({roleId, lv: towerLv, warStatus: sts});
        // return { code: 201, data: '天梯记录异常' };
    }
    return {
        canHungUp: towerLv >= HANG_UP_CONSTS.ENABLE_LV,
        hungUpEnableLv: HANG_UP_CONSTS.ENABLE_LV,
        canSendTask: true,
        curLv: towerLv,
        usedHeroes: towerRec.heroes,
        progress: towerRec.warStatus
    };
}

/**
 * 获取镇念塔挂机收益
 * @param roleId 
 */
export async function getHungupRewards(roleId: string) {
    const result = await calcuHangUpReward(roleId);
    if(result.status == -1) {
        return result;
    }
    let {timeReward, startTime, deltaTime} = result.data;
    let {hangUpSpdUpCnt, lastSpdUpTime} = await RoleModel.findByRoleId(roleId);
    let curTime = new Date();
    if (!lastSpdUpTime || (shouldRefresh(lastSpdUpTime, curTime) && hangUpSpdUpCnt <= HANG_UP_CONSTS.MAX_SPD_UP_CNT)) {
        hangUpSpdUpCnt = HANG_UP_CONSTS.MAX_SPD_UP_CNT;
    }        
    let num = HANG_UP_CONSTS.MAX_SPD_UP_CNT - hangUpSpdUpCnt + 1;
    let nextCostGold = calculateNum(GOLD_COST_RATIO.TOWER_HANG_SPDUP, {num}, 50);

    return {
        status: 0,
        resResult: null,
        data: {
            startTime, hangUpPassTime: Math.floor(deltaTime/1000), hangUpSpdUpCnt, nextCostGold, rewards: timeReward
        }
    }
}

/**
 * 获取派遣任务列表
 * @param role 
 */
export async function getTasks(role: RoleType) {
    let { roleId, roleName, towerLv, towerTaskRefTime, towerTaskReCnt = 0 } = role;
    let curTime = new Date();
    let curTasks = await TowerTaskRecModel.getCurTasks(roleId); // 当前显示中的任务

    const needRefresh = shouldRefresh(towerTaskRefTime, curTime);

    if(needRefresh) {
        const batchCode = genCode(8);
        let {waitingTaskCode, doingTaskCode, doingIds} = getDoingOrWaitingTasks(curTasks, curTime);
        await TowerTaskRecModel.hideTask(roleId, waitingTaskCode); // 隐藏没有在做的任务
        await TowerTaskRecModel.updateBatchCode(roleId, doingTaskCode, batchCode); // 更新留下来的旧任务的batchCode
        await createCurTasks(towerLv, batchCode, roleId, roleName, doingTaskCode.length, TOWER_TASK_CONST.RAND_CNT-doingTaskCode.length, doingIds); // 新建任务
        curTasks = await TowerTaskRecModel.getCurTasks(roleId);

        // 重置派遣次数
        const role = await RoleModel.resetTowerCnt(roleId, curTime); 
        towerTaskReCnt = role.towerTaskReCnt;
    }

    let refRemainTime = getRemainTime(curTime);
    let nextCostGold = calculateNum(GOLD_COST_RATIO.TOWER_TASK_REF, {num: towerTaskReCnt + 1}, 50);

    return {curTasks: treatTask(curTasks, curTime), refRemainTime, nextCostGold}
}

export async function checkTowerWar(roleId: string, battleId: number, heroes: Array<number>) {
    let { towerLv } = await RoleModel.findByRoleId(roleId);
    const towerInfo = gameData.tower.get(towerLv);
    if (!towerInfo) {
        console.error(`天梯层数异常，lv ${towerLv} by ${roleId}`);
        return { status: -1, resResult: resResult(STATUS.TOWER_INFO_NOT_FOUND) };
    }
    const warIds = towerInfo.warArray;

    if (warIds.indexOf(battleId) === -1) {
        return { status: -1, resResult: resResult(STATUS.TOWER_WRONG_BATTLE_ID) };
    }

    const tower = await TowerRecordModel.getRecordByLv(roleId, towerLv);
    if (!tower) {
        return { status: -1, resResult: resResult(STATUS.TOWER_NOT_FOUND) };
    }
    let { heroes: recHeroes = [], warStatus = [] } = tower;
    for (let hid of heroes) {
        if (recHeroes.indexOf(hid) !== -1) {
            return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_HERO) };
        }
    }
    const curWarStatus = warStatus.find(elem => elem.warId == battleId);
    if (curWarStatus && curWarStatus.status) {
        return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_CHALLENGE) };
    }

    return {
        status: 0, data: {
            heroes: recHeroes, warStatus: curWarStatus
        }
    };
}

export async function towerBattleEnd(sid: string, funcs: number[], roleId: string, serverId: number, battleCode: string, battleId: number, succeed: boolean, heroes: Array<number>) {
    if (succeed) {
        let battleRec = await BattleRecordModel.getBattleRecordByCode(battleCode);
        if (battleRec.battleId != battleId) {
            return { status: -1, resResult: resResult(STATUS.BATTLE_ID_NOT_MATCH) };
        }
        let { towerLv, roleName } = await RoleModel.findByRoleId(roleId);
        let { warStatus, heroes: recHeroes } = await TowerRecordModel.getRecordByLv(roleId, towerLv);
        for (let hid of heroes) {
            if (recHeroes.indexOf(hid) !== -1) {
                return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_CHALLENGE) };
            }
        }

        let inc = 1;
        warStatus.forEach(st => {
            if (st.warId !== battleId && st.status === false) {
                inc = 0;
            }
        })
        let newRec = await TowerRecordModel.updateRecord(roleId, towerLv, battleCode, battleId, heroes, inc);
        let towerReward: RewardInter[] = null;
        if (inc === 1) {
            let role = await RoleModel.towerLvUp(roleId);
            // 更新redis
            let r = new Rank(REDIS_KEY.TOWER_RANK, { serverId });
            await r.setRankWithRoleInfo(roleId, towerLv + 1, role.towerUpTime.getTime(), role);

            const nextTowerInfo = gameData.tower.get(towerLv + 1);
            if (nextTowerInfo) {
                const { warArray } = nextTowerInfo;
                const sts = warArray.map(id => {
                    return { warId: id, status: false };
                });

                await TowerRecordModel.createRecord({ roleId, lv: towerLv + 1, warStatus: sts });

            }
            const { reward } = gameData.tower.get(towerLv);
            if (reward) towerReward = reward;
            if (towerLv + 1 == HANG_UP_CONSTS.ENABLE_LV) {
                await startHangUp(roleId, roleName);
                pinus.app.get('channelService').pushMessageByUids('hangUpEnable', resResult(STATUS.SUCCESS, { enable: true }), [{ uid: roleId, sid }]);
            }
            // 任务
            await checkTask(roleId, sid, funcs, TASK_TYPE.BATTLE_TOWER_LV, role.towerLv, false, {});
            await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.BATTLE_TOWER_LV, 1, { towerLv: role.towerLv });
        }
        return {
            status: 0,
            data: {
                newRec,
                towerReward,
                towerStatus: !!inc
            }
        };
    }
}

async function startHangUp(roleId: string, roleName: string) {
    await HangUpRecordModel.initRecord(roleId, roleName);
}

export async function checkHangUpSpdUpCnt(roleId: string, cnt: number, curTime: Date) {
    const role = await RoleModel.findByRoleId(roleId);
    let { hangUpSpdUpCnt, gold, lastSpdUpTime } = role;
    if (!lastSpdUpTime || (shouldRefresh(lastSpdUpTime, curTime) && hangUpSpdUpCnt <= HANG_UP_CONSTS.MAX_SPD_UP_CNT)) {
        hangUpSpdUpCnt = HANG_UP_CONSTS.MAX_SPD_UP_CNT;
    }
    if (cnt > hangUpSpdUpCnt) {
        return { status: -1, resResult: resResult(STATUS.TOWER_NOT_ENOUGH_HANG_UP_TIME) }
    }
    return { status: 0, data: { hangUpSpdUpCnt, gold, lastSpdUpTime } }
}

export async function calcuHangUpReward(roleId: string, speedUp = false, speedUpCnt = 1, curTime = new Date()) {
    let { towerLv = 1, hangUpSpdUpCnt = 0, lastSpdUpTime } = await RoleModel.findByRoleId(roleId);
    if (towerLv < HANG_UP_CONSTS.ENABLE_LV) {
        return { status: -1, resResult: resResult(STATUS.TOWER_HANG_UP_NOT_START) }
    }

    let towerInfo = gameData.tower.get(towerLv - 1); // towerLv 是当前层，奖励计算按照已经通过的层，即上一层
    let timeReward = []; // 奖励
    let needReceiveGoods = []; // 由于小数，未能领取的奖励
    let baseReward = towerInfo.rewardOfcollect;

    let { startTime } = await HangUpRecordModel.getCurRec(roleId);
    let endTime = curTime; // 挂机结束时间，现在到开始时间，经历了10的倍数的时间
    let deltaTime = curTime.getTime() - startTime.getTime(); // 累计的挂机时间，受最大时间限制
    if (deltaTime > HANG_UP_CONSTS.MAX_TIME) {
        deltaTime = HANG_UP_CONSTS.MAX_TIME;
        endTime = curTime; // 累积到超过24小时，那么结束时间和下一次开启时间就取整了
    } else {
        let multiReal = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME);// 距开始挂机实际过去的时间单位
        endTime = new Date(startTime.getTime() + multiReal * HANG_UP_CONSTS.UNIT_TIME)
    }

    if (speedUp) { // 加速，直接收取6小时收益，小数的累积和普通收取独立
        if (hangUpSpdUpCnt >= speedUpCnt || !lastSpdUpTime || shouldRefresh(lastSpdUpTime, new Date)) { // 可加速
            let multi = Math.floor(HANG_UP_CONSTS.SPD_UP_REC_TIME / HANG_UP_CONSTS.UNIT_TIME);
            let spdUpRec = await HangUpSpdUpRecModel.getSpdUpRec(roleId, towerLv - 1);
            let goods = [];
            if (spdUpRec) {
                let { notReceivedGoodsList = [], cnt } = spdUpRec;
                let notReceivedGoods = notReceivedGoodsList.find(cur => cur.cnt == cnt);
                goods = notReceivedGoods ? notReceivedGoods.goods : [];
            }
            for (let { id, count } of baseReward) {
                let newCount = cal.mul(count, multi);
                let oldGoods = goods.find(cur => cur.gid == id);
                if (oldGoods) newCount = cal.add(newCount, oldGoods.count);
                let roundCount = Math.floor(newCount);
                if (newCount > roundCount) {
                    needReceiveGoods.push({ gid: id, count: cal.sub(newCount, roundCount) });
                }
                if (roundCount > 0) {
                    timeReward.push({ id, count: roundCount })
                }
            }
        }
    } else {
        let lastRec = await HangUpRecordModel.getLastRec(roleId);
        let notReceivedGoods = lastRec ? lastRec.notReceivedGoods : [];

        let multi = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME); // 结算奖励的倍数，受最大时间限制

        // console.log(deltaTime, multi, baseReward);
        for (let { id, count } of baseReward) {
            let newCount = cal.mul(count, multi);
            let oldGoods = notReceivedGoods.find(cur => cur.gid == id);
            if (oldGoods) newCount = cal.add(newCount, oldGoods.count);
            let roundCount = Math.floor(newCount);
            if (newCount > roundCount) {
                needReceiveGoods.push({ gid: id, count: cal.sub(newCount, roundCount) });
            }
            if (roundCount > 0) {
                timeReward.push({ id, count: roundCount })
            }
        }
    }
    return {
        status: 0,
        data: {
            endLv: towerLv - 1,
            startTime,
            deltaTime,
            endTime,
            timeReward,
            needReceiveGoods
        }
    };
}

async function checkCond(roleId: string, heroes, type: number, param: number, cnt: number) {

    let heroCnt = 0;

    switch (type) {
        case 1:
            for (let seqId of heroes) {
                const { star, colorStar } = await HeroModel.findBySeqIdAndRole(seqId, roleId);
                if (star + colorStar >= param) {
                    heroCnt++;
                }
            }
            break;
        case 2:
            for (let seqId of heroes) {
                let { hid } = await HeroModel.findBySeqIdAndRole(seqId, roleId);
                const hInfo = gameData.hero.get(hid);
                if (hInfo.camp === param) {
                    heroCnt++;
                }
            }
            break;
        case 3:
            for (let seqId of heroes) {
                let { hid } = await HeroModel.findBySeqIdAndRole(seqId, roleId);
                const hInfo = gameData.hero.get(hid);
                if (gameData.job.get(hInfo.jobid).job_class === param) {
                    heroCnt++;
                }
            }
            break;
        default:
            break;
    }
    if (heroCnt >= cnt) {
        return true;
    }
    return false;
}

export async function checkTaskConditions(roleId: string, heroes: number[], condition: { type: number, param: number, count: number }[]) {

    let res = true;
    for (let { type, param, count } of condition) {
        const checkRes = await checkCond(roleId, heroes, type, param, count);
        if (!checkRes) {
            res = false;
            break;
        }
    }
    return res;
}

/**
 * 创建新的派遣任务
 * @param towerLv 玩家层数
 * @param roleId 玩家id
 * @param roleName 玩家名
 * @param hideNum 隐藏的任务数量
 */
export async function createCurTasks(towerLv: number, batchCode: string, roleId: string, roleName: string, hideNum: number, showNum: number, rids: Array<number>) {
    let randomList = [];
    let dicTask = gameData.towerTask;
    for (let [_id, task] of dicTask) {
        let { suitFloor: { min, max } } = task;
        let flag = max ? (towerLv >= min && towerLv <= max) : (towerLv >= min);
        if (flag) {
            randomList.push(task);
        }
    }
    let returnTasks = new Array();
    const arr = [{ refreshStatus: 0, num: hideNum }, { refreshStatus: 1, num: showNum }];
    for (let { refreshStatus, num } of arr) {
        let taskIds = [];
        for (let i = 0; i < num; i++) {
            let list = randomList.filter(cur => !rids.includes(cur.taskId));
            let { dic: tmp } = getRandEelmWithWeight(list);
            if (tmp) {
                taskIds.push(tmp.taskId);
                rids.push(tmp.taskId)
            }
        }
        const curTasks = await TowerTaskRecModel.createTasks(roleId, roleName, batchCode, taskIds, refreshStatus);

        if (refreshStatus == 1) {
            returnTasks = curTasks;
        }
    }

    return returnTasks;
}

export function getRemainTime(curTime: Date) {
    let nextTime = <number>getTimeFunM(curTime).getAfterDayWithHour(1);
    return Math.floor((nextTime - curTime.getTime()) / 1000);
}

export function treatTask(recs: Array<any>, curTime: Date) {
    return recs.map(cur => {
        let { heroes, batchCode, taskId, taskCode, status, completeTime } = cur;

        let getStatusResult = getTaskStatus(status, completeTime, curTime);
        return { heroes, batchCode, taskId, taskCode, completeTime, ...getStatusResult }
    });
}

// 刷新任务，正在进行中的部分保留，没有进行中的刷掉
export function getDoingOrWaitingTasks(taskList: Array<any>, curTime: Date) {
    let doingTaskCode = new Array<string>(), waitingTaskCode = new Array<string>(), doingIds = new Array<number>();
    for (let task of taskList) {
        let { status } = getTaskStatus(task.status, task.completeTime, curTime);
        if (status == 1 || status == 2) {
            doingTaskCode.push(task.taskCode);
            doingIds.push(task.taskId);
        } else if (status == 0 || status == 3) {
            waitingTaskCode.push(task.taskCode);
        }
    }
    return { doingTaskCode, waitingTaskCode, doingIds }
}

export function getTaskStatus(status: number, completeTime: Date, curTime: Date) {
    let remainTime = 0;
    if (status == 1) {
        // console.log(curTime.getTime(), completeTime.getTime())
        remainTime = Math.floor((completeTime.getTime() - curTime.getTime()) / 1000);
        if (remainTime < 0) {
            status = 2; remainTime = 0;
        }
    }

    return { status, remainTime }
}

export async function getTasksReward(roleId: string, curTime: Date) {
    let oldTasks = await TowerTaskRecModel.getCurTasks(roleId);
    let goods: ItemInter[] = [];
    for (let task of oldTasks) {
        let { completeTime, reward, termsForAdd, additionalReward } = gameData.towerTask.get(task.taskId);
        if (task.sendTime && task.sendTime.getTime() + completeTime * 1000 < curTime.getTime()) {
            goods = goods.concat(reward);
            if (termsForAdd) {
                const result = checkTaskConditions(roleId, task.heroes, termsForAdd);
                if (result) {
                    goods = goods.concat(additionalReward);
                }
            }
        }
    }
    return goods
}

/**
 * 获取随机机器人数据
 * @param cnt 机器人数量
 * @param withAttr 是否返回属性，false: 仅返回阵容
 */
export function getRandRobot(cnt = 1, withAttr = false) {
    let setInfo = getRandExpedition(cnt);
    if (!setInfo || setInfo.length !== cnt) return null;

    let robots = [];
    for (let info of setInfo) {
        if (!info || !info.warId) continue;
        const warInfo = gameData.warJson.get(info.warId);
        if (!warInfo || !warInfo.length) continue;

        let heroes = [];
        for (let hero of warInfo) {
            if (hero && hero.relation === 2 && hero.actorId) {
                heroes.push(hero.actorId);
            }
        }
        robots.push(heroes);
    }
    return robots;
}

/**
 * 拷贝敌军数组并添加当前血量字段
 * @param source 原敌军数组，不包含当前血量字段
 */
export function transBossHpArr(source: Array<{ dataId: number, hp: number, actorId: number }>): Array<{ dataId: number, hp: number, curHp: number, actorId: number }> {
    let desArr = [];
    source.forEach(elem => {
        let { hp } = elem;
        desArr.push(Object.assign(elem, { curHp: hp }));
    });
    return cloneDeep(desArr);
}
