import { Application, BackendSession, pinus } from 'pinus';
import { resResult } from '../../../pubUtils/util';
import { STATUS } from '../../../consts/statusCode';
import { getRoleOnlineInfo, updateUserInfo } from '../../../services/redisService';
import { addItems } from '../../../services/role/rewardService';
import { RewardInter } from '../../../pubUtils/interface';
import { gameData, getExpByLv, getHeroExpByLv, getHeroLvByExp, getLvByExp, getEquipByJobClassAndEPlace } from '../../../pubUtils/data';
import { RoleModel, RoleType, WarStar } from '../../../db/Role';
import { BLOCK_TYPE, DEFAULT_HEROES, GUILD_AUTH, GUILD_DISMISS_WAY, GUILD_JOB, GUILD_MASTER_CHANGE_WAY, GUILD_STATUS, GUILD_STRUCTURE, HERO_SYSTEM_TYPE, ITEM_CHANGE_REASON, MAIL_TYPE, MEMORY_LOG_TYPE, PUSH_ROUTE, REDIS_KEY, TA_EVENT, WAR_TYPE } from '../../../consts';
import { GuildFormParam, SetHeroParam } from '../../../domain/backEndField/params';
import { GuildModel, GuildUpdateParam } from '../../../db/Guild';
import { Rank } from '../../../services/rankService';
import { UserGuildModel } from '../../../db/UserGuild';
import { UserGuildApplyModel } from '../../../db/UserGuildApply';
import { reportTAEvent } from '../../../services/sdkService';
import { sendMailByContent } from '../../../services/mailService';
import { GuildLeader } from '../../../domain/rank';
import { HeroModel } from '../../../db/Hero';
import { SkinModel } from '../../../db/Skin';
import { PvpDefenseModel } from '../../../db/PvpDefense';
import { createHeroes } from '../../../services/role/createHero';
import { calculateCeWithHero, calculateCeWithRole } from '../../../services/playerCeService';
import { pushChangeGuildLeader, pushGuildDismiss, pushGuildInfoUpdate } from '../../../services/guildService';
import { sendMessageToUserWithSuc } from '../../../services/pushService';
import { RScriptRecordModel } from '../../../db/RScriptRecord';
import { DicWar } from '../../../pubUtils/dictionary/DicWar';
import { SchoolModel } from '../../../db/School';
import { JewelModel } from '../../../db/Jewel';
import { RoleCeModel } from '../../../db/RoleCe';
import { setTrainLv } from '../../../services/gmService';
import { ArtifactModel } from '../../../db/Artifact';

