/**
 * knex仓储模块
 * @date 2022-03
 * @author 何华东
 */

import { logger } from "mslib";
import Contants from "./Contants";
import SqlUtils from "./SqlUtils";

/**
 * 生成mysql knex实例
 * 
 * 方法已给定一些默认配置且接入日志logger,外部只需传递connection配置即可
 * @param config 
 * @returns 
 */
export function generateMySqlKnexInstance(config: {
    connection: {
        host: string,
        port: number,
        user: string,
        password: string,
        database: string,
        multipleStatements?: boolean
    }
}) {
    const defaults = {
        client: 'mysql2',
        pool: { min: 0, max: 10 },
        debug: Contants.IS_DEBUG,
        log: {
            warn(message) {
                logger.warn(message);
            },
            error(message) {
                logger.error(message);
            },
            deprecate(message) {
                logger.error(message);
            },
            debug(message) {
                logger.debug(message);
            },
        }
    };

    return require('knex')(Object.assign(defaults, config));
}

/**
 * knex仓储基类
 */
export abstract class KnexRepositoryBase<T extends object> {
    abstract knexInstance: any;
    abstract tableName: string;

    /**
     * 返回一个knex可执行对象
     * 
     * knex语法较多，但可以从这里入手
     * @returns 
     */
    execable() {
        return this.knexInstance(this.tableName);
    }

    /**
     * 插入实体
     * @param entity 要插入的实体
     */
    async insert(entity: T) {
        await this.execable().insert(entity);
    }

    /**
     * 批量插入实体
     * @param entities 
     */
    async batchInsert(entities: T[]) {
        await this.knexInstance.batchInsert(this.tableName, entities);
    }

    /**
     * replace 会丢失数据，慎用
     * knex connection 需开启 multipleStatements: true
     * @param entity 
     */
    async replace(entity: T) {
        let sql = SqlUtils.generateReplaceSql(this.tableName, entity);
        await this.knexInstance.raw(sql);
    }

    /**
     * replace 会丢失数据，慎用
     * knex connection 需开启 multipleStatements: true
     * @param entity 
     */
    async batchReplace(entities: T[]) {
        if (!entities || entities.length === 0) return;

        let sqlList = [];
        for (const entity of entities) {
            sqlList.push(SqlUtils.generateReplaceSql(this.tableName, entity));
        }

        await this.knexInstance.raw(sqlList.join(';'));
    }
}

/**
 * knex 实体仓储基类，需包含主键id
 */
export abstract class KnexEntityRepositoryBase<T extends object & { id: any }> extends KnexRepositoryBase<T> {
    async delete(entity: T) {
        return await this.execable().where({ id: entity.id }).del();
    }

    async deleteById(id: any) {
        return await this.execable().where({ id: id }).del();
    }

    async update(entity: T) {
        return await this.execable().where({ id: entity.id }).update(entity);
    }

    async findById(id: any): Promise<T> {
        return await this.execable().where({ id: id }).first();
    }

    async getById(id: any): Promise<T> {
        let entity = await this.execable().where({ id: id }).first();
        if (!entity) throw `entity not found,id:${id}`;

        return entity;
    }
}