import { vertexsMap, Entity, Structure, LogicItem, LEVEL_ENUM, utils } from '../..';
import { NameGroup } from './utils';

export function genQueryStructure(allEntities: Array<Entity>, nameGroup: NameGroup) {
    allEntities = Array.from(new Set(allEntities));
    return {
        level: 'structure',
        ideVersion: '2.0',
        editable: true,
        name: nameGroup.structure,
        type: 'object',
        propertyList: allEntities.map((entity) => ({
            level: 'property',
            ideVersion: '2.0',
            editable: true,
            name: utils.firstLowerCase(entity.name),
            $ref: entity.schemaRef,
            _posIndex: 0,
            typeKey: entity.schemaRef,
        })),
        serviceId: allEntities[0].serviceId,
        serviceType: 'entity',
    };
}

function genWhereExpression(entity: Entity): object {
    const propertyList = entity.propertyList.filter((property) => property?.display.inFilter);
    const expressions = propertyList.map((property) => {
        const res = {
            left: {
                level: 'expressionNode',
                type: 'QueryFieldExpression',
                label: '查询组件实体属性',
                joinPartRef: entity.name,
                propertyRef: property.id,
                code: `ID_${entity.id}.ID_${property.id}`,
            },
            right: {
                level: 'expressionNode',
                type: 'MemberExpression',
                object: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'filter',
                },
                property: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    code: `ID_${property.id}`,
                    schemaRef: property.id,
                },
            },
        };
        if (!property.$relationEntity && ['#/basicTypes/String', '#/basicTypes/Text'].includes(property.typeKey)) {
            Object.assign(res, {
                level: 'expressionNode',
                type: 'QueryStringFilterExpression',
                filterName: 'like',
            });
        } else {
            Object.assign(res, {
                level: 'expressionNode',
                type: 'BinaryExpression',
                operator: '==',
            });
        }

        return res;
    });

    function transList2Tree(arr: Array<object>): object {
        if (arr.length <= 1)
            return arr[0];

        const len = arr.length;
        const mid = len >> 1;
        const left = arr.slice(0, mid);
        const right = arr.slice(mid);
        return {
            level: 'expressionNode',
            type: 'LogicalExpression',
            operator: '&&',
            left: transList2Tree(left),
            right: transList2Tree(right),
        };
    }

    return transList2Tree(expressions);
}

export function genQueryAssignment(allEntities: Array<Entity>, nameGroup: NameGroup, supportFilter: boolean, supportSort: boolean) {
    allEntities = Array.from(allEntities);
    const entity = allEntities.shift();
    const from = {
        level: 'expressionNode',
        parentAttr: 'from',
        folded: false,
        type: 'QueryFromExpression',
        entityRef: entity.id,
        joinPartList: allEntities.map((relationEntity) => {
            const leftProperty = entity.propertyList.find((property) => property.$relationEntity === relationEntity.id);
            return {
                level: 'expressionNode',
                parentAttr: 'joinPartList',
                folded: false,
                type: 'QueryJoinExpression',
                joinType: 'LEFT',
                entityRef: relationEntity.id,
                joinPartList: [] as Array<any>,
                popupOpened: false,
                onExpressionList: [{
                    level: 'expressionNode',
                    parentAttr: 'onExpressionList',
                    label: '算术运算',
                    folded: false,
                    type: 'BinaryExpression',
                    left: {
                        level: 'expressionNode',
                        parentAttr: 'left',
                        label: '查询组件实体属性',
                        folded: false,
                        type: 'QueryFieldExpression',
                        propertyRef: leftProperty.id,
                        joinPartRef: entity.name,
                    },
                    right: {
                        level: 'expressionNode',
                        parentAttr: 'right',
                        label: '查询组件实体属性',
                        folded: false,
                        type: 'QueryFieldExpression',
                        propertyRef: leftProperty.$relationProperty,
                        joinPartRef: relationEntity.name,
                    },
                    operator: '==',
                }],
                serviceId: entity.serviceId,
            };
        }),
    };

    const whereExpression = supportFilter && genWhereExpression(entity);
    const where = whereExpression ? [whereExpression] : [];

    return {
        level: 'logicNode',
        label: '赋值',
        folded: false,
        type: 'AssignmentExpression',
        operator: '=',
        left: {
            level: 'expressionNode',
            parentAttr: 'left',
            folded: false,
            type: 'Identifier',
            name: 'result',
        },
        right: {
            level: 'logicNode',
            parentAttr: 'right',
            label: '数据查询',
            folded: false,
            type: 'CallQueryComponent',
            structureRef: nameGroup.structure,
            select: {
                level: 'expressionNode',
                parentAttr: 'select',
                label: '查询组件Select子句',
                folded: false,
                type: 'QuerySelectExpression',
                distinct: false,
                star: true,
                selectElementList: [] as Array<any>,
            },
            from,
            where,
            groupBy: [] as Array<any>,
            having: [] as Array<any>,
            orderBy: supportSort ? [{
                level: 'expressionNode',
                type: 'QueryOrderByExpression',
                label: '查询组件OrderBy元素',
                order: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'order',
                },
                orderElement: {
                    level: 'expressionNode',
                    type: 'Identifier',
                    name: 'sort',
                },
            }] : [],
            logicName: nameGroup.interface,
            serviceId: entity.serviceId,
            structureName: nameGroup.structure,
            limit: {
                level: 'expressionNode',
                parentAttr: 'limit',
                label: '查询组件 Limit 子句',
                folded: false,
                type: 'QueryLimitExpression',
                pageElement: {
                    level: 'expressionNode',
                    parentAttr: 'pageElement',
                    folded: false,
                    type: 'Identifier',
                    name: 'page',
                },
                pageSizeElement: {
                    level: 'expressionNode',
                    parentAttr: 'pageElement',
                    folded: false,
                    type: 'Identifier',
                    name: 'size',
                },
            },
        },
    };
}