let timer: NodeJS.Timer;
export default function (app: Application) {
    return new GmRoleHandler(app);
}

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

    async addItems(msg: { roleId: string, roleName: string, serverId: number, values: { reward: RewardInter[], heroes: number[], lv: number, expInc: number, fixedIpLocation: string }}, session: BackendSession) {
        let { roleId, roleName, serverId, values } = msg;

        let { reward, heroes, lv, expInc = 0, fixedIpLocation } = values;
        let connect = await getRoleOnlineInfo(roleId);
        let sid = connect.isOnline?connect.sid: null;

        if(reward &&reward.length > 0) {
            await addItems(roleId, roleName, sid, values.reward, ITEM_CHANGE_REASON.DEBUG);
        }
        if(heroes && heroes.length > 0) {
            await createHeroes(roleId, roleName, sid, serverId, heroes.map(hid => { return { hid, count: 1 }}))
        }
        if(lv && lv > 0) {
            let exp = getExpByLv(lv - 1)?.sum||0;
            exp += expInc;
            let newLv = getLvByExp(exp);
            let role = await RoleModel.updateRoleInfo(roleId, { lv: newLv, exp })
            let r = new Rank(REDIS_KEY.USER_LV, { serverId: serverId });
            await r.setRankWithRoleInfo(roleId, newLv, Date.now(), role);
            sendMessageToUserWithSuc(roleId, PUSH_ROUTE.PLAYER_EXP_CHANGE, { 
                isLvUp: false, lv: newLv, exp
            }, sid);
        }
        if(fixedIpLocation != undefined) {
            await RoleModel.updateRoleInfo(roleId, { fixedIpLocation })
        }
        return resResult(STATUS.SUCCESS);
    }

    async handleBlock(msg: { roleId: string, blockType: BLOCK_TYPE, blockReason: string }, session: BackendSession) {
        let { roleId, blockType, blockReason } = msg;
        if(roleId == undefined || blockType == undefined || blockReason == undefined) return resResult(STATUS.WRONG_PARMS);
        let role = await RoleModel.updateRoleInfo(roleId, { blockType, blockReason });
        if(!role) return resResult(STATUS.ROLE_NOT_FOUND);
        let { isOnline, sid } = await getRoleOnlineInfo(roleId);
        if(isOnline) {
            await this.app.rpc.connector.connectorRemote.setOtherUserSession.toServer(sid,[{ roleId, blockType }]);
            if(blockType == BLOCK_TYPE.BLOCK) { // 踢走
                await this.app.rpc.connector.connectorRemote.remoteLogin.toServer(sid, roleId, STATUS.BLOCKED);
            }
        }
        return resResult(STATUS.SUCCESS);
    }


    async setHero(msg: { roleId: string, hid: number, param: SetHeroParam }, session: BackendSession) {
        let { roleId, hid } = msg;
        let param = new SetHeroParam(msg.param);
        if(!param.checkParams()) return resResult(STATUS.WRONG_PARMS);
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if(!hero) return resResult(STATUS.HERO_NOT_FIND);
        let { sid } = await getRoleOnlineInfo(roleId);

        if(param.lv && param.lv > 0) {
            let exp = getHeroExpByLv(param.lv - 1)||0;
            exp += (param.expInc||0);
            let newLv = getHeroLvByExp(exp);
            param.lv = newLv;
            param['exp'] = exp;
        }
        await calculateCeWithHero(HERO_SYSTEM_TYPE.INIT, roleId, hero.serverId, sid, hid, {...hero, ...param});
        // if(param.job > 0) {
        //     await calculateCe(HERO_SYSTEM_TYPE.STAGEUP, roleId, hero.serverId, sid, param, {}, { hid });

        //     hero = await calPlayerCeAndSave(HERO_SYSTEM_TYPE.STAGEUP, sid, roleId, hero, { job: param.job, jobStage: 0 });
        // }

        // if (hero.star != param.star) {
        //     await calAllHeroCe(HERO_SYSTEM_TYPE.STAR, sid, roleId, {}, [hid, 1]); // 升星可能影响到百家学院全局加成
        // }
        return resResult(STATUS.SUCCESS);
    }

    /**
     * 升级英雄装备至限制内的最高级
     * 
     * @param {{ roleId: string, hid: number }} msg
     * @param {BackendSession} session
     * @return {*} 
     * @memberof GmRoleHandler
     */
    async upgradeHeroEPlace(msg: { roleId: string, hid: number }, session: BackendSession) {
        let { roleId, hid } = msg;
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);
        /*
         * 通过 
         * dic_zyz_equipQuality.json
         * dic_zyz_equipStar.json
         * dic_zyz_equipStrength.json
         * dic_zyz_artifactQuality.json
         * 配置文件得出:
         * maxLevel: 100
         * maxQuality: 5 (金)
         * maxStar: 12
         * 
         * 限制: 
         * 装备等级不能超过hero等级
         * maxQuality限制等级最大值: 金 最大lv 100
         * 
         * 装备信息:
         * dic_zyz_equip.json
        */
        const MAX_STAR = 12;
        const MAX_QUALITY = 5;
        // EPLACE_ID 分别代表4个部位
        const EPLACE_IDS = [1, 2, 3, 4];
        // 装备升级、升星、升品(注意升级限制)
        // 装备未满 - 补全 - 并升至满级
        let doNotGetEPlaceIds = new Set(EPLACE_IDS);
        hero.ePlace.forEach((ePlaceItem) => {
            doNotGetEPlaceIds.delete(ePlaceItem.id);
                // 该装备已拥有 - 升级、品、星
                // 升级 - 最大等级100
                ePlaceItem.lv = hero.lv; // 英雄等级肯定小于MAX_LV
                // 升品 - 金
                ePlaceItem.quality = MAX_QUALITY;
                ePlaceItem.qualityStage = 0;
                // 生星 - 最高12
                ePlaceItem.star = MAX_STAR;
                ePlaceItem.starStage = 0;
        });
        // 装备补全
        let dicHero = gameData.hero.get(hero.skinId);
        doNotGetEPlaceIds.forEach((ePlaceId) => {
            let dicEquip = getEquipByJobClassAndEPlace(dicHero?.jobClass, ePlaceId);
            if (dicEquip) {
                hero.ePlace.push({
                    lv: hero.lv,
                    quality: MAX_QUALITY,
                    qualityStage: 0,
                    star: MAX_STAR,
                    starStage: 0,
                    jewel: 0,
                    id: ePlaceId,
                    equipId: dicEquip.id,
                    stones: [
                        {
                            id: 1,
                            stone: 0
                        },
                        {
                            id: 2,
                            stone: 0
                        },
                        {
                            id: 3,
                            stone: 0
                        }
                    ]
                })
            }
        })
        
        // 更新装备信息
        await HeroModel.updateHeroInfo(roleId, hid, { ePlace: hero.ePlace });
        // 获取战力重算需要的数据 
        let role = await RoleModel.findByRoleId(roleId);
        let schools = await SchoolModel.findByRoleId(roleId);
        let jewels = await JewelModel.findbyRole(roleId);
        let heroes = await HeroModel.findByRole(roleId);
        let skins = await SkinModel.findbyRole(roleId);
        let artifacts = await ArtifactModel.findbyRole(roleId);
        // 所有战力重算
        await calculateCeWithRole(HERO_SYSTEM_TYPE.RE_CAL, roleId, role.serverId, null, {}, { role, schools, jewels, heroes, skins, artifacts });
        // 获取更新装备的英雄
        hero = await HeroModel.findByHidAndRole(hid, roleId);

        // 返回强化，升星，升品后的hero数据  
        return resResult(STATUS.SUCCESS, { curHero: hero });
    }

    async deleteHero(msg: { roleId: string, hid: number }, session: BackendSession) {
        let { roleId, hid } = msg;

        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        await HeroModel.deleteHero(roleId, hid);
        await SkinModel.deleteByHero(roleId, hid);
        let role = await RoleModel.findByRoleId(roleId);
        await PvpDefenseModel.deleteHero(roleId, hid);
        await RoleCeModel.deleteHero(roleId, hid);
        await RoleModel.updateRoleInfo(roleId, { topLineup: role.topLineup.filter(cur => cur.hid != hid), topLineupCe: role.topLineupCe - hero.ce, ce: role.ce - hero.ce });
        await JewelModel.updateMany({ hid }, { $set: { hid: 0}});

        return resResult(STATUS.SUCCESS);
    }


    async updateGuild(msg: GuildFormParam, session: BackendSession ) {
        const uid = session.get('uid');
        let params = new GuildFormParam(msg);
        if(!params.checkParams()) return resResult(STATUS.WRONG_PARMS);

        let guild = await GuildModel.findByCode(params.code, null, '+members');
        let structure = guild.structure||[];

        let updateParams: GuildUpdateParam = { code: params.code };
        if(params.name && guild.name != params.name) updateParams.name = params.name;
        if(params.fund != undefined && guild.fund != params.fund) updateParams.fund = params.fund;
        if(params.notice && guild.notice != params.notice) updateParams.notice = params.notice;
        if(params.lv && guild.lv != params.lv) updateParams.lv = params.lv;
        for(let id = GUILD_STRUCTURE.START; id < GUILD_STRUCTURE.END; id ++) {
            let curStructure = structure.find(cur => cur.id == id);
            let lv = params.lv;
            switch(id) {
                case GUILD_STRUCTURE.ARMY_CENTER: 
                    lv = params.lv; break;
                case GUILD_STRUCTURE.EQUIP_PRODUCE:
                    lv = params.equipProduce; break;
                case GUILD_STRUCTURE.BOSS:
                    lv = params.boss; break;
                case GUILD_STRUCTURE.TRAIN:
                    lv = params.train; break;
                case GUILD_STRUCTURE.DONATE:
                    lv = params.donate; break;
                case GUILD_STRUCTURE.WISH_POOL:
                    lv = params.wishPool; break;
                case GUILD_STRUCTURE.STORE:
                    lv = params.store; break;
            }
            if(curStructure) {
                curStructure.lv = lv;
            } else {
                structure.push({ id, lv });
            }
        }
        updateParams.structure = structure;
        await pushGuildInfoUpdate(params.code, updateParams);
        // 设置玩家名
        if(updateParams.name != undefined || updateParams.lv != undefined) {
            let arr = [];
            if(updateParams.name != undefined) {
                arr.push({ field: 'name', value: updateParams.name });
                for(let roleId of guild.members) {
                    await updateUserInfo(REDIS_KEY.USER_INFO, roleId, [{ field: 'guildName', value: updateParams.name }]);
                }
            }
            if(updateParams.lv != undefined) arr.push({ field: 'lv', value: updateParams.lv });
            await updateUserInfo(REDIS_KEY.GUILD_INFO, params.code, arr);
        }
        if (updateParams.lv != undefined) {
            let r = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId: guild.serverId });
            await r.setRankWithGuildInfo2(guild.code, updateParams.lv, guild.activeWeekly, guild.lvUpdateTime, guild);
        }

        guild = await GuildModel.updateInfo(params.code, updateParams);

        if(params.trainLv) {
            await setTrainLv(params.code, params.trainLv);
        }

        return resResult(STATUS.SUCCESS);
    }
    async dismissGuild(msg: { code: string }, session: BackendSession) {

        const { code } = msg;

        let guild = await GuildModel.findByCode(code, null, '+serverId');
        if(!guild) return resResult(STATUS.GUILD_NOT_FOUND);
        if(guild.status == GUILD_STATUS.DISMISSED) return resResult(STATUS.GUILD_DELETE_ERROR);

        let serverId = guild.serverId;
        guild = await GuildModel.dismiss(code, serverId);
        if (!guild) return resResult(STATUS.GUILD_DELETE_ERROR);

        await UserGuildModel.dismiss(code);
        const { members } = guild;
        await RoleModel.dissmissGuild(members);
        await UserGuildApplyModel.deleteApplyByGuild(code);

        await this.app.rpc.connector.connectorRemote.setOtherUserGuildSession.broadcast(members.map(roleId => { return { roleId, userGuild: null } })); // 更新session

        // 删除channel
        await pushGuildDismiss(code);
        let r = new Rank(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId });
        await r.removeFromRank({ guildCode: code });
        let r2 = new Rank(REDIS_KEY.GUILD_LV_RANK, { serverId });
        await r2.removeFromRank({ guildCode: code });
        let leader = <RoleType>guild.leader;
        reportTAEvent(leader.roleId, TA_EVENT.GUILD_DISMISS, { name: guild.name, way: GUILD_DISMISS_WAY.BACKEND });

        return resResult(STATUS.SUCCESS, { code, status: guild.status });
    }

    async setGuildLeader(msg: { code: string, roleId: string }, session: BackendSession) {

        const { code, roleId } = msg;
        let guild = await GuildModel.findByCode(code, null, '+serverId');
        if(!guild) return resResult(STATUS.GUILD_NOT_FOUND);
        if(guild.status == GUILD_STATUS.DISMISSED) return resResult(STATUS.GUILD_DELETE_ERROR);

        let role = await RoleModel.findByRoleId(roleId);
        if(!role) return resResult(STATUS.ROLE_NOT_FOUND);

        let userGuild = await UserGuildModel.getMyGuild(roleId);
        if(!userGuild || userGuild.guildCode != code) return resResult(STATUS.GUILD_KICK_ERROR);
        if(userGuild.auth == GUILD_AUTH.LEADER) return resResult(STATUS.GUILD_USER_IS_LEADER);

        let leader = <RoleType>guild.leader;
        // 交换
        let oldLeaderUserGuild = await UserGuildModel.updateInfo(leader.roleId, { auth: GUILD_AUTH.MEMBER, job: GUILD_JOB.SHIBING }, {}, 'auth'); // 团长撤
        await this.app.rpc.connector.connectorRemote.setOtherUserGuildSession.broadcast([{ roleId: leader.roleId, userGuild: oldLeaderUserGuild }]); // 更新session

        let newLeaderUserGuild = await UserGuildModel.updateInfo(roleId, { auth: GUILD_AUTH.LEADER, job: GUILD_JOB.DAJIANGJUN }, {}, 'auth'); // 最高功勋人升
        await this.app.rpc.connector.connectorRemote.setOtherUserGuildSession.broadcast([{ roleId: roleId, userGuild: newLeaderUserGuild }]); // 更新session

        let managerCntInc = userGuild.auth == GUILD_AUTH.MANAGER ? -1 : 0; // 管理人数

        guild = await GuildModel.updateInfo(code, { leader: role._id }, { managerCnt: managerCntInc }, 'managerCnt name'); // 如果有转让团长设置leader

        // 添加动态
        await pushChangeGuildLeader(code, guild.managerCnt, role, leader.roleId);
        await sendMailByContent(MAIL_TYPE.GUILD_BE_IMPEACH, leader.roleId, { params: [guild.name] });
        await sendMailByContent(MAIL_TYPE.GUILD_BE_SET_LEADER, roleId, { params: [guild.name] });

        await updateUserInfo(REDIS_KEY.GUILD_INFO, code, [{ field: 'leader', value: new GuildLeader(role) }]);
        reportTAEvent(roleId, TA_EVENT.GUILD_MASTER_CHANGE, { way: GUILD_MASTER_CHANGE_WAY.BACKEND, change_id_after: roleId })

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

    async skipEpilogue(msg: { roleId: string, roleName: string }, session: BackendSession) {

        const { roleId, roleName } = msg;
        let role = await RoleModel.findByRoleId(roleId);
        if(!role) return resResult(STATUS.ROLE_NOT_FOUND);
        if(role.hasInit) return resResult(STATUS.ROLE_HAS_INIT);

        let checkName = await RoleModel.checkName(roleName, role.serverId);
        if (checkName) return resResult(STATUS.NAME_HAS_USED);

        let warStars: WarStar[] = [];     
        let insertParams: DicWar[] = [];
        let exp = 0;
        for(let i = 101; i <= 103; i++) {
            let dicWar = gameData.war.get(i);
            insertParams.push(dicWar);
            warStars.push({ id: dicWar.war_id, warType: dicWar.warType, star: 0, stars: [] });
            exp += dicWar.kingExp;
        }
        let lv = getLvByExp(exp);

        await RScriptRecordModel.deleteByWarType(roleId, WAR_TYPE.NORMAL);
        await RScriptRecordModel.insertScripts(roleId, insertParams);

        let onlineUser = await getRoleOnlineInfo(roleId);
        await createHeroes(roleId, roleName, onlineUser?.sid, role.serverId, DEFAULT_HEROES.map(hid => ({hid, count: 1})), { roleName, hasInit: true, title: role.title, teraphs: role.teraphs, lv, exp, warStar: warStars });

        return resResult(STATUS.SUCCESS);
    }

    async reCalCe(msg: { roleId: string }, session: BackendSession) {
        const { roleId } = msg;
        let role = await RoleModel.findByRoleId(roleId);
        let schools = await SchoolModel.findByRoleId(roleId);
        let jewels = await JewelModel.findbyRole(roleId);
        let heroes = await HeroModel.findByRole(roleId);
        let skins = await SkinModel.findbyRole(roleId);
        let artifacts = await ArtifactModel.findbyRole(roleId);
        
        await calculateCeWithRole(HERO_SYSTEM_TYPE.RE_CAL, roleId, role.serverId, null, {}, { role, schools, jewels, heroes, skins, artifacts });
        return resResult(STATUS.SUCCESS);
    }
}