import { Schema } from '..';
import { basicTypeMap, getBasicTypeRef } from './basicTypes';
import { dataTypesMap } from './dataTypes';
import { getGenericTypeRef } from './genericTypes';
import { cloneDeep } from 'lodash';

/**
 * 会改变原来的结构，最好自己做深拷贝
 * @param schema
 * @param root
 * @example RefType - 用于前端界面的类型描述
 * {
 *     asset: {
 *         type: 'object',
 *         propertyList: [
 *             { name: 'id', $ref: '#/basicType/Long' },
 *             { name: 'name', $ref: '#/basicType/String' },
 *         ],
 *     },
 *     assetType: {
 *         type: 'enum',
 *         enumItemList: [
 *             { value: 'COMPUTER', label: '台式机' },
 *             { value: 'TABLET', label: '平板电脑' },
 *             { value: 'MOBILE', label: '移动电话' },
 *         },
 *     }，
 *     assets: {
 *         isArray: true,
 *         $ref: '#/xxxx/entity/asset'
 *     },
 *     test: { $ref: '#/basicType/Date' }
 * }
 */
export function convert2RefType(schema: Schema): Schema {
    if (!schema)
        return schema;
    if (schema.type === 'object') {
        if (schema.properties) {
            schema.propertyList = Object.keys(schema.properties).map((name) => {
                const property = schema.properties[name];
                property.name = name;
                return property;
            });
            delete schema.properties;
        }
        schema.propertyList = !schema.propertyList ? [] : schema.propertyList.map((property) => convert2RefType(property));
    } else if (schema.type === 'enum') {
        if (schema.enumItems) {
            schema.enumItemList = Object.keys(schema.enumItems).map((value) => ({
                level: 'enumItem',
                label: schema.enumItems[value],
                value,
            }));
        }
        delete schema.enumItems;
    } else if (schema.type === 'array') {
        schema.isArray = true;
        // 转成泛型
        const listSchema = dataTypesMap['#/genericTypes/List'];
        if (listSchema) {
            const genericSchema = {
                type: 'genericType',
                typeKey: '#/genericTypes/List',
                typeInstantiation: {
                    typeName: 'List',
                    typeParams: (listSchema.typeParams || []).map((param: any) => {
                        const tempParam = Object.assign({}, param);
                        tempParam.typeParamValue = convert2RefType(schema.items);
                        tempParam.type = 'typeParam';
                        return tempParam;
                    }),
                },
            };
            delete schema.items;
            Object.assign(schema, genericSchema);
        }
    } else if (schema.type === 'genericType') {
        if (schema.typeInstantiation) {
            schema.typeKey = getGenericTypeRef(schema.typeInstantiation.typeName);
            schema.typeInstantiation.typeParams.forEach((typeParam) => {
                typeParam.type = 'typeParam';
                convert2RefType(typeParam.typeParamValue);
            });
        }
    } else if (schema.type) {
        schema.typeKey = getBasicTypeRef(schema.type, schema.format);
    } else if (schema.$ref) {
        if (schema.$ref.startsWith('#/basicTypes')) { //兼容错误缓存情况
            schema.typeKey = schema.$ref;
            Object.assign(schema, dataTypesMap[schema.$ref]);
            delete schema.$ref;
        } else {
            schema.typeKey = schema.$ref;
            delete schema.type;
            delete schema.format;
        }
    }
    return schema;
}

/**
 * 会改变原来的结构，最好自己做深拷贝
 * @param schema
 * @param root
 * @example SchemaType - 非标准的 JSON Schema 结构
 * {
 *     asset: {
 *         type: 'object',
 *         propertyList: [
 *             { type: 'int', format: 'long' },
 *             { type: 'string' },
 *         ],
 *     },
 *     assetType: {
 *         type: 'enum',
 *         enumItemList: [
 *             { value: 'COMPUTER', label: '台式机' },
 *             { value: 'TABLET', label: '平板电脑' },
 *             { value: 'MOBILE', label: '移动电话' },
 *         },
 *     }，
 *     assets: {
 *         isArray: true,
 *         items: {
 *             $ref: '#/xxxx/entity/asset'
 *         }
 *     },
 *     test: { type: 'string', format: 'date' }
 * }
 */
