import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { EXTERIOR } from '../pubUtils/dicParam';
import { nowSeconds } from '../pubUtils/timeUtil';
import { RoleUpdate } from './Role';
import { COM_TEAM_STATUS } from '../consts';

export class ComRoleStatusHero {
    @prop({ required: true })
    id: number;
    @prop({ required: true })
    skinId: number;
    @prop({ required: true })
    quality: number;
    @prop({ required: true })
    star: number;
    @prop({ required: true })
    colorStar: number;
    @prop({ required: true })
    lv: number;
    @prop({ required: true })
    damage: number = 0;
    constructor(hero: { hid: number, skinId: number, quality: number, star: number, colorStar: number, lv: number }) {
        this.id = hero.hid;
        this.skinId = hero.skinId;
        this.quality = hero.quality;
        this.star = hero.star;
        this.colorStar = hero.colorStar;
        this.lv = hero.lv;
    }

    addDamage(damage: number) {
        this.damage += damage;
    }
}

export class ComBattleReward {
    @prop({ required: true })
    type: number;
    @prop({ required: true })
    id: number;
    @prop({ required: true })
    count: number;
}

export class RoleStatus {
    @prop({ required: true })
    roleId: string;
    @prop({ required: true })
    roleName: string;
    @prop({ required: true })
    sid: string;
    @prop({ required: true })
    guildCode: string;
    @prop({ required: true })
    isCap: boolean;
    @prop({ required: true, default: 0 })
    totalDmg: number;
    // 战斗状态 0-挑战中 1-挑战成功 2-挑战失败
    @prop({ required: true, default: 0 })
    battleStatus: number;
    // 队伍中每个玩家的战斗编号
    @prop({ required: false, default: '' })
    battleCode: string;
    // 玩家所用阵容
    @prop({ required: false, type: ComRoleStatusHero, default: [], _id: false })
    heroes: ComRoleStatusHero[];
    // 阵亡武将
    @prop({ required: false, type: Number, default: [] })
    killed: number[];
    // 是否情谊助阵
    @prop({ required: false, default: false })
    isFrd: boolean;
    // 头像
    @prop({ required: true, default: EXTERIOR.EXTERIOR_FACE })
    head: number;
    // 相框
    @prop({ required: true, default: EXTERIOR.EXTERIOR_FACECASE })
    frame: number;
    // 形象
    @prop({ required: true, default: EXTERIOR.EXTERIOR_APPEARANCE })
    spine: number;
    // 前五战力
    @prop({ required: true, default: 0 })
    topLineupCe: number;
    // 主公等级
    @prop({ required: true, default: 1 })
    lv: number;
    // 是否机器人
    @prop({ required: true, default: false })
    isRobot: boolean;
    // 是否领奖
    @prop({ required: true, default: false })
    gotReward: boolean;
    // 队长奖励
    @prop({ required: true, default: [], type: ComBattleReward, _id: false })
    rewards: ComBattleReward[];
    // 好友间伤害加成
    @prop({ required: true, default: 0 })
    frdRatio: number = 0;

    constructor(role: RoleUpdate, sid: string, isCap: boolean, isFrd: boolean, heroes: ComRoleStatusHero[] = [], isRobot = false) {
        if(role) {
            this.roleId = role.roleId||'';
            this.roleName = role.roleName||'';
            this.guildCode = role.guildCode||'';
            this.head = role.head||EXTERIOR.EXTERIOR_FACE;
            this.frame = role.frame||EXTERIOR.EXTERIOR_FACECASE;
            this.spine = role.spine||EXTERIOR.EXTERIOR_APPEARANCE;
            this.topLineupCe = role.topLineupCe||0;
            this.lv = role.lv||1;
        }
        this.sid = sid;
        this.isCap = isCap;
        this.totalDmg = 0;
        this.isFrd = isFrd;
        this.heroes = heroes;
        this.killed = [];
        this.isRobot = isRobot;
        this.rewards = [];
    }

    addFrdRatio(frdRatio: number) {
        if(!this.frdRatio) {
            this.frdRatio = frdRatio;
        }
        if(this.frdRatio < frdRatio) {
            this.frdRatio = frdRatio;
        }
    }

    addHeroDamage(hid: number, damage: number) {
        let hero = this.heroes.find(cur => cur.id == hid);
        if(hero) hero.addDamage(damage);
    }
}

export class BossHp {
    // boss 战场标识
    @prop({ required: true, default: 0 })
    dataId: number;
    // boss 战场标识
    @prop({ required: true, default: 0 })
    actorId: number;
    // 总血量
    @prop({ required: true, default: 0 })
    hp: number;
    // 当前血量
    @prop({ required: true, default: 0 })
    curHp: number;
}

@index({ teamCode: 1 })
@index({ roleIds: 1 })
@index({ status: 1, blueprtId: 1, pub: 1})
export default class ComBattleTeam extends BaseModel {

    // 队伍唯一编号
    @prop({ required: true })
    teamCode: string;

