import { Application, ChannelService, BackendSession, pinus, HandlerService, } from "pinus";
import { GUILD_ACTIVITY_TYPE, STATUS, REDIS_KEY, GUILD_POINT_WAYS, CITY_STATUS, TASK_TYPE, ITEM_CHANGE_REASON } from "../../../consts";
import { resResult } from "../../../pubUtils/util";
import { getGuildActivityStatus, getCityActivityObj, getCityStatus as pubGetCityStatus, getCities, getCityActivityRank, sendSingleCityActEndMsg, getMedianCe } from "../../../services/guildActivityService";
import { GuildActivityCityModel } from "../../../db/GuildActivityCity";
import { gameData } from "../../../pubUtils/data";
import { UserGuildActivityRecModel } from "../../../db/UserGuildActivityRec";
import { leaveCityChannel, addRoleToCityChannel, getCityChannelSid } from "../../../services/chatService";
import { UserGuildModel } from "../../../db/UserGuild";
import { GuildActivityRecordModel } from "../../../db/GuildActivityRec";
import { nowSeconds, getTimeFun } from "../../../pubUtils/timeUtil";
import { getGoldObject } from "../../../pubUtils/itemUtils";
import { GUILDACTIVITY, SERVER_DEBUG_MODE } from "../../../pubUtils/dicParam";
import { handleCost } from "../../../services/rewardService";
import { addActive } from "../../../services/guildService";
import { Rank } from "../../../services/rankService";
import { checkActivityTask, checkTask } from "../../../services/taskService";
import { guildInter } from "../../../pubUtils/interface";
import { dispatch } from "../../../pubUtils/dispatcher";

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

export class CityActivityHandler {
    channelService: ChannelService;
    constructor(private app: Application) {
        this.channelService = app.get('channelService');
    }

    private aid = GUILD_ACTIVITY_TYPE.CITY_ACTIVITY; // 诸侯混战id

    // 进入诸侯混战
    async getCityActivity(msg: {}, session: BackendSession) {

        // const roleId = session.get('roleId');
        // const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        const dbCities = await GuildActivityCityModel.getAllCities(serverId);
        let cities = getCities(guildCode, dbCities);

        return resResult(STATUS.SUCCESS, {
            ...statusResult,
            cities
        });
    }