export function convert2SchemaType(schema: Schema, options: { enum?: string } = {}) {
    if (!schema)
        return schema;

    if (schema.type === 'object') {
        schema.propertyList && schema.propertyList.forEach((property) => convert2SchemaType(property));
    } else if (schema.type === 'enum') {
        //
    } else if (schema.isArray) { // 去除
        //
    } else if (schema.$ref) { // 兼容老版本传$ref
        if (!schema.$ref.startsWith('#/basicTypes/')) {
            // 防止切换 type 时的污染
            delete schema.type;
            delete schema.format;
            Object.assign(schema, { typeKey: schema.$ref });
        } else {
            Object.assign(schema, { typeKey: schema.$ref });
            Object.assign(schema, basicTypeMap[schema.$ref]);
            delete schema.$ref;
        }
    } else if (schema.typeKey) { // 从s-datatype-select中选择类型后只传typeKey
        if (schema.typeKey.startsWith('#/genericTypes')) {
            if (!schema.typeInstantiation)
                return schema;
            schema = {
                type: 'genericType',
                typeKey: `#/genericTypes/${schema.typeInstantiation.typeName}`,
                typeInstantiation: {
                    typeName: schema.typeInstantiation.typeName,
                    typeParams: schema.typeInstantiation.typeParams.map((typeParam: any) => {
                        delete typeParam.tempColor;
                        delete typeParam.tempText;
                        typeParam.typeParamValue = convert2SchemaType(typeParam.typeParamValue);
                        typeParam.type = 'typeParam';
                        return typeParam;
                    }),
                },
            };
        } else if (!schema.typeKey.startsWith('#/basicTypes/')) { // { $ref: '', typeKey: ''}
            // 防止切换 type 时的污染
            schema = { $ref: schema.typeKey, typeKey: schema.typeKey };
        } else {
            schema = Object.assign({ typeKey: schema.typeKey }, basicTypeMap[schema.typeKey]);
        }
    }
    return schema;
}

/**
 * 根据 Ref Schema 生成子节点
 * @param schema
 * @param code
 * @param dataTypesMap
 */
export function genSchemaChildren(
    schema: Schema, code: string = '', usedSchemaRefs: { [name: string]: boolean } = {},
): Array<any> {
    const typeKey = schema.typeKey;
    if (typeKey && usedSchemaRefs[typeKey])
        return;
    // 理论上可以继续往下选，但这里先断掉！

    const next = dataTypesMap[typeKey];
    usedSchemaRefs = Object.assign({}, usedSchemaRefs);
    if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
        usedSchemaRefs[typeKey] = true;

    if (schema.type === 'genericType') {
        const children: Array<any> = [];
        const genericClass = dataTypesMap[schema.typeKey];
        if (genericClass) {
            const propertyList = genericClass.propertyList;
            propertyList.forEach((property: any) => {
                let propertySchema = cloneDeep(property);
                if (property.type === 'genericParam' && schema.typeInstantiation) {
                    const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
                    if (param) {
                        propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
                    }
                } else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
                    if (propertySchema.typeInstantiation) {
                        propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
                            const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
                            if (param) {
                                typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
                            }
                        });
                    }
                }
                const item: any = {
                    level: 'property',
                    name: propertySchema.name,
                    schema: propertySchema,
                    code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
                };
                if (schema.typeName === '#/genericTypes/List') {
                    item.arrayLength = true;
                }
                item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
                item.isLeaf = !item.children;
                children.push(item);
            });
        }
        return children;
    } else if (schema.type === 'object') {
        const children: Array<any> = [];
        schema.propertyList && schema.propertyList.forEach((property: Schema) => {
            const item: any = {
                level: 'property',
                name: property.name,
                schema: property,
                code: code + '.' + (property.id ? 'ID_' + property.id : property.name),
            };

            item.children = genSchemaChildren(item.schema, item.code, usedSchemaRefs);
            item.isLeaf = !item.children;
            children.push(item);
        });
        return children;
    } else if (schema.isArray) {
        //
    } else if (typeKey && typeKey.startsWith('#/basicTypes')) {
        return undefined;
    } else if (next) {
        return genSchemaChildren(next, code, usedSchemaRefs);
    }
}

/**
 * 根据 Ref Schema 生成子节点
 * @param schema
 * @param code
 */
