/**
 * 模型控制器  根据配置初始化模型
 */

import mongoose = require('mongoose');
import { ModelArray, TABLESOURCEENUM } from './model';
import { ERRORENUM } from '../../config/errorEnum';
import { BizError } from '../../util/bizError';

/**
 * 匹配mongo数据类型
 * @param typeInfo 
 * @returns 
 */
function checkTypeIsMongoDBType(typeInfo:any) {
    const TypeList = ['String', '[String]', 'Number', '[Number]', 'Boolean', 'Files'];
    if (typeof typeInfo == 'string') {
        return TypeList.indexOf(typeInfo) > -1;
    } else if (typeof typeInfo == 'object') {
        return TypeList.indexOf(typeInfo.type) > -1;
    }
    else throw new BizError(ERRORENUM.系统错误, `初始化 mongodb 的 model 时 ${typeInfo} 不可被系统识别`)
}


const FilesSchema = new mongoose.Schema({
    name:String,//文件名称
    url:String,//地址
}, {_id:false});


/**
 * 校验mongo类型
 * @param fileKey 
 * @param fileValue 
 * @returns 
 */
function eccMongoFile(fileKey, fileValue:any) {
    if (typeof fileValue == 'string') {
        switch(fileValue) {
            case 'String': return String; 
            case '[String]': return [String]; 
            case 'Number': return Number; 
            case '[Number]': return [Number]; 
            case 'Boolean': return Boolean;
            case 'Files': return [FilesSchema];
            // default: throw new BizError(ERRORENUM.系统错误, `${fileKey}字段类型为${fileValue} 解析失败`);
        }
    } else {
        let fileInfo:any = {type:null};
        if (fileValue.type) {
            switch(fileValue.type) {
                case 'String': fileInfo.type = String; break;
                case '[String]': fileInfo.type = [String]; break;
                case 'Number': fileInfo.type = Number; break;
                case '[Number]': fileInfo.type = [Number]; break;
                case 'Boolean': fileInfo.type = Boolean; break;
                case 'Files':
                    fileInfo.type = [FilesSchema]; 
                break;
            }
        } else throw new BizError(ERRORENUM.系统错误, `${fileKey}字段缺失类型 解析失败`);
        if (fileValue.index) fileInfo.index = true;
        if (fileValue.default != null || fileValue.default != undefined) {
            fileInfo.default = fileValue.default;
        }
        return fileInfo;
    }
    
}
let dataModels = {};

/**
 * 初始化mongodb数据库模型
 * @param dataBase 
 */
async function initMongoModel(myMongoDB:any) {
    /**得到文档树 */
    let modelListData = [];
    let modelMapData = {};
    ModelArray.forEach(info => {
        let {tableName, source} = info;
        if (source == TABLESOURCEENUM.mongo) {
            modelMapData[tableName] = info;
            modelListData.push(info);
        }
       
    });
    let distinctMap = {};
    function arrayToObject(array, parentKey) {
        let modelSchema = {};
        for (let i = 0; i < array.length; i++) {
            let item = typeof array[i] == 'string' ? modelMapData[array[i]]: array[i];
            let {tableName, schema} = item;
            if (!parentKey) {
                modelSchema[tableName] = {};
                for (let key in schema) {
                    /**解析字段 */
                    let fileKey = key;
                    let fileValue = schema[fileKey];

                    if ( !checkTypeIsMongoDBType(fileValue) ) {//非基础数据类型
                        modelSchema[tableName][key] = arrayToObject([ modelMapData[fileValue] ], tableName);
                        distinctMap[fileValue] = 1;
                    } else {
                        modelSchema[tableName][key] = eccMongoFile(fileKey, fileValue);
                    }
                }
            } else {
                for (let key in schema) {
                    /**解析子文档字段 */
                    let fileKey = key;
                    let fileValue = schema[fileKey];

                    if ( !checkTypeIsMongoDBType(fileValue) ) {//非基础数据类型
                        modelSchema[key] = arrayToObject([ modelMapData[fileValue] ], tableName);      
                        distinctMap[fileValue] = 1;
                    } else {
                        modelSchema[key] = eccMongoFile(fileKey, fileValue);
                    }
                }
            }

        }
        return parentKey ? new mongoose.Schema(modelSchema) : modelSchema;
    }
    let tree = arrayToObject(modelListData, '');
    for (let tableName in tree) {
        if (distinctMap[tableName]) continue;
        dataModels[tableName] = myMongoDB.model(tableName, tree[tableName]);
        dataModels[tableName].addOneData = async function(data) {
            return await dataModels[tableName].create(data);
        }
        dataModels[tableName].addManyData = async function(dataList) {
            return await dataModels[tableName].insertMany(dataList);
        }
        dataModels[tableName].findCount = async function(param) {
            return await dataModels[tableName].find(param).countDocuments();
        }
        dataModels[tableName].updateManyData = async function(param, updateInfo) {
            return await dataModels[tableName].updateMany(param, {$set:updateInfo});
        }
        dataModels[tableName].updateOneData = async function(param, updateInfo) {
            return await dataModels[tableName].updateOne(param, {$set:updateInfo});
        }
        dataModels[tableName].deleteManyData = async function(param) {
            return await dataModels[tableName].deleteMany(param);
        }
        dataModels[tableName].deleteOneData = async function(param) {
            return await dataModels[tableName].deleteOne(param);
        }
        
        
    }
    console.log(`mongo database model init success`);
}

async function initMysqlModel(mysqlDB:any) {
    /**初始化表 */
    for (let i =0; i < ModelArray.length; i++) {
        let { tableName, schema, source } = ModelArray[i];

        if (source == TABLESOURCEENUM.mysql) {
            let schemaConf = {
                freezeTableName:true, //true表示使用给定的表名，false表示模型名后加s作为表名
                timestamps:false  //true表示给模型加上时间戳属性(createAt、updateAt),false表示不带时间戳属性
            };
    
            let model = mysqlDB.define( tableName, schema, schemaConf);
            
            dataModels[tableName] = await model.sync({}).then();
        }
        
        
    }

     /**初始化表关联 */
     for (let i =0; i < ModelArray.length; i++) {
        let { tableName, association }:any  = ModelArray[i];
        association = association || [];
        association.forEach( (item:any) => {
            if (item) {
                let {type, check, foreignKey} = item;
                if (type == "hasOne") {
                    dataModels[check].hasOne(dataModels[tableName]);
                } else if (type == "hasMany") {
                    dataModels[tableName].hasMany(dataModels[check], {foreignKey});
                }
                dataModels[check].belongsTo(dataModels[tableName], {foreignKey});
            }
        });
        
    }

    console.log(`mysql database model init success`);
}


export {initMongoModel, initMysqlModel, dataModels};