import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, mongoose } from '@typegoose/typegoose';
import { RoleType } from './Role';
import { getZeroPoint } from '../pubUtils/timeUtil';

export class Lineup {
    @prop({ required: true })
    actorId: number; // 武将
    @prop({ required: true })
    dataId: number;  // 出兵表上所占位置
    @prop({ required: true })
    order: number;   // 行动顺序
    @prop({ required: true })
    ce: number;      // 战力
}

@index({ day: 1, groupKey: 1, vestigeId: 1, rank: 1 })
@index({ roleId: 1 })
export default class GVGVestigeRank extends BaseModel {

    @prop({ required: true })
    configId: number;       // 赛期

    @prop({ required: true })
    day: number;           // 每天5点

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

    @prop({ required: true })
    vestigeId: number;     // 遗迹id

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

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

    @prop({ required: true })
    name: string;           // 联军名字

    @prop({ required: true, default: 0 })
    rank: number;          // 排名
    
    @prop({ required: true, default: 0 })
    oldRank: number;       // 交换前排名
    
    @prop({ required: true, default: 0 })
    historyRank: number;   // 历史最高排名

    @prop({ required: true, default: false })
    hasDefense: boolean;  // 是否调整过阵容，首次挑战自动保存阵容

    @prop({ required: true, type: Lineup, default: [], _id: false })
    lineup: Lineup[];         // 阵容

    @prop({ required: true, type: Number, default: [], _id: false })
    oppRanks: number[];         // 对手的排名

    @prop({ required: true, default: 0 })
    refreshCnt: number;         // 刷新次数

    public static async findAllByRole(roleId: string) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, roleId }).lean();
        return result;
    }

    public static async findByRole(vestigeId: number, roleId: string) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType = await GVGVestigeRankModel.findOne({ day: today, vestigeId, roleId }).lean();
        return result;
    }

    public static async findByRank(configId: number, groupKey: string, vestigeId: number, rank: number) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType = await GVGVestigeRankModel.findOne({ configId, groupKey, day: today, vestigeId, rank }).lean();
        return result;
    }

    public static async initRank(configId: number, vestigeId: number, groupKey: string, role: RoleType, leagueCode: string, name: string, oppRanks: number[]) {
        let today = getZeroPoint();
        let doc = new GVGVestigeRankModel();
        let update = Object.assign(doc.toJSON(), { role: role._id, configId, groupKey, oppRanks, leagueCode, name });
        let result: GVGVestigeRankType = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId: role.roleId }, { $setOnInsert: update }, { new: true, upsert: true }).lean();
        return result;
    }

    public static async updateByRoleId(vestigeId: number, roleId: string, update: GVGVestigeRankUpdate) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId }, { $set: update }, { new: true, upsert: true }).lean();
        return result;
    }

    public static async findByRanks(groupKey: string, vestigeId: number, ranks: number[]) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, vestigeId, groupKey, rank: { $in: ranks } })
        .lean();
        return result;
    }

    public static async updateCe(roleId: string, hid: number, ce: number) {
        let today = getZeroPoint();
        await GVGVestigeRankModel.updateMany({ day: today, hasDefense: true, roleId, 'lineup.actorId': hid }, { $set: { 'lineup.$.ce': ce } });
    }

    public static async changeRank(isSuccess: boolean, vestigeId: number, attackInfo: { roleId: string }, defenseInfo: { isRobot: boolean, roleId: string, oldRank: number }): Promise<{ isChange: boolean, atkData?: GVGVestigeRankType, defData?: GVGVestigeRankType, endTime: number }> {
        
        let today = getZeroPoint();
        const session = await mongoose.connection.startSession();
        session.startTransaction()
        try {
            let newAtkData: GVGVestigeRankType, newDefData: GVGVestigeRankType, isChange = false, endTime = Date.now();
            if(defenseInfo.isRobot) {
                let atkData = await GVGVestigeRankModel.findOne({ day: today, vestigeId, roleId: attackInfo.roleId }).lean();
                if(isSuccess && atkData && (atkData.rank == 0 || atkData.rank > defenseInfo.oldRank)) {
                    newAtkData = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId: attackInfo.roleId }, { $set: { rank: defenseInfo.oldRank, oldRank: atkData.rank } }, { new: true, session }).lean();
                    isChange = true;
                    endTime = Date.now();
                } else {
                    newAtkData = atkData;
                }
            } else {
                let atkData = await GVGVestigeRankModel.findOne({ day: today, vestigeId, roleId: attackInfo.roleId }).lean();
                let defData = await GVGVestigeRankModel.findOne({ day: today, vestigeId, roleId: defenseInfo.roleId }).lean();
                if(isSuccess && atkData && defData && (atkData.rank == 0 || atkData.rank > defData.rank)) {
                    newAtkData = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId: attackInfo.roleId }, { $set: { rank: defData.rank, oldRank: atkData.rank } }, { new: true, session }).lean();
                    newDefData = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId: defenseInfo.roleId }, { $set: { rank: atkData.rank, oldRank: defData.rank } }, { new: true, session }).lean();
                    isChange = true;
                    endTime = Date.now();
                } else {
                    newAtkData = atkData;
                    newDefData = defData;
                }
            }
            await session.commitTransaction()
            return { isChange, atkData: newAtkData, defData: newDefData, endTime }
        } finally {
            session.endSession()
        }
    }
    
    public static async refreshOpp(roleId: string, vestigeId: number, oppRanks: number[]) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId }, { $set: { oppRanks }, $inc: { refreshCnt: 1 } }, { new: true, upsert: true }).lean();
        return result;
    }

    public static async findRank(): Promise<{ _id: { groupKey: string, vestigeId: number }, arr: { roleId: string, rank: number }[], roleIds: string[] }[]> {
        let today = getZeroPoint();
        let ranks = await GVGVestigeRankModel.aggregate([
            { $match: { day: today } },
            { $group: { 
                _id: { groupKey: "$groupKey", vestigeId: "$vestigeId" }, 
                arr: { $push: { roleId: "$roleId", rank: "$rank" } }, roleIds: { $addToSet: "$roleId" }
            }},
        ]);
        return ranks;
    }

    public static async findRankByVestige(groupKey: string, vestigeId: number) {
        let today = getZeroPoint();
        let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, vestigeId, groupKey, rank: { $gt: 0 } }).sort({ rank: 1 }).select('roleId rank lineup').lean();
        return result;
    }
}

export const GVGVestigeRankModel = getModelForClass(GVGVestigeRank);

export interface GVGVestigeRankType extends Pick<DocumentType<GVGVestigeRank>, keyof GVGVestigeRank> {};
export type GVGVestigeRankUpdate = Partial<GVGVestigeRankType>; // 将所有字段变成可选项
