import { dataTypesMap, vertexsMap, utils, Entity, View, EntityProperty, Enum, LEVEL_ENUM } from '../..';
import {
    filterProperty, NameGroup, genUniqueQueryNameGroup, getFirstDisplayedProperty, getParamFromResolver, genGenericTypeSchema, genVariable, genInterParam, genCallComponentLogic, genCallInterface, genQueryInterface, genQueryStructure,
    genEnumSelectBlock, genTableTemplate, genTableLoadLogic, genTableRemoveLogic, genLoadSelectLogic, genGridViewTemplate, genGridViewLoadLogic, genGridViewRemoveLogic,
} from '.';
import builtInFunctions = require('./builtInFunctions.json');

function genFilterTemplate(entity: Entity, nameGroup: NameGroup, selectNameGroupMap: Map<string, NameGroup>, tableRefName = 'tableView') {
    const propertyList = entity.propertyList
        .filter(filterProperty('inFilter'));

    return `
<u-linear-layout justify="space-between">
    <u-form layout="inline" label-size="auto">
    ${propertyList.map((property) => {
        const label = property.label || property.name;
        let formItem = `<u-form-item label="${label}">`;

        if (property.$relationEntity) { // 有外键关联
            const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
            if (relationEntity) {
                const relationProperty = vertexsMap.get(property.$relationProperty) as EntityProperty;
                const displayedProperty = getFirstDisplayedProperty(relationEntity, 'inFilter');
                if (displayedProperty) {
                    const lowerEntityName = utils.firstLowerCase(relationEntity.name);
                    formItem += `
                        <u-select clearable placeholder="请选择${label}" :data-source="${selectNameGroupMap.get(relationEntity.id).load}"
                            text-field="${lowerEntityName}.${displayedProperty.name}" value-field="${lowerEntityName}.${relationProperty.name}" pageable remote-paging v-model="filter.${property.name}">
                        </u-select>
                    `;
                } else
                    return '';
            } else
                return '';
        } else if (property.typeKey === '#/basicTypes/Boolean') {
            formItem += `
                        <u-select clearable v-model="filter.${property.name}" placeholder="请输入${label}">
                            <u-select-item :value="true">是</u-select-item>
                            <u-select-item :value="false">否</u-select-item>
                        </u-select>
                    `;
        } else if (property.typeKey === '#/basicTypes/Integer' || property.typeKey === '#/basicTypes/Long') {
            formItem += `<u-number-input v-model="filter.${property.name}" placeholder="请输入${label}"></u-number-input>`;
        } else if (property.typeKey === '#/basicTypes/Decimal') {
            formItem += `<u-number-input v-model="filter.${property.name}" :precision="0" :step="0" placeholder="请输入${label}"></u-number-input>`;
        } else if (property.typeKey === '#/basicTypes/Text') {
            formItem += `<u-textarea v-model="filter.${property.name}" placeholder="请输入${label}"></u-textarea>`;
        } else if (property.typeKey === '#/basicTypes/Date') {
            formItem += `<u-date-picker clearable :date.sync="filter.${property.name}" placeholder="请输入${label}"></u-date-picker>`;
        } else if (property.typeKey === '#/basicTypes/Time') {
            formItem += `<u-time-picker :time.sync="filter.${property.name}" placeholder="请输入${label}"></u-time-picker>`;
        } else if (property.typeKey === '#/basicTypes/DateTime') {
            formItem += `<u-date-time-picker clearable :date.sync="filter.${property.name}" placeholder="请输入${label}"></u-date-time-picker>`;
        } else if (dataTypesMap[property.typeKey].type === 'enum') {
            formItem += genEnumSelectBlock(dataTypesMap[property.typeKey] as Enum, `filter.${property.name}`, '', `请选择${label}`);
        } else {
            formItem += `<u-input v-model="filter.${property.name}" placeholder="请输入${label}"></u-input>`;
        }

        formItem += `</u-form-item>`;
        return formItem;
    }).join('')}

        <u-form-item>
            <u-button color="primary" @click="$refs.${tableRefName}.reload()">查询</u-button>
        </u-form-item>
    </u-form>

    <u-button color="primary" @click="${nameGroup.create}($event)">创建</u-button>
</u-linear-layout>
    `;
}

