import { Provide } from "@midwayjs/core";
import { ComoNestUtils } from "como-nestjs-utils";
import { AggregateOptions, Attributes, BulkCreateOptions, CountOptions, CreateOptions, CreationAttributes, DataType, DestroyOptions, FindAndCountOptions, FindOptions, Identifier, UpdateOptions, WhereOptions } from "sequelize";
import { Model, Repository } from "sequelize-typescript";
import { AsyncHasOptions, GlobalOjectType } from "../interface";
import { ComoValidatorError } from "./ErrorDefaultFilter";
import { ModelDefaultValue } from "./ModelDefaultValue";

@Provide()
export class BaseAppService <R extends Model>{
    /**
     * 当前使用的模型
     */
    protected _useModel:Repository<Model> = null;
    /**
     * 动态关联
     */
    protected _asyncHasList:AsyncHasOptions[] = [];
    /**
     * 获取当前时间
     */
    get CurDateTime() {
        return ComoNestUtils.Ins.Date('Y-m-d H:i:s');
    }
    /**
     * 获取当前日期
     */
    get CurDate() {
        return ComoNestUtils.Ins.Date('Y-m-d');
    }
    /**
     * 获取当前时间戳
     */
    get CurTime() {
        return ComoNestUtils.Ins.Time();
    }
    /**
     * 获取当前使用的模型
     * 如果没有配置 为 null
     */
    GetModel() {
        return this._useModel;
    }
    /**
     * 自动关联挂载的模型
     * @param joinOptions 
     *        临时需要挂载关联的模型
     * @param notJoinsAs  
     *        哪些模型别名不需要本次挂载,好处就是以免挂载到本次用不到的模型
     */
    AutoJoin(joinOptions?:AsyncHasOptions[],notJoinsAs?:string[]) {
        notJoinsAs = notJoinsAs ?? [];
        this._CheckModelEntry();
        const _joins = this._asyncHasList.concat(joinOptions ?? []);
        for(let i = 0;i < _joins.length;i++) {
            const item = _joins[i];
            if(notJoinsAs.includes(item.joinOptions.as as string)) {
                continue;
            }
            if(!this._useModel.associations[item.joinOptions.as as string]) {
                if(item.joinName == 'hasOne') {
                    this._useModel.hasOne(item.model,item.joinOptions);
                } else if(item.joinName == 'hasMany') {
                    this._useModel.hasMany(item.model,item.joinOptions);
                }
            }
        }
        return this;
    }
    /**
     * 设置动态关联
     * @param joins 
     */
    SetJoinHas(joins:AsyncHasOptions[]) {
        this._asyncHasList = joins;
        return this;
    }
    /**
     * 获取模型的主键
     * @param model 
     * @returns 
     */
    GetPrimaryKey(model?:Repository<Model>) {
        const attributes = model?model.getAttributes():this._useModel.getAttributes();
        let primaryKey:string;
        for(const key in attributes) {
            const item = attributes[key];
            if(item.primaryKey) {
                primaryKey = item.field;
                break;
            }
        }
        return primaryKey;
    }
    /**
     * 检测当前是否配置模型
     */
    private _CheckModelEntry() {
        if(!this._useModel) throw new ComoValidatorError(`没有配置实体操作类,无法操作`);
    }
    /**
     * 查询单条数据
     * @param options 
     */
    async FindOne<M extends R>(options?:FindOptions<M>) {
        this._CheckModelEntry();
        const result = await this._useModel.findOne(options ?? {});
        return result;
    }
    /**
     * 主键查询
     * @param identifier 
     * @param options 
     * @returns 
     */
    async FindByPk<M extends Model>(identifier?: Identifier,options?: Omit<FindOptions<Attributes<M>>, 'where'>) {
        this._CheckModelEntry();
        const result = await this._useModel.findByPk(identifier,options);
        return result;
    }

    /**
     * 查询所有数据
     * @param options 
     */
    async FindAll<M extends R>(options?:FindOptions<M>) {
        this._CheckModelEntry();
        const result = await this._useModel.findAll(options ?? {});
        return result;
    }
    /**
     * 查询所有数据并返回条数
     * @param options 
     */
    async FindAllAndCount<M extends R>(options?:Omit<FindAndCountOptions<M>,'group'>) {
        this._CheckModelEntry();
        const result = await this._useModel.findAndCountAll(options ?? {});
        return result;
    }
    /**
     * 添加数据
     * @param data 
     * @param options 
     * @param toDefaultValue 
     */
    async Create<M extends R['_attributes']>(data:M,options?:CreateOptions<R>,toDefaultValue?:boolean) {
        this._CheckModelEntry();
        toDefaultValue = toDefaultValue ?? true;
        let createData = data;
        if(toDefaultValue) {
            createData = new ModelDefaultValue(this._useModel).CreateDefaultValue(data);
        }
        const result = await this._useModel.create(createData as Omit<any,string>,options ?? {});
        return result;
    }
    /**
     * 批量添加
     * @param records 
     * @param options 
     * @param toDefaultValue 
     * @returns 
     */
    async CreateAll<M extends R>(records: ReadonlyArray<CreationAttributes<M>>,options?: BulkCreateOptions<Attributes<M>>,toDefaultValue?:boolean) {
        this._CheckModelEntry();
        const datas = [];
        toDefaultValue = toDefaultValue ?? true;
        for(let i = 0;i < records.length;i++) {
            const tmp = new ModelDefaultValue(this._useModel).CreateDefaultValue(records[i]);
            datas.push(tmp);
        }
        const result = this._useModel.bulkCreate(datas,options);
        return result;
    }

    /**
     * 更新数据
     * @param data 
     * @param options 
     */
    async Update<M extends GlobalOjectType>(data:M,options:UpdateOptions<R>) {
        this._CheckModelEntry();
        const result = await this._useModel.update(data,options);
        return result;
    }
    /**
     * 删除数据
     * @param options 
     */
    async Delete<M extends R>(options:DestroyOptions<M>) {
        this._CheckModelEntry();
        const result = await this._useModel.destroy(options);
        return result;
    }
    /**
     * 主键删除
     * @param primaryVal 
     */
    async DelPrimary<M extends R>(primaryVal:string | number) {
        this._CheckModelEntry();
        const primaryKey = this.GetPrimaryKey();
        const whereOptions:WhereOptions<M> = {};
        whereOptions[primaryKey] = primaryVal;
        const options:DestroyOptions<M> = {
            where:whereOptions
        };
        const result = await this._useModel.destroy(options);
        return result;
    }
    /**
     * 数据统计
     * @param options 
     */
    async Count(options?:CountOptions<Attributes<R>>) {
        this._CheckModelEntry();
        const result = await this._useModel.count(options ?? {});
        return result;
    }
    /**
     * 数据求和
     * @param field 
     * @param options 
     * @returns 
     */
    async Sum<T extends DataType | unknown>(field: keyof Attributes<Model>,options?: AggregateOptions<T, Attributes<Model>>) {
        this._CheckModelEntry();
        const result = await this._useModel.sum(field,options ?? {});
        return result;
    }
    /**
     * 获取事务
     * @param model 
     */
    async Transaction(model?:R) {
        if(model) return await model.sequelize.transaction();
        return await this._useModel.sequelize.transaction();
    }
}