export function genQueryInterface(
    allEntities: Array<Entity>, nameGroup: NameGroup,
    supportFilter: boolean, supportSort: boolean,
) {
    const itface = {
        level: LEVEL_ENUM.interface,
        name: nameGroup.interface,
        path: '/api/' + nameGroup.interface,
        method: 'POST',
        description: '',
        entityId: allEntities[0].id,
        serviceType: 'MICROSERVICE',
        serviceId: allEntities[0].serviceId,
        parameters: '',
        requestBody: null as string,
        responses: '{"200":{"description":"","content":{"application/json":{"schema":{"type":"genericType","typeInstantiation":{"typeName":"PageOf","typeParams":[{"typeParamValue":{"$ref":"<structureRef>"},"type":"typeParam","typeParamName":"T"}]}}}}}}',
        logic: {
            level: 'logic',
            name: nameGroup.interface,
            description: '',
            moduleId: allEntities[0].serviceId,
            moduleType: 'microService',
            params: [
                {
                    level: LEVEL_ENUM.param,
                    type: 'Identifier',
                    name: 'page',
                    description: '',
                    schema: { type: 'integer', format: 'int' },
                    required: false,
                    in: '',
                },
                {
                    level: LEVEL_ENUM.param,
                    type: 'Identifier',
                    name: 'size',
                    description: '',
                    schema: { type: 'integer', format: 'int' },
                    required: false,
                    in: '',
                },
            ],
            returns: [
                {
                    level: LEVEL_ENUM.return,
                    type: 'Identifier',
                    name: 'result',
                    description: '',
                    schema: {
                        type: 'genericType',
                        typeInstantiation: {
                            typeName: 'PageOf',
                            typeParams: [
                                {
                                    typeParamValue: {
                                        $ref: nameGroup.structure,
                                    },
                                    type: 'typeParam',
                                    typeParamName: 'T',
                                },
                            ],
                        },
                    },
                },
            ],
            variables: [] as Array<any>,
            body: [
                {
                    level: 'logicNode',
                    label: '开始',
                    folded: false,
                    type: 'Start',
                },
                genQueryAssignment(allEntities, nameGroup, supportFilter, supportSort),
                {
                    level: 'logicNode',
                    label: '结束',
                    folded: false,
                    type: 'End',
                },
            ],
            playground: [] as Array<any>,
        },
    };

    if (supportSort)
        itface.logic.params.push(
            {
                level: LEVEL_ENUM.param,
                type: 'Identifier',
                name: 'sort',
                description: '',
                schema: { type: 'string', format: '' },
                required: false,
                in: '',
            },
            {
                level: LEVEL_ENUM.param,
                type: 'Identifier',
                name: 'order',
                description: '',
                schema: { type: 'string', format: '' },
                required: false,
                in: '',
            },
        );

    if (supportFilter)
        itface.logic.params.push({
            level: LEVEL_ENUM.param,
            type: 'Identifier',
            name: 'filter',
            schema: {
                $ref: allEntities[0].schemaRef,
            },
            required: false,
            in: '',
        } as any);

    const parameters: any = {};
    let requestBody: any;
    let responses: any;

    itface.logic.params.forEach((param: any) => {
        if (param.in === 'body') {
            requestBody = {
                description: param.description,
                content: {
                    'application/json': {
                        schema: param.schema,
                    },
                },
            };
        } else {
            parameters[param.name] = {
                name: param.name,
                description: param.description,
                in: param.in,
                required: param.required,
                schema: param.schema,
            };
        }
    });
    const ret = itface.logic.returns[0];
    if (ret) {
        responses = {
            200: {
                description: ret.description,
                content: {
                    'application/json': {
                        schema: ret.schema,
                    },
                },
            },
        };
    }

    itface.parameters = JSON.stringify(parameters);
    itface.requestBody = JSON.stringify(requestBody);
    itface.responses = JSON.stringify(responses);

    return itface;
}

