import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose';
import { TASK_FUN_TYPE } from '../consts';
import { genCode } from '../pubUtils/util';
import { getZeroPointD } from '../pubUtils/timeUtil';
import { UpdateTaskParam } from '../domain/roleField/task';

/**
  * 玩家任务记录表
**/
@modelOptions({ schemaOptions: { id: false } })
@index({ code: 1 })
@index({ roleId: 1, type: 1, taskType: 1, group: 1 })

export default class UserTaskRec extends BaseModel {
    @prop({ required: true })
    code: string;					// 该记录唯一标识

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

    @prop({ required: true, enum: TASK_FUN_TYPE })
    type: number;					// 任务类型

    @prop({ required: true })
    taskType: number;  				// 行为类型

    @prop({ required: true })
    group: string;  				// 行为类型下的分组

    @prop({ required: true })
    count: number;  				// 达成次数

    @prop({ required: true, type: Number, default: [] })
    received: number[];  	    	// 是否已领取

    @prop({ required: true, type: String, default: [] })
    records: string[];  	    	// 历史记录

    private static getRefreshCondition(type: number) {
        let today = getZeroPointD();

        if(type == TASK_FUN_TYPE.DAILY || type == TASK_FUN_TYPE.PVP) {
            return { type, createdAt: { $gte: today } };
        } else {
            return { type };
        }
    }

    public static async setTaskRec(roleId: string, type: number, taskType: number, group: string, count: number, records?: string[]) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType = await UserTaskRecModel.findOneAndUpdate({ roleId, group, taskType, ...condition }, { $setOnInsert: { code: genCode(8), received: [] }, $set: { count, records: records||[] } }, { new: true, upsert: true }).lean();
        return rec;
    }

    public static async incTaskRec(roleId: string, type: number, taskType: number, group: string, count: number,  records?: string[]) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType = await UserTaskRecModel.findOneAndUpdate({ roleId, group, taskType, ...condition }, { $setOnInsert: { code: genCode(8), received: [] }, $inc: { count }, $set: { records: records||[] } }, { new: true, upsert: true }).lean();
        return rec;
    }


    public static async recordTaskRec(roleId: string, type: number, taskType: number, group: string, records?: string[]) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType = await UserTaskRecModel.findOneAndUpdate({ roleId, group, taskType, ...condition }, { $setOnInsert: { code: genCode(8), received: [], count: 0 }, $set: { records: records||[] } }, { new: true, upsert: true }).lean();
        return rec;
    }

    public static async setOrIncTask(roleId: string, type: number, taskType: number, group: string, param: UpdateTaskParam) {
        if(param.set) {
            return await this.setTaskRec(roleId, type, taskType, group, param.set, param.records);
        } else if (param.inc) {
            return await this.incTaskRec(roleId, type, taskType, group, param.inc, param.records);
        } else if (param.records) {
            return await this.recordTaskRec(roleId, type, taskType, group, param.records);
        }
    }

    public static async checkHistoryAndSetTaskRec(roleId: string, type: number, taskType: number, group: string, count: number) {
        let rec: UserTaskRecType = await UserTaskRecModel.findByRoleAndGroup(roleId, type, taskType, group);
        if(rec) {
            if(rec.count < count) {
                rec = await UserTaskRecModel.setTaskRec(roleId, type, taskType, group, count);
            }
        } else {
            rec = await UserTaskRecModel.setTaskRec(roleId, type, taskType, group, count);
        }
        return rec;
    }

    public static async checkRecordAndIncTaskRec(roleId: string, type: number, taskType: number, group: string, count: number, record: string) {
        let rec: UserTaskRecType = await UserTaskRecModel.findByRoleAndGroup(roleId, type, taskType, group);
        if(!rec || rec.records.indexOf(record) == -1) {
            let condition = this.getRefreshCondition(type);
            rec = await UserTaskRecModel.findOneAndUpdate({ roleId, group, taskType, ...condition }, { $setOnInsert: { code: genCode(8), received: [] }, $inc: { count }, $push: { records: record } }, { new: true, upsert: true }).lean();
        }
        return rec;
    }

    public static async findByRoleAndType(roleId: string, type: number) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType[] = await UserTaskRecModel.find({ roleId, ...condition }).lean();
        let map = new Map<number, Map<string, UserTaskRecType>>(); // taskType => group => userTask
        for(let userTaskRec of rec) {
            let { taskType, group } = userTaskRec;
            if(!map.has(taskType)) {
                map.set(taskType, new Map<string, UserTaskRecType>());
            }
            map.get(taskType).set(group, userTaskRec);
        }
        return map;
    }

    public static async findByRoleAndGroup(roleId: string, type: number, taskType: number, group: string) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType = await UserTaskRecModel.findOne({ roleId, taskType, group, ...condition }).lean();
        return rec;
    }
    
    public static async receiveTask(roleId: string, type: number, taskType: number, group: string, id: number) {
        let condition = this.getRefreshCondition(type);
        let rec: UserTaskRecType = await UserTaskRecModel.findOneAndUpdate({ roleId, taskType, group, ...condition }, { $push: { received: id } }, { new: true }).lean();
        return rec;
    }

    public static async getReceiveRec(roleId: string, type: number) {
        let condition = this.getRefreshCondition(type);
        let rec = await UserTaskRecModel.find({ roleId, ...condition }).lean();
        return rec
    }

    public static async getHistoryRec(roleId: string, type: number, today?: Date) {
        if(!today) today = getZeroPointD();
        let rec: UserTaskRecType[] = await UserTaskRecModel.find({ roleId, type, createdAt: { $lt: today } }).lean();
        return rec
    }

    public static async deleteHistory(history: UserTaskRecType[]) {
        let codes = history.map(cur => cur.code);
        let result = await UserTaskRecModel.deleteMany({ code: { $in: codes } });
        return result;
    }
}

export const UserTaskRecModel = getModelForClass(UserTaskRec);

export interface UserTaskRecType extends Pick<DocumentType<UserTaskRec>, keyof UserTaskRec> { }
export type UserTaskRecParam = Partial<UserTaskRecType>;
