import { Application, ChannelService, BackendSession, pinus, HandlerService, } from "pinus";
import { GUILD_ACTIVITY_TYPE, STATUS, REDIS_KEY, CITY_STATUS, TASK_TYPE, ITEM_CHANGE_REASON, PUSH_ROUTE, GUILD_ACTIVITY_STATUS } from "../../../consts";
import { genCode, resResult } from "../../../pubUtils/util";
import { getGuildActivityStatus, getCityActivityObj, getCityStatus as pubGetCityStatus, getCities, getCityActivityRank, sendSingleCityActEndMsg, declareCity, autoDeclareMyCity, getGAIndexInPinus, getDeclareIndex, sendCityGuildRankToUser } from "../../../services/guildActivity/guildActivityService";
import { GuildActivityCityModel } from "../../../db/GuildActivityCity";
import { gameData } from "../../../pubUtils/data";
import { UserGuildActivityRecModel } from "../../../db/UserGuildActivityRec";
import { leaveCityChannel, addRoleToCityChannel } from "../../../services/chatService";
import { GuildActivityRecordModel } from "../../../db/GuildActivityRec";
import { nowSeconds, getTimeFun } from "../../../pubUtils/timeUtil";
import { GUILDACTIVITY } from "../../../pubUtils/dicParam";
import { getGoldObject, handleCost } from "../../../services/role/rewardService";
import { Rank } from "../../../services/rankService";
import { checkTask } from "../../../services/task/taskService";
import { guildInter } from "../../../pubUtils/interface";
import { dispatch } from "../../../pubUtils/dispatcher";
import { ServerRecordModel } from "../../../db/ServerRecords";
import { sendMessageToCityWithSuc } from "../../../services/pushService";
import { isDebugTime } from "../../../pubUtils/sdkUtil";
import { GuildActivityCityDeclareModel } from "../../../db/GuildActivityCityDeclare";
import { GuildModel } from "../../../db/Guild";
import { getHistoryCity, setHistoryCity } from "../../../services/redisService";
import { HeroModel } from "../../../db/Hero";

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 index = getGAIndexInPinus(this.aid);
        let declareIndex = getDeclareIndex();

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        
        let obj = getCityActivityObj();
        if(obj.guildActivityStatus == GUILD_ACTIVITY_STATUS.START) {
            await autoDeclareMyCity(serverId, roleId, guildCode, declareIndex);
        }
        const dbCities = await GuildActivityCityModel.getAllCities(serverId, declareIndex);
        let cities = getCities(guildCode, dbCities);
        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid, index);
        let ranks = await getCityActivityRank(guildCode, serverId, guildActivityRec?.challengeCityId, roleId, roleName);

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

    // 获取单个城池状态
    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 index = getGAIndexInPinus(this.aid);
        let declareIndex = getDeclareIndex();

        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.CITY_NOT_FOUND);
        let obj = getCityActivityObj();
        let {gateHp, maxHp} = await obj.getGateHpAndInc(serverId, cityId);

        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid, index);
        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, declareIndex);
        let cityStatus = pubGetCityStatus(guildCode, cityId, dicCity, dbCities);
        if (cityStatus == CITY_STATUS.DECLARED) {
            obj.pushGuild(guildCode, serverId, cityId);
        }

        let historyCity = await getHistoryCity(roleId);
        if (historyCity) {
            await leaveCityChannel(roleId, sid, serverId, `${historyCity}`);
        }
        await addRoleToCityChannel(roleId, sid, serverId, cityId);
        await setHistoryCity(roleId, cityId);

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

        return resResult(STATUS.SUCCESS, {
            cityId,
            ...statusResult,
            cityStatus,
            gateHp, maxHp,
            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');

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

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

        let declareResult = await declareCity(serverId, roleId, guildCode, cityId, false, declareIndex);
        if(!declareResult) return resResult(STATUS.HAS_DECLARED);

        const dbCities = await GuildActivityCityModel.getAllCities(serverId, declareIndex);
        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;

        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 index = getGAIndexInPinus(this.aid);
        let declareIndex = getDeclareIndex();

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

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

        let obj = getCityActivityObj();

        const dicCity = gameData.cityActivity.get(cityId);
        if (!dicCity) return resResult(STATUS.CITY_NOT_FOUND);

        let { gateHp, maxHp } = await obj.getGateHpAndInc(serverId, cityId);
        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, index, [], cityId);
        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, index }, true);
        let guildScore = await r.getMyScore({ guildCode });

        const dbCities = await GuildActivityCityModel.getAllCities(serverId, declareIndex);
        let cityStatus = pubGetCityStatus(guildCode, cityId, dicCity, dbCities);
        // 全服活跃统计
        await ServerRecordModel.addActiveGuild(serverId, roleId, guildCode);

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

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

    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');
        let index = getGAIndexInPinus(this.aid);

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

        let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid, index);
        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;
        let obj = getCityActivityObj();
        let { gateHp, maxHp } = await obj.getGateHpAndInc(serverId, cityId);
        if (gateHp <= 0) return resResult(STATUS.GATE_HP_IS_ZERO);

        let hero = await HeroModel.findBySeqIdAndRole(hid, roleId);
        if(!hero && (damage/maxHp > 0.01)) damage = 0;

        let statusResult = getGuildActivityStatus(this.aid);
        if (!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        if(!statusResult.isOpen) return resResult(STATUS.GUILD_ACTIVITY_NOT_OPEN);
        if(statusResult.status != GUILD_ACTIVITY_STATUS.START) {
            return resResult(STATUS.SUCCESS, {
                code,
                ...statusResult
            })
        }

        let challengeTime = nowSeconds() + GUILDACTIVITY.CITYACTIVITY_CHALLENGE_CD;
        if(isDebugTime()) {
            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 pushResult = await UserGuildActivityRecModel.pushCityRecord(code, { round, hid, damage }, challengeTime);
        if (!pushResult) return resResult(STATUS.WRONG_PARMS);

        let nextWeek = <number>getTimeFun().getAfterDayWithHour(7);
        // 更新redis数据
        let index = getGAIndexInPinus(this.aid);
        let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode, index }, true);
        let myScore = await myR.setRankWithRoleInfo(roleId, damage, obj.getTimeGap(), null, true);
        await myR.setExpire(nextWeek);

        let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId, index }, true);
        let guildScore = await r.setRankWithGuildInfo(guildCode, damage, obj.getTimeGap(), null, true);
        await r.setExpire(nextWeek);

        ({gateHp, maxHp} = await obj.getGateHpAndInc(serverId, cityId, -1 * damage));
        if (gateHp <= 0) {
            // 推送 停止活动并结算奖励
            await sendSingleCityActEndMsg(cityId, serverId);
        }
        // 推送 城门血量
        await sendMessageToCityWithSuc(serverId, cityId, PUSH_ROUTE.GUILD_CITY_ACT_HP, { cityId, gateHp, maxHp });
        await sendCityGuildRankToUser(serverId, cityId, guildCode, roleId);

        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(isDebugTime()) {
            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);

        // 返回当前军团总军功
        let index = getGAIndexInPinus(this.aid);
        let myR = new Rank(REDIS_KEY.USER_CITY_ACTIVITY, { serverId, guildCode, index }, true);
        let myScore = await myR.getMyScore({ roleId });
        let r = new Rank(REDIS_KEY.CITY_ACTIVITY, { serverId, cityId, index }, true);
        let guildScore = await r.getMyScore({ guildCode });
        let { gateHp, maxHp } = await obj.getGateHpAndInc(serverId, cityId);

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

    async debugTestAutoDeclare(msg: {  }, session: BackendSession) {
        let serverId = session.get('serverId');
        let roleId = session.get('roleId');
        let guildCode = session.get('guildCode');
        let declareIndex = getDeclareIndex();
        for(let i = 0; i < 1000; i++) {
            autoDeclareMyCity(serverId, roleId, guildCode, declareIndex);
        }
        return resResult(STATUS.SUCCESS)
    }

    private interval: NodeJS.Timer;
    async debugStartHitGate(msg: { param: number  }, session: BackendSession) {
        const roleId = session.get('roleId');
        const serverId = session.get('serverId');
        let { param = 1000 } = msg;


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

        if(this.interval) {
            clearInterval(this.interval);
        }

        let fun = async () => {
            for(let [cityId] of gameData.cityActivity) {
                let guilds = pinus.app.getServersByType('guild');
                let guild = dispatch(`${cityId}`, guilds);
                await pinus.app.rpc.guild.guildActivityRemote.debugSendGateHp.toServer(guild.id, serverId, cityId);
            }
            await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(this.aid);
        };
        await fun();
        this.interval = setInterval(fun, param)
        return resResult(STATUS.SUCCESS)
    }

    async debugStopHitGate(msg: { magicWord: string }, session: BackendSession) {
        
        if(this.interval) {
            clearInterval(this.interval);
        }

        return resResult(STATUS.SUCCESS);
    }
}