export function genRefSchemaChildren(
    schema: Schema, code: string,
    usedSchemaRefs: { [name: string]: boolean } = {},
): Array<any> {
    if (!schema)
        return;
    const typeKey = schema.typeKey;
    if (typeKey && usedSchemaRefs[typeKey])
        return;
    // 理论上可以继续往下选，但这里先断掉！

    const next = dataTypesMap[typeKey];
    usedSchemaRefs = Object.assign({}, usedSchemaRefs);
    if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
        usedSchemaRefs[typeKey] = true;

    if (schema.type === 'genericType') {
        const children: Array<any> = [];
        const genericClass = dataTypesMap[schema.typeKey];
        if (genericClass) {
            const propertyList = genericClass.propertyList;
            propertyList.forEach((property: any) => {
                let propertySchema = cloneDeep(property);
                if (property.type === 'genericParam' && schema.typeInstantiation) {
                    const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
                    if (param) {
                        propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
                    }
                } else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
                    if (propertySchema.typeInstantiation) {
                        propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
                            const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
                            if (param) {
                                typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
                            }
                        });
                    }
                }
                const item: any = {
                    level: 'refProperty',
                    schema: propertySchema,
                    code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
                };
                if (schema.typeKey === '#/genericTypes/List') {
                    item.arrayLength = true;
                }
                item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
                item.isLeaf = !item.children;
                children.push(item);
            });
        }
        return children;
    } else if (schema.type === 'object') {
        const children: Array<any> = [];
        schema.propertyList && schema.propertyList.forEach((property: Schema) => {
            const item: any = {
                level: 'refProperty',
                schema: property,
                code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
            };

            item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
            item.isLeaf = !item.children;
            children.push(item);
        });
        return children;
    } else if (schema.isArray) {
        //
    } else if (typeKey && typeKey.startsWith('#/basicTypes')) {
        return undefined;
    } else if (next) {
        return genRefSchemaChildren(next, code, usedSchemaRefs);
    }
}

/**
 * 根据 Ref Schema 生成子节点
 * @param schema
 * @param code
 */
export function genFullRefSchemaChildren(schema: Schema, code: string, usedSchemaRefs: { [name: string]: boolean } = {}): Array<any> {
    if (!schema)
        return;

    const typeKey = schema.typeKey;
    if (typeKey && usedSchemaRefs[typeKey])
        return;
    // 理论上可以继续往下选，但这里先断掉！

    const next = dataTypesMap[typeKey];
    usedSchemaRefs = Object.assign({}, usedSchemaRefs);
    if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
        usedSchemaRefs[typeKey] = true;

    if (schema.type === 'genericType') {
        let children: Array<any> = [];
        if (schema.typeKey === '#/genericTypes/List') {
            children = genRefSchemaChildren(schema.typeInstantiation.typeParams[0].typeParamValue, code, usedSchemaRefs);
        } else {
            const genericClass = dataTypesMap[schema.typeKey];
            if (genericClass) {
                const propertyList = genericClass.propertyList;
                propertyList.forEach((property: any) => {
                    let propertySchema = cloneDeep(property);
                    if (property.type === 'genericParam' && schema.typeInstantiation) {
                        const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
                        if (param) {
                            propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
                        }
                    } else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
                        if (propertySchema.typeInstantiation) {
                            propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
                                const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
                                if (param) {
                                    typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
                                }
                            });
                        }
                    }
                    const item: any = {
                        level: 'refProperty',
                        schema: propertySchema,
                        code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
                    };
                    item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
                    item.isLeaf = !item.children;
                    children.push(item);
                });
            }
        }

        return children;
    } else if (schema.type === 'object') {
        const children: Array<any> = [];
        schema.propertyList && schema.propertyList.forEach((property: Schema) => {
            const item: any = {
                level: 'refProperty',
                schema: property,
                code: `${code}.ID_${property.id}`,
            };

            item.children = genFullRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
            item.isLeaf = !item.children;
            children.push(item);
        });
        return children;
    } else if (schema.isArray) {
        //
    } else if (typeKey && typeKey.startsWith('#/basicTypes')) {
        return undefined;
    } else if (next) {
        return genFullRefSchemaChildren(next, code, usedSchemaRefs);
    }
}

/**
 * 统一在 lcap-pc-template 中处理
 */
export function genInitData(schema: Schema, relationship = 'None', usedSchemaRefs: { [name: string]: boolean } = {}): any {
    throw new Error('统一在 lcap-pc-template 中处理！');
}