function genSaveModalTemplate(entity: Entity, nameGroup: NameGroup, selectNameGroupMap: Map<string, NameGroup>) {
    const propertyList = entity.propertyList
        .filter(filterProperty('inForm'));

    const idProperty = entity.propertyList.find((item) => item.primaryKey || item.name === 'id');

    return `
    <u-modal ref="saveModal">
        <template #title>
            <div v-if="input.${idProperty.name}">修改</div>
            <div v-if="input.${idProperty.name} == undefined">创建</div>
        </template>
        <template #body>
            <u-form ref="form1">
    ${propertyList.map((property) => {
        const label = property.label || property.name;
        const required = !!property.required;
        const rules = [];
        if (property.rules && property.rules.length) {
            property.rules.forEach((rule) => rules.push(rule));
        }
        if (required)
            rules.push('required');
        const rulesStr = rules.join(' | ');

        let formItem = `<u-form-item
                                    label="${label}"
                                    ${required ? 'required' : ''}
                                    ${rulesStr.length ? `rules="${rulesStr}"` : ''}
                                >`;

        if (property.$relationEntity) { // 有外键关联
            const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
            if (relationEntity) {
                const relationProperty = vertexsMap.get(property.$relationProperty) as EntityProperty;
                const displayedProperty = getFirstDisplayedProperty(relationEntity, 'inForm');
                if (displayedProperty) {
                    const lowerEntityName = utils.firstLowerCase(relationEntity.name);
                    formItem += `
                        <u-select clearable placeholder="请选择${label}" :data-source="${selectNameGroupMap.get(relationEntity.id).load}"
                            text-field="${lowerEntityName}.${displayedProperty.name}" value-field="${lowerEntityName}.${relationProperty.name}" pageable remote-paging v-model="input.${property.name}">
                        </u-select>
                    `;
                } else
                    return '';
            } else
                return '';
        } else if (property.typeKey === '#/basicTypes/Boolean') {
            formItem += `
                        <u-select clearable v-model="input.${property.name}" placeholder="请输入${label}">
                            <u-select-item :value="true">是</u-select-item>
                            <u-select-item :value="false">否</u-select-item>
                        </u-select>`;
        } else if (property.typeKey === '#/basicTypes/Integer' || property.typeKey === '#/basicTypes/Long') {
            formItem += `<u-number-input v-model="input.${property.name}" placeholder="请输入${label}"></u-number-input>`;
        } else if (property.typeKey === '#/basicTypes/Decimal') {
            formItem += `<u-number-input v-model="input.${property.name}" :precision="0" :step="0" placeholder="请输入${label}"></u-number-input>`;
        } else if (property.typeKey === '#/basicTypes/Text') {
            formItem += `<u-textarea v-model="input.${property.name}" placeholder="请输入${label}"></u-textarea>`;
        } else if (property.typeKey === '#/basicTypes/Date') {
            formItem += `<u-date-picker clearable :date.sync="input.${property.name}" placeholder="请输入${label}"></u-date-picker>`;
        } else if (property.typeKey === '#/basicTypes/Time') {
            formItem += `<u-time-picker :time.sync="input.${property.name}" placeholder="请输入${label}"></u-time-picker>`;
        } else if (property.typeKey === '#/basicTypes/DateTime') {
            formItem += `<u-date-time-picker clearable :date.sync="input.${property.name}" placeholder="请输入${label}"></u-date-time-picker>`;
        } else if (dataTypesMap[property.typeKey].type === 'enum') {
            formItem += genEnumSelectBlock(dataTypesMap[property.typeKey] as Enum, `input.${property.name}`, '', `请选择${label}`);
        } else {
            formItem += `<u-input v-model="input.${property.name}" placeholder="请输入${label}"></u-input>`;
        }

        formItem += `</u-form-item>\n`;

        return formItem;
    }).join('')}
            </u-form>
        </template>
        <template #foot>
            <u-linear-layout>
                <u-button color="primary" @click="${nameGroup.submit}()" v-if="input.${idProperty.name}">提交修改</u-button>
                <u-button color="primary" @click="${nameGroup.submit}()" v-if="input.${idProperty.name} == undefined">立即创建</u-button>
            </u-linear-layout>
        </template>
    </u-modal>`;
}

