// 存在激战期的内存数据

import { pinus } from "pinus";
import { GVG_CATAPULT, GVG_PERIOD } from "../../consts";
import { GVGTeamModel, GVGTeamType } from "../../db/GVGTeam";
import { GVGTeamMem } from "../../domain/battleField/gvgBattle";
import { dispatch } from "../../pubUtils/dispatcher";
import { getGVGPeriodData } from "./gvgService";

// 积分点占领情况，groupId_serverType_cityId -> GVGBattleData
const gvgBattleMap: Map<string, GVGBattleData> = new Map();

class GVGBattleData {
    public groupKey: string;     // 战区

    private teams: Map<string, GVGTeamMem> = new Map(); // 队伍, teamCode => team
    public rolePoints: Map<string, Map<number, string>> = new Map(); // roleId => pointId[]，用于更新玩家的积分
    private roleToTeam: Map<string, string[]> = new Map(); // roleId => teamCode
    private leagueToTeam: Map<string, Set<string>> = new Map(); // leagueCode => teamCode
    public areaToTeams: Map<number, Set<string>> = new Map(); // areaId => teamCode set，用于定时下发地图玩家数据
    private leagueActiveTech: Map<string, number[]> = new Map(); // 千机阁状态 leagueCode => activeTech
    
    constructor(groupKey: string) {
        this.groupKey = groupKey;
    }

    public findTeamsByArea(areaId: number) {
        let teamCodes = this.areaToTeams.get(areaId)||new Set();
        let teams: GVGTeamMem[] = [];
        for(let teamCode of teamCodes) {
            let team = this.teams.get(teamCode);
            if(team && !team.isBroken) teams.push(team);
        }
        return teams.slice(0, 20);
    }

    public findCatapultAttackTeam(areaId: number, leagueCode: string) {
        let teams: GVGTeamMem[] = [];
        let teamCodes = this.areaToTeams.get(areaId)||new Set();
        for(let teamCode of teamCodes) {
            let team = this.teams.get(teamCode);
            if(team && team.leagueCode != leagueCode && !team.isRobot) teams.push(team);
        }
        return teams;
    }

    public leaveCity(roleId: string) {
        let teamCodes = this.roleToTeam.get(roleId)||[];
        for(let teamCode of teamCodes) {
            let team = this.teams.get(teamCode);
            if(team) team.setCity(0);
            for(let [_areaId, teamSet] of this.areaToTeams) {
                if(teamSet.has(teamCode)) teamSet.delete(teamCode);
            }
            if(team.leagueCode && this.leagueToTeam.has(team.leagueCode)) 
                this.leagueToTeam.get(team.leagueCode).delete(teamCode);
        }
        this.rolePoints.delete(roleId);
    }

    public enterCity(...teams: GVGTeamType[]) {
        for(let team of teams) {
            let fromAreaId = this.teams.get(team.teamCode)?.areaId||0;

            if(!this.teams.has(team.teamCode)) {
                this.teams.set(team.teamCode, new GVGTeamMem(team))
            } else {
                this.teams.get(team.teamCode).updateTeam(team);
            }
            this.teams.get(team.teamCode).setCity(team.cityId, team.areaId, team.pointId);
            if(team.pointId > 0 && !team.isRobot) {
                if(!this.rolePoints.has(team.roleId)) this.rolePoints.set(team.roleId, new Map());
                for(let [pointId, teamCode] of this.rolePoints.get(team.roleId)) {
                    if(teamCode == team.teamCode) this.rolePoints.get(team.roleId).delete(pointId);
                }
                this.rolePoints.get(team.roleId).set(team.pointId, team.teamCode);
            }
            let teamCodesOfRole = this.roleToTeam.get(team.roleId)||[];
            if(teamCodesOfRole.indexOf(team.teamCode) == -1) teamCodesOfRole.push(team.teamCode);
            this.roleToTeam.set(team.roleId, teamCodesOfRole);
            if(team.leagueCode) {
                if(!this.leagueToTeam.has(team.leagueCode)) {
                    this.leagueToTeam.set(team.leagueCode, new Set());
                }
                if(!this.leagueToTeam.get(team.leagueCode).has(team.teamCode))
                    this.leagueToTeam.get(team.leagueCode).add(team.teamCode);
            }
            
            this.setAreaMap(team.teamCode, fromAreaId, team.areaId);
        }
    }

    private setAreaMap(teamCode: string, fromAreaId: number, toAreaId: number) {
        if(fromAreaId == toAreaId) return;
        let fromAreaSet = this.areaToTeams.get(fromAreaId);
        if(fromAreaSet && fromAreaSet.has(teamCode)) fromAreaSet.delete(teamCode);

        if(!this.areaToTeams.has(toAreaId)) {
            this.areaToTeams.set(toAreaId, new Set());
        }
        if(!this.areaToTeams.get(toAreaId).has(teamCode)) {
            this.areaToTeams.get(toAreaId).add(teamCode);
        }
    }