    @prop({ required: true, type: String, default: [], _id: false })
    roleIds: string[];

    // 队伍是否开放加入
    @prop({ required: true, default: true })
    pub: boolean;

    // 对应藏宝图 Id
    @prop({ required: true })
    blueprtId: number;

    // 藏宝图品阶，用来匹配
    @prop({ required: true })
    lv: number;

    // 战斗状态 0：未开始，1：已开始，2：胜利，3：失败
    @prop({ required: true, default: 0 })
    status: number;

    @prop({ required: true, type: RoleStatus, default: [], _id: false })
    roleStatus: RoleStatus[];

    // 队长 roleId
    @prop({ required: true })
    capId: string;

    // 战力限制
    @prop({ required: true, default: 0 })
    ceLimit: number;

    // 队伍中人数
    @prop({ required: true, default: 1 })
    roleCnt: number;

    // 单个 boss 血量状态
    @prop({ required: false, type: BossHp, default: [], _id: false })
    bossHpArr: BossHp[];

    // 是否超时
    @prop({ required: true, default: false })
    timeout: boolean;

    // 是否撤退
    @prop({ required: true, default: false })
    retreat: boolean;

    // 黑名单
    blacklist: string[] = [];

    // 开始时间
    @prop({ required: true, default: 0 })
    startTime: number = 0;

    // 是否有特殊时间奖励
    @prop({ required: true, default: 0 })
    hasTimeExtraReward: boolean = false;

    // 结束时间
    @prop({ required: true, default: 0 })
    endTime: number = 0;
    
    // 是否发出邀请了
    @prop({ required: true, default: false })
    isInviting: boolean = false;

    // 邀请时间
    @prop({ required: true, default: 0 })
    inviteTime: number = 0;

    // 邀请对象
    @prop({ required: true, default: [], type: String, _id: false })
    inviteTarget: string[] = [];