function genInitLogic(entity: Entity, nameGroup: NameGroup) {
    return {
        level: 'logic',
        expanded: false,
        name: nameGroup.init,
        description: '',
        params: [] as Array<any>,
        returns: [] as Array<any>,
        variables: [] as Array<any>,
        body: [
            {
                level: 'logicNode',
                type: 'Start',
                label: '开始',
            },
            {
                level: 'expressionNode',
                type: 'BuiltInFunction',
                label: '内置函数',
                calleeCode: 'Clear',
                builtInFuncParams: [
                    {
                        parentAttr: 'builtInFuncParams',
                        level: 'expressionNode',
                        type: 'BuiltInFuncParam',
                        name: 'obj',
                        builtInFuncParamValue: {
                            parentAttr: 'builtInFuncParamValue',
                            level: 'expressionNode',
                            type: 'Identifier',
                            name: 'filter',
                        },
                        schema: builtInFunctions.Clear.params[0].schema,
                    },
                ],
            },
            {
                level: 'logicNode',
                type: 'End',
                label: '结束',
            },
        ],
        playground: [] as Array<any>,
    };
}

function genCreateLogic(entity: Entity, nameGroup: NameGroup) {
    return {
        level: 'logic',
        expanded: false,
        name: nameGroup.create,
        description: '',
        params: [] as Array<any>,
        returns: [] as Array<any>,
        variables: [] as Array<any>,
        body: [
            {
                level: 'logicNode',
                type: 'Start',
                label: '开始',
            },
            {
                level: 'logicNode',
                type: 'AssignmentExpression',
                label: '赋值',
                operator: '=',
                left: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'input',
                },
                right: {
                    parentAttr: 'right',
                    level: 'expressionNode',
                    type: 'BuiltInFunction',
                    label: '内置函数',
                    calleeCode: 'Clone',
                    builtInFuncParams: [
                        {
                            parentAttr: 'builtInFuncParams',
                            level: 'expressionNode',
                            type: 'BuiltInFuncParam',
                            name: 'obj',
                            builtInFuncParamValue: {
                                parentAttr: 'builtInFuncParamValue',
                                level: 'expressionNode',
                                type: 'Identifier',
                                name: nameGroup.lowerEntity,
                            },
                            schema: builtInFunctions.Clone.params[0].schema,
                        },
                    ],
                },
            },
            genCallComponentLogic('saveModal', 'open'),
            {
                level: 'logicNode',
                type: 'End',
                label: '结束',
            },
        ],
        playground: [] as Array<any>,
    };
}

