// GVGTeam 数据库表，存储 GVG 队伍信息
import BaseModel from "./BaseModel";
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { genCode } from "../pubUtils/util";
import { nowSeconds } from "../pubUtils/timeUtil";
import { EXTERIOR, GVG } from "../pubUtils/dicParam";
import { DicGVGAreaPoint } from "../pubUtils/dictionary/DicGVGAreaPoint";
import { InitTeamParam, SaveTeamUpdateParam } from "../domain/gvgField/gvgDb";
import { GVGHeroInfo } from "../domain/dbGeneral";
import { GVG_CATAPULT, GVG_ROBOT } from "../consts";

@index({ roleId: 1, index: 1 })
@index({ teamCode: 1 })
@index({ cityId: 1, leagueId: 1 })
export default class GVGTeam extends BaseModel {

    @prop({ required: true })
    configId: number;     // 赛季，每赛季在enterCity的时候检测，如果不一样就更新durability那些东西

    @prop({ required: true })
    roleId: string;     // 玩家id

    @prop({ required: true })
    roleName: string;     // 玩家名

    @prop({ required: true })
    serverId: number;     // 小区id

    @prop({ required: true })
    lv: number;     // 玩家等级

    @prop({ required: true })
    teamCode: string;     // 玩家队伍唯一标识

    @prop({ required: true })
    index: number;     // 队伍id，1-3，玩家的第几个队伍

    @prop({ required: true })
    leagueCode: string; // 联军

    @prop({ required: true })
    leagueName: string; // 联军

    @prop({ required: true })
    guildCode: string; // 军团

    @prop({ required: false })
    areaId: number;

    @prop({ required: false })
    cityId: number;

    @prop({ required: false })
    pointId: number;

    @prop({ required: true })
    head: number;       // 头像

    @prop({ required: true })
    spine: number;      // 形象

    @prop({ required: true })
    frame: number;      // 相框

    @prop({ required: true, default: 0 })
    durability: number; // 耐久

    @prop({ required: true, default: 0 })
    maxDurability: number; // 耐久

    @prop({ required: true, default: 0 })
    restartTime: number; // 修整期倒计时

    @prop({ required: true, default: 0 })
    attackTime: number;  // 进攻冷却时间

    @prop({ required: true, default: 0 })
    lockTime: number;  // 正在被进攻中

    @prop({ required: true, default: 0 })
    lockTeamCode: string;  // 正在被这支队伍进攻中

    @prop({ required: true, default: 0 })
    fromAreaId: number;   // 从那个区域移动

    @prop({ required: true, default: 0 })
    startMoveTime: number;

    @prop({ required: true, default: 0 })
    moveCdTime: number;  // 移动冷却时间

    @prop({ required: true, default: 0 })
    stopMoveTime: number;    // 移动时间

    @prop({ required: true, default: 0 })
    defenseTime: number; // 防守保护时间

    @prop({ required: true, default: 0 })
    battleScore: number;     // 积分

    @prop({ required: true, default: 0 })
    settleScore: number;     // 积分

    @prop({ required: true, type: () => GVGHeroInfo, _id: false })
    lineup: GVGHeroInfo[];

    @prop({ required: true })
    lineupCe: number;

    @prop({ required: true })
    groupKey: string;   // 战区

    @prop({ required: true, default: false })
    isRobot: boolean;     // 是否是机器人

    @prop({ required: true, default: false })
    captapultAtk: number;     // 投石车的攻击力

    @prop({ required: true, default: false })
    isCatapult: boolean;     // 是否是投石车

    @prop({ required: true, default: false })
    isBroken: boolean;     // 机器人是否被击破，如果没有被击破，那么这个位置上就还是机器人，如果被击破了，这个位置就空出给玩家

    originPointId: number;
    curTeamBreak: boolean;

    // 创建队伍
    public static async saveTeam(roleId: string, index: number, updateParam: SaveTeamUpdateParam, initParam: InitTeamParam) {
        const doc = new GVGTeamModel();
        const teamCode = genCode(8);
        const insert = Object.assign(doc.toJSON(), { ...initParam, teamCode });
        delete insert.lineup;
        const team: GVGTeamType | null = await GVGTeamModel.findOneAndUpdate({ roleId, index }, { $setOnInsert: insert, $set: updateParam }, { upsert: true, new: true }).lean();
        return team;
    }