    // 获取单个城池状态
    async getCityStatus(msg: { cityId: number }, session: BackendSession) {

        const roleId = session.get('roleId');
        const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');
        let sid = session.get('sid');

        const { cityId } = msg;

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let dicCity = gameData.cityActivity.get(cityId);
        if (!dicCity) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let obj = getCityActivityObj();
        let gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp);

        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid);
        if (!guildActivityRec) return resResult(STATUS.WRONG_PARMS);

        let myGuildActivityRec = await UserGuildActivityRecModel.findByRoleId(roleId, guildActivityRec.code);
        let challengeTime = 0;
        if (myGuildActivityRec) {
            challengeTime = myGuildActivityRec.challengeTime;
        }

        const dbCities = await GuildActivityCityModel.getAllCities(serverId);
        let cityStatus = pubGetCityStatus(guildCode, cityId, dicCity, dbCities);
        if (cityStatus == CITY_STATUS.DECLARED) {
            obj.pushGuild(guildCode, serverId, cityId);
        }

        let historyCity = obj.getHistoryCity(roleId);
        if (historyCity) {
            await leaveCityChannel(roleId, sid, historyCity);
        }
        await addRoleToCityChannel(roleId, sid, cityId);
        obj.setHistoryCity(roleId, cityId);


        let ranks = await getCityActivityRank(guildCode, serverId, cityId, roleId, roleName);

        return resResult(STATUS.SUCCESS, {
            cityId,
            ...statusResult,
            cityStatus,
            gateHp,
            challengeTime,
            ...ranks
        });
    }

    // 宣战
    async declareCity(msg: { cityId: number }, session: BackendSession) {
        // const roleId = session.get('roleId');
        // const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');

        //TODO 权限校验&时间校验

        let { cityId } = msg;
        let dicCity = gameData.cityActivity.get(cityId);
        if (dicCity.preCity.length > 0) return resResult(STATUS.CAN_NOT_DECLARE);

        let checkResult = await GuildActivityCityModel.checkDeclartion(serverId, guildCode);
        if (!!checkResult) return resResult(STATUS.HAS_DECLARED);
        let curCity = await GuildActivityCityModel.getCity(serverId, cityId);
        if (curCity && curCity.guardGuildCode) {
            return resResult(STATUS.CITY_IS_GUARD);
        }

        let rec = await GuildActivityCityModel.declare(serverId, cityId, guildCode);

        let chatSid = await getCityChannelSid(guildCode);
        pinus.app.rpc.chat.guildRemote.sendGuildCityDeclare.toServer(chatSid, cityId, guildCode, rec.declareCount);

        let obj = getCityActivityObj();
        obj.pushGuild(guildCode, serverId, cityId);

        const dbCities = await GuildActivityCityModel.getAllCities(serverId);
        let cities = getCities(guildCode, dbCities);
        return resResult(STATUS.SUCCESS, { cities });
    }

    // 开始挑战
    async checkBattle(msg: guildInter & { cityId: number, heroes: number[] }, session: BackendSession) {
        let { cityId, heroes, myUserGuild: userGuild } = msg;
        if (!heroes || heroes.length <= 0) return resResult(STATUS.WRONG_PARMS);

        const roleId = session.get('roleId');
        const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');
        const sid = session.get('sid');

        if (!guildCode) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH);

        let checkResult = await GuildActivityCityModel.checkDeclartion(serverId, guildCode);
        if (!checkResult) return resResult(STATUS.HAS_NOT_DECLARED);

        let obj = getCityActivityObj();

        const dicCity = gameData.cityActivity.get(cityId);

        let gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp);
        if (gateHp <= 0) return resResult(STATUS.GATE_HP_IS_ZERO);

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        if(!statusResult.isOpen) return resResult(STATUS.GUILD_ACTIVITY_NOT_OPEN);

        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid);
        if (!guildActivityRec) return resResult(STATUS.WRONG_PARMS);

        let { code: sourceCode } = guildActivityRec;

        let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, this.aid);
        if (myGuildActivityRec.challengeTime > nowSeconds()) {
            return resResult(STATUS.CHALLENGE_TIME_NOT_REACH);
        }
            
        myGuildActivityRec = await UserGuildActivityRecModel.incChallengeCnt(myGuildActivityRec.code, heroes);

        let { code } = myGuildActivityRec;

        // 更新公会参与的玩家
        obj.pushMembers(guildCode, roleId, userGuild.job, myGuildActivityRec.code);

        // 返回当前军团总军功
        let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId }, true);
        let guildScore = await r.getMyScore({ guildCode });

        const dbCities = await GuildActivityCityModel.getAllCities(serverId);
        let cityStatus = pubGetCityStatus(guildCode, cityId, dicCity, dbCities);

        // 前一天中位数战力
        let medianCe = await getMedianCe(serverId);

        return resResult(STATUS.SUCCESS, {
            code,
            cityId,
            cityStatus,
            ...statusResult,
            gateHp,
            guildScore,
            myScore: 0,
            medianCe
        });
    }

    async resetChallengeTime(msg: {}, session: BackendSession) {
        const roleId = session.get('roleId');
        const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');
        const sid = session.get('sid');

        if (!guildCode) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH);

        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid);
        if (!guildActivityRec) return resResult(STATUS.WRONG_PARMS);

        let { code: sourceCode } = guildActivityRec;

        let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, this.aid);

        let goldObj = getGoldObject(GUILDACTIVITY.CITYACTIVITY_CD_COST);
        const costRes = await handleCost(roleId, sid, [goldObj], ITEM_CHANGE_REASON.CITY_ACT_RESET_CHALLENGE_TIME);
        if (!costRes) {
            return resResult(STATUS.BATTLE_GOLD_NOT_ENOUGH);
        }

        myGuildActivityRec = await UserGuildActivityRecModel.updateInfo(myGuildActivityRec.code, { challengeTime: 0 });

        return resResult(STATUS.SUCCESS, {
            challengeTime: myGuildActivityRec.challengeTime
        });
    }


    // 上报城门受到伤害
    async hitGate(msg: { cityId: number, code: string, damage: number, hid: number, round: number }, session: BackendSession) {
        const roleId = session.get('roleId');
        const roleName = session.get('roleName');
        const serverId = session.get('serverId');
        const guildCode = session.get('guildCode');
        const sid = session.get('sid');

        let { cityId, code, damage, hid, round } = msg;
        if (damage == undefined || damage < 0) return resResult(STATUS.WRONG_PARMS);

        const dicCity = gameData.cityActivity.get(cityId);
        let obj = getCityActivityObj();
        let gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp);
        if (gateHp <= 0) return resResult(STATUS.GATE_HP_IS_ZERO);

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        if(!statusResult.isOpen) return resResult(STATUS.GUILD_ACTIVITY_NOT_OPEN);

        let pushResult = await UserGuildActivityRecModel.pushCityRecord(code, { round, hid, damage });
        if (!pushResult) return resResult(STATUS.WRONG_PARMS);

        let tomorrow = <number>getTimeFun().getAfterDayWithHour();
        // 更新redis数据
        let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode }, true);
        let myScore = await myR.setRankWithRoleInfo(roleId, damage, Date.now(), null, true);
        await myR.setExpire(tomorrow);

        let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId }, true);
        let guildScore = await r.setRankWithGuildInfo(guildCode, damage, Date.now(), null, true);
        await r.setExpire(tomorrow);

        gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp, -1 * damage);
        if (gateHp <= 0) {
            // 推送 停止活动并结算奖励
            await sendSingleCityActEndMsg(cityId, serverId);
        }
        // 推送 城门血量
        let chatSid = await getCityChannelSid(cityId);
        pinus.app.rpc.chat.guildRemote.pushCityGateHp.toServer(chatSid, cityId, gateHp);
        await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(this.aid);

        return resResult(STATUS.SUCCESS, {
            code,
            ...statusResult,
            guildScore,
            myScore,
            gateHp
        })
    }

    // 结束挑战
    async battleEnd(msg: { cityId: number, code: string, isSuccess: boolean }, session: BackendSession) {
        let roleId = session.get('roleId');
        let sid = session.get('sid');
        let guildCode = session.get('guildCode');
        let serverId = session.get('serverId');
        

        let { cityId, code, isSuccess } = msg;
        let obj = getCityActivityObj();

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let dicCity = gameData.cityActivity.get(cityId);
        if (!dicCity) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        // 更新userGuildActivityRecord
        let challengeTime = nowSeconds() + GUILDACTIVITY.CITYACTIVITY_CHALLENGE_CD;
        if(SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
            let guilds = pinus.app.getServersByType('guild');
            let guild = dispatch(roleId, guilds, 'guild');
            let currentTime = await pinus.app.rpc.guild.guildActivityRemote.getCurrentTime.toServer(guild.id);
            challengeTime = Math.floor(currentTime/1000) + GUILDACTIVITY.CITYACTIVITY_CHALLENGE_CD;
        }
        let myGuildActivityRec = await UserGuildActivityRecModel.updateInfo(code, { isSuccess, isCompleted: true, cityId, challengeTime });
        if (!myGuildActivityRec) return resResult(STATUS.WRONG_PARMS);

        // 发放活跃
        await addActive(roleId, serverId, GUILD_POINT_WAYS.ACTIVITY);  //获得活跃值
        // 返回当前军团总军功
        let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode }, true);
        let myScore = await myR.getMyScore({ roleId });
        let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId }, true);
        let guildScore = await r.getMyScore({ guildCode });
        let gateHp = obj.getGateHpAndInc(serverId, cityId, dicCity.hp);

        // 任务
        await checkTask(roleId, sid, TASK_TYPE.GUILD_ACTIVITY, 1, true, { aid: this.aid });
        //成长任务
        await checkActivityTask(serverId, sid, roleId, TASK_TYPE.GUILD_ACTIVITY, 1, { aid: this.aid })

        return resResult(STATUS.SUCCESS, {
            isSuccess,
            code,
            ...statusResult,
            guildScore,
            myScore,
            gateHp,
            challengeTime: myGuildActivityRec.challengeTime
        });
    }

    // ! 测试接口
    async debugResetCity(msg: { }, session: BackendSession) {
        let guildCode = session.get('guildCode');
        await GuildActivityCityModel.findOneAndUpdate(
            { declareGuilds: { $in: [guildCode] } }, 
            { $set: { guardGuildCode: "", guardGuildName: "", declareCount: 0, declareGuilds: [] } }, 
            { new: true }).lean();

        return resResult(STATUS.SUCCESS);
    }
}