function genModifyLogic(entity: Entity, nameGroup: NameGroup) {
    const scopeStructureSchema = genGenericTypeSchema('ScopeOf', { T: { $ref: nameGroup.structure } });

    return {
        level: 'logic',
        expanded: false,
        name: nameGroup.modify,
        description: '',
        params: [
            {
                level: 'param',
                type: 'Identifier',
                name: 'event',
                schema: {
                    type: 'string',
                    format: '',
                },
            },
            {
                level: 'param',
                name: 'scope',
                schema: scopeStructureSchema,
            },
        ],
        returns: [] as Array<any>,
        variables: [] as Array<any>,
        body: [
            {
                level: 'logicNode',
                type: 'Start',
                label: '开始',
            },
            {
                level: 'logicNode',
                type: 'AssignmentExpression',
                label: '赋值',
                operator: '=',
                left: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'input',
                },
                right: {
                    parentAttr: 'right',
                    level: 'expressionNode',
                    type: 'BuiltInFunction',
                    label: '内置函数',
                    calleeCode: 'Clone',
                    builtInFuncParams: [
                        {
                            parentAttr: 'builtInFuncParams',
                            level: 'expressionNode',
                            type: 'BuiltInFuncParam',
                            name: 'obj',
                            builtInFuncParamValue: {
                                parentAttr: 'builtInFuncParamValue',
                                level: 'expressionNode',
                                type: 'MemberExpression',
                                object: {
                                    parentAttr: 'builtInFuncParamValue',
                                    level: 'expressionNode',
                                    type: 'MemberExpression',
                                    object: {
                                        parentAttr: 'object',
                                        level: 'expressionNode',
                                        type: 'Identifier',
                                        name: 'scope',
                                    },
                                    property: {
                                        parentAttr: 'property',
                                        level: 'expressionNode',
                                        type: 'Identifier',
                                        name: 'item',
                                        schemaRef: '',
                                        code: 'item',
                                    },
                                    code: '',
                                },
                                property: {
                                    parentAttr: 'property',
                                    level: 'expressionNode',
                                    type: 'Identifier',
                                    name: nameGroup.lowerEntity,
                                    schemaRef: `${nameGroup.structure}.${nameGroup.lowerEntity}`,
                                    code: '',
                                },
                                code: '',
                            },
                            schema: builtInFunctions.Clone.params[0].schema,
                        },
                    ],
                },
            },
            genCallComponentLogic('saveModal', 'open'),
            {
                level: 'logicNode',
                type: 'End',
                label: '结束',
            },
        ],
        playground: [] as Array<any>,
    };
}

function genSubmitLogic(entity: Entity, nameGroup: NameGroup, tableRefName = 'tableView') {
    const updateResolver = entity.resolvers.find((item) => item.name === 'update');
    const createResolver = entity.resolvers.find((item) => item.name === 'create');
    const updateBodyParam = getParamFromResolver(updateResolver, 'body');
    const createBodyparam = getParamFromResolver(createResolver, 'body');

    const idProperty = entity.propertyList.find((item) => item.primaryKey || item.name === 'id');

    return {
        level: 'logic',
        name: nameGroup.submit,
        params: [] as Array<any>,
        returns: [] as Array<any>,
        variables: [
            genVariable('validateResult', '#/systemTypes/ValidateEvent', LEVEL_ENUM.variable),
        ],
        body: [
            {
                level: 'logicNode',
                type: 'Start',
                label: '开始',
            },
            {
                level: 'logicNode',
                type: 'AssignmentExpression',
                label: '赋值',
                operator: '=',
                left: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'validateResult',
                },
                right: genCallComponentLogic('form1', 'validate'),
            },
            {
                level: 'logicNode',
                type: 'IfStatement',
                label: '条件分支',
                test: {
                    level: 'expressionNode',
                    type: 'MemberExpression',
                    name: 'name',
                    hide: false,
                    object: {
                        level: 'expressionNode',
                        type: 'Identifier',
                        name: 'validateResult',
                    },
                    property: {
                        level: 'expressionNode',
                        type: 'Identifier',
                        name: 'valid',
                    },
                },
                consequent: [
                    {
                        level: 'logicNode',
                        type: 'IfStatement',
                        label: '条件分支',
                        test: {
                            parentAttr: 'test',
                            level: 'expressionNode',
                            type: 'BuiltInFunction',
                            label: '内置函数',
                            calleeCode: 'Convert',
                            builtInFuncParams: [
                                {
                                    parentAttr: 'builtInFuncParams',
                                    level: 'expressionNode',
                                    type: 'BuiltInFuncParam',
                                    name: 'value',
                                    builtInFuncParamValue: {
                                        level: 'expressionNode',
                                        type: 'MemberExpression',
                                        object: {
                                            level: 'expressionNode',
                                            type: 'Identifier',
                                            name: 'input',
                                        },
                                        property: {
                                            level: 'expressionNode',
                                            type: 'Identifier',
                                            name: 'id',
                                            schemaRef: idProperty.id,
                                            code: `ID_${idProperty.id}`,
                                        },
                                    },
                                    schema: builtInFunctions.Convert.params[0].schema,
                                },
                                {
                                    parentAttr: 'builtInFuncParams',
                                    level: 'expressionNode',
                                    type: 'BuiltInFuncParam',
                                    name: 'convertTo',
                                    builtInFuncParamValue: {
                                        level: 'expressionNode',
                                        type: 'TypeNote',
                                        schema: {
                                            format: '',
                                            type: 'boolean',
                                        },
                                    },
                                    schema: builtInFunctions.Convert.params[1].schema,
                                },
                            ],
                        },
                        consequent: [
                            genCallInterface(updateResolver.interface, [
                                genInterParam(updateBodyParam.id, 'input'),
                            ]),
                        ],
                        alternate: [
                            genCallInterface(createResolver.interface, [
                                genInterParam(createBodyparam.id, 'input'),
                            ]),
                        ],
                    },
                    genCallComponentLogic('saveModal', 'close'),
                    genCallComponentLogic(tableRefName, 'reload'),
                ],
                alternate: [] as Array<any>,
            },
            {
                level: 'logicNode',
                type: 'End',
                label: '结束',
            },
        ],
        playground: [] as Array<any>,
        expanded: false,
    };
}