    private setRolePoints(roleId: string, fromPointId: number, pointId: number, teamCode: string) {
        if(fromPointId > 0) { // 被打得撤离积分点
            if(this.rolePoints.has(roleId) && this.rolePoints.get(roleId).has(fromPointId)) {
                this.rolePoints.get(roleId).delete(fromPointId);
            }
        }
        if (pointId > 0) {
            if(!this.rolePoints.has(roleId)) this.rolePoints.set(roleId, new Map());
            this.rolePoints.get(roleId).set(pointId, teamCode);
        }
    }

    public move(teamCode: string, areaId: number, fromAreaId: number, startMoveTime: number, stopMoveTime: number) {
        let team = this.teams.get(teamCode);
        if(!team) return;
        team.moveToArea(areaId, fromAreaId, startMoveTime, stopMoveTime);
        this.setAreaMap(teamCode, fromAreaId, areaId);
    }

    public setTime(teamCode: string, teamUpdate: GVGTeamType) {
        let team = this.teams.get(teamCode);
        if(!team) return;
        team.attackTime = teamUpdate.attackTime;
        team.defenseTime = teamUpdate.defenseTime;
    }

    // 队伍入驻积分点
    public teamSettle(roleId: string, teamCode: string, pointId: number) {
        let teamMem = this.teams.get(teamCode);
        if(!teamMem) return;
        let { pointId: fromPointId } = teamMem;
        this.setRolePoints(roleId, fromPointId, pointId, teamCode);
        teamMem.pointId = pointId;
}

    // 关卡结算
    public battleEnd(teams: GVGTeamType[]) {
        for(let team of teams) {
            let teamMem = this.teams.get(team.teamCode);
            if(!teamMem) { // 基本不会有没有这个内存的情况，有的话就让他重新计算一次吧
                this.enterCity(team); continue;
            }
            let { curTeamBreak, pointId } = team;
            let { areaId: fromAreaId, pointId: originPointId } = teamMem;
            teamMem.updateTeam(team);
            this.setRolePoints(team.roleId, originPointId, curTeamBreak? 0: pointId, team.teamCode);
            this.setAreaMap(team.teamCode, fromAreaId, team.areaId);
        }
    }

    // 投石车
    public findCatapult() {
        return this.findTeamsByRole(GVG_CATAPULT);
    }

    public findTeamsByRole(roleId: string) {
        let teamCodes = this.roleToTeam.get(roleId)||[];
        return teamCodes.map(teamCode => this.teams.get(teamCode));
    }

    // 积分点情况
    public findSettledPoint() {
        let teams: GVGTeamMem[] = [];
        for(let [_roleId, pointIds] of this.rolePoints) {
            for(let [pointId, teamCode] of pointIds) {
                let team = this.teams.get(teamCode);
                if(pointId > 0 && team) teams.push(team);
            }
        }
        return teams;
    }

    public findSettledPointMapByLeague(cityId: number) {
        let pointByLeague = new Map<string, number[]>();
        for(let [_roleId, pointIds] of this.rolePoints) {
            for(let [pointId, teamCode] of pointIds) {
                let team = this.teams.get(teamCode);
                if(pointId > 0 && team && team.leagueCode && team.cityId == cityId) {
                    if(!pointByLeague.has(team.leagueCode)) pointByLeague.set(team.leagueCode, []);
                    pointByLeague.get(team.leagueCode).push(team.pointId);
                };
            }
        }
        return pointByLeague;
    }

    public findAreas() {
        return this.areaToTeams.keys();
    }

    public updateLeagueInfo(leagueCode: string, leagueName: string) {
        let teamCodes = this.leagueToTeam.get(leagueCode)||new Set();
        for(let teamCode of teamCodes) {
            let team = this.teams.get(teamCode);
            team.leagueName = leagueName;
        }
    }

    public updateRoleInfo(roleId: string, info: { roleName?: string, lv?: number }) {
        let teamCodes = this.leagueToTeam.get(roleId)||new Set();
        for(let teamCode of teamCodes) {
            let team = this.teams.get(teamCode);
            if(info.roleName) team.roleName = info.roleName;
            if(info.lv) team.lv = info.lv;
        }
    }

    public getLeagueTech(leagueCode: string) {
        return this.leagueActiveTech.get(leagueCode);
    }

    public setLeagueTech(leagueCode: string, activeTech: number[]) {
        this.leagueActiveTech.set(leagueCode, activeTech);
    }
}

export function getGVGBattleData(groupKey: string) {
    if(!gvgBattleMap.has(groupKey)) {
        gvgBattleMap.set(groupKey, new GVGBattleData(groupKey));
    }
    return gvgBattleMap.get(groupKey);
}

export function getGVGBattleMap() {
    return gvgBattleMap;
}

export async function initTeamToMem() {
    let sid = pinus.app.getServerId();
    let servers = pinus.app.getServersByType('guild');
    let { configId, period } = getGVGPeriodData();
    if(period != GVG_PERIOD.BATTLE) return;
    let teams = await GVGTeamModel.findByConfigId(configId);
    for(let team of teams) {
        if(team.isBroken) continue;
        if(dispatch(team.cityId.toString(), servers)?.id == sid) {
            let teamObj = getGVGBattleData(team.groupKey);
            teamObj.enterCity(team);
        }
    }
}

export function clearBattleMemory() {
    gvgBattleMap.clear();
}