export async function joinEntity(callQueryComponent: LogicItem, entity: Entity, recordStructure: Structure) {
    let lastJoinPart = (callQueryComponent as any).from;
    const queue = [lastJoinPart];

    let joinInfo: { leftPropertyRef: string, rightPropertyRef: string };
    // 找出应该 join 的 Part
    while (queue.length) {
        lastJoinPart = queue.shift();
        queue.push(...lastJoinPart.joinPartList);

        // 无法判断用户行为，优先加有关系的
        const lastEntity = vertexsMap.get(lastJoinPart.entityRef) as Entity;
        // lastEntity 和 entity Join
        // 判断要不要自动关联 onExpression
        // 一般左联是自动有的了
        const rightProperty = entity.propertyList.find((property) => lastEntity.id === property.$relationEntity);
        if (rightProperty) {
            const leftProperty = vertexsMap.get(rightProperty.$relationProperty);
            joinInfo = {
                leftPropertyRef: leftProperty.id,
                rightPropertyRef: rightProperty.id,
            };
            break;
        } else {
            const leftProperty = lastEntity.propertyList.find((property) => property.$relationEntity === entity.id);
            if (leftProperty) {
                const rightProperty = vertexsMap.get(leftProperty.$relationProperty);
                joinInfo = {
                    leftPropertyRef: leftProperty.id,
                    rightPropertyRef: rightProperty.id,
                };
                break;
            }
        }
    }

    if (!joinInfo) {
        lastJoinPart = (callQueryComponent as any).from;
    }

    const newJoinPartLogicItem = LogicItem.from({
        level: 'expressionNode',
        folded: false,
        type: 'QueryJoinExpression',
        joinType: 'LEFT',
        entityRef: entity.id,
        joinPartList: [],
        popupOpened: false,
        onExpressionList: [],
        _posIndex: lastJoinPart.length,
        parentId: lastJoinPart.id,
        parentAttr: 'joinPartList',
        logicId: callQueryComponent.logicId,
    }, callQueryComponent.logic, null);

    await newJoinPartLogicItem.createOrMove({
        parent: lastJoinPart,
        parentId: lastJoinPart.id,
        parentAttr: 'joinPartList',
    });

    if (joinInfo) {
        const onExpression = {
            level: 'expressionNode',
            label: '算术运算',
            folded: false,
            type: 'BinaryExpression',
            left: {
                level: 'expressionNode',
                ideVersion: '2.0',
                label: '查询组件实体属性',
                folded: false,
                type: 'QueryFieldExpression',
                propertyRef: joinInfo.leftPropertyRef,
                joinPartRef: lastJoinPart.id,
                parentAttr: 'left',
            },
            right: {
                level: 'expressionNode',
                ideVersion: '2.0',
                label: '查询组件实体属性',
                folded: false,
                type: 'QueryFieldExpression',
                propertyRef: joinInfo.rightPropertyRef,
                joinPartRef: newJoinPartLogicItem.id,
                parentAttr: 'right',
            },
            operator: '==',
            parentAttr: 'onExpressionList',
            _posIndex: 0,
            logicId: callQueryComponent.logicId,
        };

        const onExpressionLogicItem = LogicItem.from(onExpression, callQueryComponent.logic, newJoinPartLogicItem);
        await onExpressionLogicItem.createOrMove({
            parent: newJoinPartLogicItem,
            parentId: newJoinPartLogicItem.id,
            parentAttr: 'onExpressionList',
        }, {
            checkTypeIgnore: true,
        });
    }
    await recordStructure.loadPro();
}

export default genQueryAssignment;