    public static async createTeam(teamData: ComBattleTeamParam, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamData.teamCode }, {$set :{...teamData, roleCnt: teamData.roleIds.length}}, {upsert: true, new: true}).lean(lean);
        return team;
    }

    public static async findByRoleId(roleId: string) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOne({ roleIds: { $in: [roleId] }}).lean();
        return team;
    }

    public static async checkHasTeamByRoleId(roleId: string) {
        return await ComBattleTeamModel.exists({ roleIds: { $in: [roleId] }, status: COM_TEAM_STATUS.FIGHTING });
    }

    public static async addRole(teamCode: string, roleStatus: RoleStatus, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, roleCnt: { $lte: 2 } }, {$push: {roleIds: roleStatus.roleId, roleStatus}, $inc: {roleCnt: 1}}, {new: true}).lean(lean);
        return team;
    }

    public static async removeRole(teamCode: string, roleIdToRm: string, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode }, {$pull: {roleIds: roleIdToRm, roleStatus: {roleId: roleIdToRm}}, $inc: {roleCnt: -1}}, {new: true}).lean(lean);
        return team;
    }

    public static async updateRoleStFrd(teamCode: string, roleId: string, isFrd: boolean, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId }, { $set: {'roleStatus.$.isFrd': isFrd}}, {new: true}).lean(lean);
        return team;
    }

    public static async removeTeam(teamCode: string, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndDelete({ teamCode }).lean(lean);
        return team;
    }

    public static async updateHeroes(teamCode: string, roleId: string, heroes: ComRoleStatusHero[], lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.heroes': heroes}}, {new: true}).lean(lean);
        return team;
    }

    public static async setupBattleInfo(teamCode: string, roleId: string, heroes: ComRoleStatusHero[], battleCode: string, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.heroes': heroes, 'roleStatus.$.battleCode': battleCode}}, {new: true}).lean(lean);
        return team;
    }

    public static async updateRewardSt(teamCode: string, roleId: string, gotReward: boolean, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.gotReward': gotReward}}).lean(lean);
        return team;
    }

    public static async syncTeamData(teamData: {teamCode: string, status: number, roleStatus: RoleStatus[], bossHpArr: BossHp[], endTime?: number}, timeout = false, retreat = false) {
        // console.log('syncTeamData bossHpArr: ', teamData.bossHpArr);
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamData.teamCode }, {$set :{...teamData, roleCnt: teamData.roleStatus.length, timeout, retreat}}, {new: true}).lean();
        return team;
    }

    public static async updateStatusByCode(teamCode: string, status: number, startTime: number, endTime: number, hasTimeExtraReward: boolean) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamCode }, { $set: {status, startTime, endTime, hasTimeExtraReward }}, {new: true}).lean();
        return team;
    }

    public static async updateStatusByStatus(preStatus: number, newStatus: number, lean = true) {
        const team = await ComBattleTeamModel.updateMany({ status: preStatus }, {status: newStatus}, {new: true}).lean(lean);
        return team;
    }

    public static async updateResult(teamCode: string, roleId: string, isSuccess: boolean, _lean = true) {
        let team = await ComBattleTeamModel.findOne({teamCode}).select('status roleStatus');
        if(!team) return;
        if (isSuccess === true) {
            team.status = 2;
            team.roleStatus.forEach(st => {
                if (st.battleStatus !== 2) {
                    st.battleStatus = 1;
                }
            })
        } else if (isSuccess === false) {
            let loseCnt = 1;
            team.roleStatus.forEach(st => {
                if (st.battleStatus === 2 && st.roleId !== roleId) {
                    loseCnt += 1;
                } else if (st.roleId === roleId && !st.battleStatus) {
                    st.battleStatus = 2;
                }
            });
            if (loseCnt === team.roleStatus.length) {
                team.status = 3;
            }
        }
        await team.save();
        return team;
    }

    public static async getTeamByCapAndStatus(roleId: string, status: number, lean = true) {
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({capId: roleId, status}).lean(lean);
        return teams;
    }

    public static async getBlueprtInUse(roleId: string, status: number, blueprtId: number, lean = true) {
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({capId: roleId, status, blueprtId}).lean(lean);
        return teams;
    }

    public static async getTeamByBlueprt(blueprtIds: number[], status: number, pub = true, limit = 50, lean = true) {
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({blueprtId: {$in: blueprtIds}, status, pub}).limit(limit).lean(lean);
        return teams;
    }

    public static async getOneTeamByQualityAndSt(qualityArr: number[], status: number, ce = 0, pub = true, cntLmt = 2, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOne({quality: {$in: qualityArr}, status, ceLimit: {$lte: ce}, pub, roleCnt: {$lte: cntLmt}}).lean(lean);
        return team;
    }

    public static async getOtherTeamByLvAndSt(roleId: string, lvs: number[], status: number, ce = 0, pub = true, cntLmt = 2, lean = true) {
        const curTime = new Date(Date.now() - 10 * 60 * 1000); // 10分钟之前
        const team: ComBattleTeamType[] = await ComBattleTeamModel.find({ lv: { $in: lvs }, status, ceLimit: {$lte: ce}, pub, roleCnt: {$lte: cntLmt}, roleIds: {$nin: [roleId]}, updatedAt: {$gte: curTime}}).lean(lean);
        return team;
    }

    public static async getTeamByRoleAndTime(roleId: string, qualityArr?: number[], time?: Date, isAssist?: boolean, limitCnt = 50, lean = true) {
        let query = {roleIds: roleId};
        if (qualityArr) {
            query = Object.assign(query, {quality: {$in: qualityArr}});
        }
        if (time) {
            query = Object.assign(query, {createdAt: {$gte: time}});
        }
        if (isAssist) {
            query = Object.assign(query, {capId: {$ne: roleId}});
        }
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find(query).limit(limitCnt).sort({createdAt: -1}).lean(lean);
        return teams;
    }

    public static async getAssistTeamsByTime(roleId: string, time?: Date, isAssist?: boolean, lean = true) {
        let query = {roleIds: roleId, status: {$in: [0, 1, 2]}}; // 失败不计入助战
        if (time) {
            query = Object.assign(query, {createdAt: {$gte: time}});
        }
        if (isAssist) {
            query = Object.assign(query, {capId: {$ne: roleId}});
        }
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find(query).lean(lean);
        return teams;
    }

    public static async getCapExtraRewardCnt(roleId: string, time: Date) {
        const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({
            capId: roleId, createdAt: { $gte: time }, status: {$in: [0, 1, 2]}, hasTimeExtraReward: true
        }).lean();
        return teams;
    }

    public static async getTeamByRoleAndBattleCode(roleId: string, battleCode: string, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOne({roleIds: roleId, 'roleStatus.battleCode': battleCode}).lean(lean);
        return team;
    }

    public static async getTeamByCode(teamCode: string, lean = true) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOne({teamCode}).lean(lean);
        return team;
    }

    public static async invite(teamCode: string, target: string) {
        const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({teamCode}, { $set: { isInviting: true, inviteTime: nowSeconds()}, $push: { inviteTarget: target }}, {new: true}).lean();
        return team;
    }

    public static async findInvitations(roleId: string, minLv: number, maxLv: number, ce: number, refreshTime: number, limit: number) {
        const invitations: ComBattleTeamType[] = await ComBattleTeamModel.find({ inviteTarget: { $in: ['world', roleId] },status: 0, isInviting: true, lv: { $gte: minLv, $lte: maxLv }, ceLimit: { $lte: ce }, inviteTime: { $gte: refreshTime } }).sort({ inviteTime: -1 }).limit(limit).lean();
        return invitations;
    }
}

export const ComBattleTeamModel = getModelForClass(ComBattleTeam);

export interface ComBattleTeamType extends Pick<DocumentType<ComBattleTeam>, keyof ComBattleTeam>{};
export type ComBattleTeamParam = Partial<ComBattleTeamType>;