export function genCurdBlock(entity: Entity, view: View) {
    const existingNameSets = {
        viewLogic: new Set(view.$def.logics.map((logic) => logic.name)),
        interface: new Set(entity.dataNode.service.interfaces.map((itface) => itface.name)),
        structure: new Set(entity.dataNode.structures.map((structure) => structure.name)),
    };

    const nameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'tableView');
    nameGroup.remove = utils.unique('remove', existingNameSets.viewLogic);
    nameGroup.init = utils.unique('init', existingNameSets.viewLogic);
    nameGroup.create = utils.unique('create', existingNameSets.viewLogic);
    nameGroup.modify = utils.unique('modify', existingNameSets.viewLogic);
    nameGroup.submit = utils.unique('submit', existingNameSets.viewLogic);
    nameGroup.lowerEntity = utils.firstLowerCase(entity.name);

    // 收集所有和本实体关联的实体
    const allEntities = [entity];
    entity.propertyList.forEach((property) => {
        if (property.$relationEntity) { // 有外键关联
            const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
            if (relationEntity) {
                const displayedProperty = getFirstDisplayedProperty(relationEntity);
                if (displayedProperty && !allEntities.includes(relationEntity))
                    allEntities.push(relationEntity);
            }
        }
    });

    const newStructures: Array<any> = [genQueryStructure(allEntities, nameGroup)];
    const newInterfaces: Array<any> = [genQueryInterface(allEntities, nameGroup, true, true)];

    const selectNameGroupMap: Map<string, NameGroup> = new Map();
    const loadSelectLogics = allEntities.slice(1).map((entity) => {
        const selectNameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'select', false, entity.name);
        selectNameGroupMap.set(entity.id, selectNameGroup);
        return genLoadSelectLogic(entity, selectNameGroup, newStructures, newInterfaces);
    });

    return `<template>
    <u-linear-layout direction="vertical">
        ${genFilterTemplate(entity, nameGroup, selectNameGroupMap)}
        ${genTableTemplate(entity, nameGroup)}
        ${genSaveModalTemplate(entity, nameGroup, selectNameGroupMap)}
    </u-linear-layout>
</template>

<definition>
{
    "variables": [
        ${JSON.stringify(genVariable(utils.firstLowerCase(entity.name), entity.schemaRef, LEVEL_ENUM.variable))},
        ${JSON.stringify(genVariable('input', entity.schemaRef, LEVEL_ENUM.variable))},
        ${JSON.stringify(genVariable('filter', entity.schemaRef, LEVEL_ENUM.variable))}
    ],
    "lifecycles": [
        {
            "level": "lifecycle",
            "name": "created",
            "logicId": "${nameGroup.init}"
        }
    ],
    "logics": [
        ${JSON.stringify(genTableLoadLogic(entity, nameGroup, newStructures, newInterfaces, true))},
        ${JSON.stringify(genTableRemoveLogic(entity, nameGroup))},
        ${JSON.stringify(genInitLogic(entity, nameGroup))},
        ${JSON.stringify(genCreateLogic(entity, nameGroup))},
        ${JSON.stringify(genModifyLogic(entity, nameGroup))},
        ${JSON.stringify(genSubmitLogic(entity, nameGroup))}
        ${loadSelectLogics.map((logic) => ',' + JSON.stringify(logic)).join('\n')}
    ],
    "interfaces": ${JSON.stringify(newInterfaces)},
    "structures": ${JSON.stringify(newStructures)}
}
</definition>
`;
}