    // 玩家切换城池更新队伍信息
    public static async enterCity(teamCode: string, cityId: number, areaId: number, groupKey: string) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { cityId, areaId, fromAreaId: areaId, pointId: 0, groupKey } }, { new: true }).lean();
        return team;
    }

    // 离开队伍
    public static async leaveCity(roleId: string) {
        let res = await GVGTeamModel.updateMany({ roleId }, { $set: { cityId: 0, areaId: 0, pointId: 0 } });
        return !!res["ok"];
    }

    // 查找角色队伍数
    public static async getTeamCntByRole(roleId: string) {
        const teams: GVGTeamType[] = await GVGTeamModel.find({ roleId }).select('teamCode').lean();
        return teams.length;
    }

    // 获取我的编队
    public static async findByRole(roleId: string, select = '') {
        const teams: GVGTeamType[] = await GVGTeamModel.find({ roleId }).select(select).lean();
        return teams;
    }

    // 获取我的编队
    public static async findByRoleAndIndex(roleId: string, index: number, select = '') {
        const teams: GVGTeamType = await GVGTeamModel.findOne({ roleId, index }).select(select).lean();
        return teams;
    }

    // 获取某个玩家的某个的编队
    public static async findMyTeamByCode(roleId: string, teamCode: string, select = '') {
        const res: GVGTeamType = await GVGTeamModel.findOne({ roleId, teamCode }).select(select).lean();
        return res;
    }

    // 获取编队
    public static async findByCode(teamCode: string, select = '') {
        const res: GVGTeamType = await GVGTeamModel.findOne({ teamCode }).select(select).lean();
        return res;
    }

    // 移动
    public static async startMove(teamCode: string, areaId: number, fromAreaId: number) {
        const res: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { areaId, fromAreaId, stopMoveTime: nowSeconds() + GVG.GVG_DEFAULT_MOVE_CD, moveCdTime: nowSeconds() + GVG.GVG_DEFAULT_MOVE_CD } }, { new: true }).lean();
        return res;
    }

    public static async stopMove(teamCode: string, areaId: number) {
        const res: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { areaId, stopMoveTime: nowSeconds() } }, { new: true }).lean();
        return res;
    }

    // 查询挑战和防守的编队
    public static async findBattleTeams(teamCode: string, oppoTeamCode: string) {
        const teams: GVGTeamType[] = await GVGTeamModel.find({ teamCode: { $in: [ teamCode, oppoTeamCode ] } }).lean();
        let attackTeam: GVGTeamType = null, defenseTeam: GVGTeamType = null;
        for(let team of teams) {
            if(team.teamCode == teamCode) attackTeam = team;
            if(team.teamCode == oppoTeamCode) defenseTeam = team;
        }
        return { attackTeam, defenseTeam }
    }

    // 每赛季初刷新自己的耐久等东西
    public static async refreshByConfig(teamCode: string, update: GVGTeamUpdate) {
        const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: update }, { new: true }).lean();
        return team;
    }

    public static async findRobotTeams(configId: number, groupKey: string, cityId: number) {
        const team: GVGTeamType[] = await GVGTeamModel.find({ configId, groupKey, cityId, isRobot: true }).lean();
        return team;
    }

    // 每赛季初刷新机器人
    public static async initRobots(configId: number, groupKey: string, cityId: number, dicPoints: DicGVGAreaPoint[], lv: number) {
        let teams: GVGTeamType[] = [];
        for(let { pointId, areaId, name, head, spine, ce, durability } of dicPoints) {
            let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ configId, groupKey, cityId, areaId, pointId }, {
                $setOnInsert: {teamCode: genCode(8), maxDurability: durability, roleName: name, head, spine, frame: EXTERIOR.EXTERIOR_FACECASE, lineupCe: ce, isRobot: true, lv, isBroken: false, startMoveTime: 0, stopMoveTime: 0, guildCode: '', leagueCode: '', leagueName: '', fromAreaId: areaId, roleId: GVG_ROBOT,
                }, $set: { durability }
            }, { new: true, upsert: true }).lean();
            teams.push(team);
        }
        return teams;
    }

    public static async findCatapultTeams(groupKey: string, cityId: number) {
        const team: GVGTeamType[] = await GVGTeamModel.find({ groupKey, cityId, isRobot: true, isCatapult: true }).lean();
        return team;
    }

    // 生成投石车
    public static async initCatapult(configId: number, groupKey: string, cityId: number, dicPoints: DicGVGAreaPoint[], leagueCode: string, leagueName: string, atk: number, durability: number, lv: number) {
        const teams: GVGTeamType[] = [];
        for(let { pointId, areaId, name, head, spine, ce } of dicPoints) {
            let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ configId, groupKey, cityId, areaId, pointId }, {
                $setOnInsert: {
                    teamCode: genCode(8), maxDurability: durability, roleName: name, head, spine, frame: EXTERIOR.EXTERIOR_FACECASE, lineupCe: ce, lv, leagueCode, leagueName, captapultAtk: atk,
                    isRobot: true, isCatapult: true, isBroken: false, startMoveTime: 0, stopMoveTime: 0, guildCode: '', fromAreaId: areaId, roleId: GVG_CATAPULT,
                }, $set: { durability }
            }, { new: true, upsert: true }).lean();
            teams.push(team);
        }
        return teams
    }

    // 攻击方攻击cd
    public static async battleStartLockAttack(teamCode: string) {
        const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { attackTime: nowSeconds() + GVG.GVG_DEFAULT_ATTACK_CD } }, { new: true }).lean();
        return team;
    }

    // 防守方锁定cd
    public static async battleStartLockDefense(teamCode: string, lockTeamCode: string) {
        const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { lockTime: nowSeconds() + GVG.GVG_DEFAULT_BATTLE_CD, lockTeamCode } }, { new: true }).lean();
        return team;
    }

    // 占领积分点
    public static async settlePoint(teamCode: string, pointId: number) {
        const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { pointId } }, { new: true }).lean();
        return team;
    }

    // 结算挑战方
    public static async battleEndAttack(teamCode: string, hpInc: number, rebirthAreaId: number, reviveCd: number) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $inc: { durability: hpInc } }, { new: true }).lean();
        if(team.durability <= 0) {
            let originPointId = team.pointId;
            team = await GVGTeamModel.teamBreak(teamCode, team.isRobot, team.maxDurability, rebirthAreaId, team.areaId, reviveCd);
            team.originPointId = originPointId;
        }
        return team;
    }

    // 结算防守方
    public static async battleEndDefense(teamCode: string, hpInc: number, rebirthAreaId: number, defenseCd: number, reviveCd: number) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { defenseTime: nowSeconds() + defenseCd, lockTime: 0, lockTeamCode: '' }, $inc: { durability: hpInc } }, { new: true }).lean();
        if(team.durability <= 0) {
            let originPointId = team.pointId;
            team = await GVGTeamModel.teamBreak(teamCode, team.isRobot, team.maxDurability, rebirthAreaId, team.areaId, reviveCd);
            team.originPointId = originPointId;
        }
        return team;
    }

    // 队伍进入修整器
    public static async teamBreak(teamCode: string, isRobot: boolean, maxDurability: number, areaId: number, fromAreaId: number, reviveCd: number) {
        if(!isRobot) {
            const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { restartTime: nowSeconds() + reviveCd, stopMoveTime: nowSeconds(), areaId, fromAreaId, durability: maxDurability, pointId: 0 } }, { new: true }).lean();
            team.curTeamBreak = true;
            return team;
        } else {
            const team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { fromAreaId, durability: 0, isBroken: true } }, { new: true }).lean();
            team.curTeamBreak = true;
            return team;
        }
    }

    public static async findByAreaId(configId: number, groupKey: string, cityId: number, areaId: number) {
        const team: GVGTeamType[] = await GVGTeamModel.find({ configId, groupKey, cityId, areaId, stopMoveTime: { $lte: nowSeconds() }, isBroken: false }).lean();
        return team;
    }

    public static async findByConfigId(configId: number) {
        const team: GVGTeamType[] = await GVGTeamModel.find({ configId, cityId: { $gt: 0 } }).lean();
        return team;
    }

    // 这个编队上是否已经有队伍了
    public static async checkPoint(configId: number, groupKey: string, pointId: number) {
        return await GVGTeamModel.exists({ configId, groupKey, pointId, isBroken: false });
    }

    // 投石车伤害
    public static async attackByCatapult(teamCode: string, inc: number, rebirthAreaId: number, reviveCd: number) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $inc: { durability: -inc } }, { new: true }).lean();
        let originPointId = team.pointId;
        if(team.durability <= 0) {
            team = await GVGTeamModel.findOneAndUpdate({ teamCode }, {  $set: { fromAreaId: team.areaId, areaId: rebirthAreaId, pointId: 0, durability: team.maxDurability, restartTime: nowSeconds() + reviveCd } }, { new: true }).lean();
            team.originPointId = originPointId;
            team.curTeamBreak = true;
        }
        return team;
    }

    // 加积分
    public static async addScore(teamCode: string, battleScore: number, settleScore: number) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $inc: { battleScore, settleScore } }, { new: true }).lean();
        return team;
    }
    
    // 复活
    public static async reviveTeam(teamCode: string) {
        let team: GVGTeamType = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $set: { restartTime: 0 } }, { new: true }).lean();
        return team;
    }

    public static async updateLeagueName(leagueCode: string, leagueName: string) {
        await GVGTeamModel.updateMany({ leagueCode }, { $set: { leagueName } });
    }

    public static async updateRoleInfo(roleId: string, info: { roleName?: string, lv?: number }) {
        await GVGTeamModel.updateMany({ roleId }, { $set: info });
    }

    public static async checkLockTeam(roleId: string, cityId: number) {
        return await GVGTeamModel.exists({ roleId, lockTime: { $gt: nowSeconds() }, cityId: { $ne: cityId } })
    }
}

export const GVGTeamModel = getModelForClass(GVGTeam);

export interface GVGTeamType extends Pick<DocumentType<GVGTeam>, keyof GVGTeam> {
    id: number;
};

export type GVGTeamUpdate = Partial<GVGTeamType>; // 将所有字段变成可选项