import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { LADDER_STATUS } from '../consts';
import { LadderDefense, LadderOppPlayerInfo, LadderOppPlayerHeroInfo } from '../domain/battleField/ladder';
import { genCode } from '../pubUtils/util';

@index({ battleCode: 1 })
export default class LadderMatchRec extends BaseModel {
    @prop({ required: true })
    battleCode: string;					// 唯一code
    @prop({ required: true })
    roleId1: string;				// 攻方玩家id
    @prop({ required: true })
    roleId2: string;				// 守方玩家id
    @prop({ required: true })
    serverId: number;					// 区 id
    @prop({ required: true })
    status: LADDER_STATUS;					// 1-出兵中 2-挑战中 3-挑战完毕
    @prop({ required: true })
    timeout: boolean;					// 是否超时
    @prop({ required: true })
    giveup: boolean;					// 是否取消
    @prop({ required: true })
    checkTime: number;					// 开始出兵的时间，13位时间戳
    @prop({ required: true })
    battleTime: number;				// 开始挑战的时间，13位时间
    @prop({ required: true })
    endTime: number;				// 结束时间，13位时间戳
    @prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false })
    attackInfo: LadderOppPlayerInfo;			     	// 攻方信息
    @prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false })
    defenseInfo: LadderOppPlayerInfo;			     	// 守方信息
    @prop({ required: true, type: () => LadderDefense, default: {}, _id: false })
    defense: LadderDefense;			     	// 守方信息

    @prop({ required: true, default: false })
    hasRpl: boolean;            // 是否存在对应录像

    public static async findByRoleId(roleId: string, getters = false) {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId }).lean({ getters});
        return result;
    }

    public static async findNotCompleteRecByRoleId(roleId: string, getters = false) {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId, status: { $in: [ LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK ] } }).lean({ getters});
        return result;
    }

    public static async createRec(serverId: number, roleId1: string, roleId2: string, defense: LadderDefense, attackInfo: LadderOppPlayerInfo, defenseInfo: LadderOppPlayerInfo) {
        const battleCode = genCode(10);
        const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, {
            $set: { roleId1, roleId2, serverId, defense, status: LADDER_STATUS.CHECK, checkTime: Date.now(), attackInfo, defenseInfo }
        }, { new: true, upsert: true }).lean();
        return result;
    }

    public static async timeout(battleCode: string) {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true, endTime: Date.now() } }, { new: true }).lean();
        return result;
    }

    public static async giveup(roleId: string, battleCode: string) {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ roleId1: roleId, battleCode }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: false, giveup: true, endTime: Date.now() } }, { new: true }).lean();
        return result;
    }

    public static async findByBattleCode(battleCode: string, select: string = '') {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ battleCode }).select(select).lean();
        return result;
    }

    public static async startBattle(battleCode: string, attackHeroes: LadderOppPlayerHeroInfo[]) {
        const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, {
            $set: { status: LADDER_STATUS.BATTLE, battleTime: Date.now(), 'attackInfo.heroes': attackHeroes }
        }, { new: true }).lean();
        return result;
    }

    public static async battleEnd(battleCode: string, isSuccess: boolean, endTime: number, atkNewRank?: number, defNewRank?: number) {
        let update: LadderMatchRecUpdate = { status: LADDER_STATUS.COMPLETE, endTime, timeout: false };
        if(isSuccess) {
            update['attackInfo.isSuccess'] = true;
            update['defenseInfo.isSuccess'] = false;
        }
        if(atkNewRank != undefined && defNewRank != undefined) {
            update['attackInfo.newRank'] = atkNewRank;
            update['attackInfo.oldRank'] = defNewRank;
            update['defenseInfo.newRank'] = defNewRank;
            update['defenseInfo.oldRank'] = atkNewRank;
        }

        const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: update }, { new: true }).lean();
        return result;
    }

    public static async findRec(roleId: string) {
        let recs: LadderMatchRecType[] = await LadderMatchRecModel.find({ 
            $or: [{roleId1: roleId}, { roleId2: roleId }],
            status: LADDER_STATUS.COMPLETE
        }).select({ battleCode: 1, roleId1: 1, roleId2: 1,  _id: -1, endTime: 1, attackInfo: 1, defenseInfo: 1, hasRpl: 1 }).limit(1000).lean();
        return recs;
    }

    /**
     * 按时间区间查询名将擂台战报（用战斗结束时间（endTime）做比较）
     * @static
     * @param {string} roleId
     * @param {number} startMs 开始时间（13位时间戳）
     * @return {*} 
     * @memberof LadderMatchRec
     */
    public static async findRecentRec(roleId: string, startMs: number) {
        let recs: LadderMatchRecType[] = await LadderMatchRecModel.find({
            endTime: { $gt: startMs },
            $or: [{roleId1: roleId}, { roleId2: roleId }],
            status: LADDER_STATUS.COMPLETE
        }).select({ battleCode: 1, roleId1: 1, roleId2: 1,  _id: -1, endTime: 1, attackInfo: 1, defenseInfo: 1, hasRpl: 1 }).limit(1000).lean();
        return recs;
    }

    public static async checkByRank(serverId: number, roleId2: string) {
        const result = await LadderMatchRecModel.exists({ serverId, roleId2, status: { $in: [LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK] } });
        return result;
    }

    public static async getUncompleteData() {
        let recs = await LadderMatchRecModel.find({ 
            $or: [
                { status: LADDER_STATUS.CHECK },
                { status: LADDER_STATUS.BATTLE }
            ]
         });
        return recs;
    }

    public static async timeoutMany(battleCode: string[]) {
        
        await LadderMatchRecModel.updateMany({ 
            battleCode: { $in: battleCode }
        }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true, endTime: Date.now() } });

    }

    public static async updateRplStatus(battleCode: string, hasRpl: boolean) {
        let result = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { hasRpl }, { new: true }).lean();
        return result;
    }
}

export const LadderMatchRecModel = getModelForClass(LadderMatchRec);

export interface LadderMatchRecType extends Pick<DocumentType<LadderMatchRec>, keyof LadderMatchRec> { };
export type LadderMatchRecUpdate = Partial<LadderMatchRecType>;