export function genCurdGridViewBlock(entity: Entity, view: View) {
    const existingNameSets = {
        viewLogic: new Set(view.$def.logics.map((logic) => logic.name)),
        interface: new Set(entity.dataNode.service.interfaces.map((itface) => itface.name)),
        structure: new Set(entity.dataNode.structures.map((structure) => structure.name)),
    };

    const nameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'gridView');
    nameGroup.remove = utils.unique('remove', existingNameSets.viewLogic);
    nameGroup.init = utils.unique('init', existingNameSets.viewLogic);
    nameGroup.create = utils.unique('create', existingNameSets.viewLogic);
    nameGroup.modify = utils.unique('modify', existingNameSets.viewLogic);
    nameGroup.submit = utils.unique('submit', existingNameSets.viewLogic);
    nameGroup.lowerEntity = utils.firstLowerCase(entity.name);

    // 收集所有和本实体关联的实体
    const allEntities = [entity];
    entity.propertyList.forEach((property) => {
        if (property.$relationEntity) { // 有外键关联
            const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
            if (relationEntity) {
                const displayedProperty = getFirstDisplayedProperty(relationEntity);
                if (displayedProperty && !allEntities.includes(relationEntity))
                    allEntities.push(relationEntity);
            }
        }
    });

    const newStructures: Array<any> = [genQueryStructure(allEntities, nameGroup)];
    const newInterfaces: Array<any> = [genQueryInterface(allEntities, nameGroup, true, true)];

    const selectNameGroupMap: Map<string, NameGroup> = new Map();
    const loadSelectLogics = allEntities.slice(1).map((entity) => {
        const selectNameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'select', false, entity.name);
        selectNameGroupMap.set(entity.id, selectNameGroup);
        return genLoadSelectLogic(entity, selectNameGroup, newStructures, newInterfaces);
    });

    return `<template>
    <u-linear-layout direction="vertical">
        ${genFilterTemplate(entity, nameGroup, selectNameGroupMap, 'gridView')}
        ${genGridViewTemplate(entity, nameGroup)}
        ${genSaveModalTemplate(entity, nameGroup, selectNameGroupMap)}
    </u-linear-layout>
</template>

<definition>
{
    "variables": [
        ${JSON.stringify(genVariable(utils.firstLowerCase(entity.name), entity.schemaRef, LEVEL_ENUM.variable))},
        ${JSON.stringify(genVariable('input', entity.schemaRef, LEVEL_ENUM.variable))},
        ${JSON.stringify(genVariable('filter', entity.schemaRef, LEVEL_ENUM.variable))}
    ],
    "lifecycles": [
        {
            "level": "lifecycle",
            "name": "created",
            "logicId": "${nameGroup.init}"
        }
    ],
    "logics": [
        ${JSON.stringify(genGridViewLoadLogic(entity, nameGroup, newStructures, newInterfaces, true))},
        ${JSON.stringify(genGridViewRemoveLogic(entity, nameGroup))},
        ${JSON.stringify(genInitLogic(entity, nameGroup))},
        ${JSON.stringify(genCreateLogic(entity, nameGroup))},
        ${JSON.stringify(genModifyLogic(entity, nameGroup))},
        ${JSON.stringify(genSubmitLogic(entity, nameGroup, 'gridView'))}
        ${loadSelectLogics.map((logic) => ',' + JSON.stringify(logic)).join('\n')}
    ],
    "interfaces": ${JSON.stringify(newInterfaces)},
    "structures": ${JSON.stringify(newStructures)}
}
</definition>
`;
}

export default genCurdBlock;
