import { firstUpperCase } from '../utils';
import pluralize = require('pluralize');
import { kebabCase } from 'lodash';

import { Entity, EntityProperty } from '..';
import { dataTypesMap } from './dataTypes';
import Schema from './Schema';

export const ENTITY_STRUCTURE_PREFIX = [
    'Sort', 'PageOf', 'ApiReturnOfPageOf',
    'ApiReturnOf', 'ApiReturnOflong', 'ScopeOf',
];
export const ENTITY_INTERFACE_PREFIX = [
    'get', 'delete', 'count', 'update', 'create',
    'getAll', 'import', 'export',
];

interface Parameter {
    name: string,
    in: string,
    required: boolean,
    schema: Schema,
    entityFieldId?: string,
    [name: string]: any,
}

type Parameters = { [name: string]: Parameter };

const resolverNormal = {
    ast: {
        type: 'MemberExpression',
        object: {
            type: 'Identifier',
            name: 'data',
        },
        property: {
            type: 'Identifier',
            name: 'Data',
        },
    },
    code: 'data.Data',
};
const resolverPageOf = {
    ast: {
        type: 'MemberExpression',
        object: {
            type: 'MemberExpression',
            object: {
                type: 'Identifier',
                name: 'data',
            },
            property: {
                type: 'Identifier',
                name: 'Data',
            },
        },
        property: {
            type: 'Identifier',
            name: 'content',
        },
    },
    code: 'data.Data.content',
};

/**
 * 获取实体相关的所有数据结构的名称
 * @param entityName 实体名称
 */
export const getEntityStructureNames = (entityName: string) =>
    ENTITY_STRUCTURE_PREFIX.map((key) => `${key}${firstUpperCase(entityName)}`);

/**
 * 获取实体相关的所有数据结构
 * @param entity 实体
 */
export function getEntityStructures(entity: Entity) {
    const service = entity.dataNode.service;

    return [
        {
            name: `Sort${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'empty',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
                {
                    level: 'property',
                    name: 'sorted',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
                {
                    level: 'property',
                    name: 'unsorted',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
            ],
        },
        {
            name: `PageOf${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'content',
                    description: '',
                    $ref: `#/${service.id}/${entity.id}`,
                    isArray: true,
                },
                {
                    level: 'property',
                    name: 'empty',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
                {
                    level: 'property',
                    name: 'first',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
                {
                    level: 'property',
                    name: 'last',
                    description: '',
                    $ref: '#/basicTypes/Boolean',
                },
                {
                    level: 'property',
                    name: 'number',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'numberOfElements',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'size',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'totalElements',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'totalPages',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
            ],
        },
        {
            name: `ApiReturnOfPageOf${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'Code',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'Data',
                    description: '',
                    $ref: `PageOf${firstUpperCase(entity.name)}`, //
                },
                {
                    level: 'property',
                    name: 'Message',
                    description: '',
                    type: 'string',
                    format: '',
                    $ref: '#/basicTypes/String',
                },
            ],
            structureRef: `PageOf${firstUpperCase(entity.name)}`,
        },
        {
            name: `ApiReturnOf${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'Code',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'Data',
                    description: '',
                    $ref: `#/${service.id}/${entity.id}`,
                },
                {
                    level: 'property',
                    name: 'Message',
                    description: '',
                    $ref: '#/basicTypes/String',
                },
            ],
        },
        {
            name: `ApiReturnOflong${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'Code',
                    description: '',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'Data',
                    description: '',
                    $ref: '#/basicTypes/Decimal',
                },
                {
                    level: 'property',
                    name: 'Message',
                    description: '',
                    $ref: '#/basicTypes/String',
                },
            ],
        },
        {
            name: `ScopeOf${firstUpperCase(entity.name)}`,
            type: 'object',
            propertyList: [
                {
                    level: 'property',
                    name: 'item',
                    $ref: `#/${service.id}/${entity.id}`,
                },
                {
                    level: 'property',
                    name: 'value',
                    $ref: '#/basicTypes/String',
                },
                {
                    level: 'property',
                    name: 'rowIndex',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'columnIndex',
                    $ref: '#/basicTypes/Integer',
                },
                {
                    level: 'property',
                    name: 'index',
                    $ref: '#/basicTypes/Integer',
                },
            ],
        },
    ];
}

/**
 * 获取实体相关的所有 GET 接口请求参数
 * @param entity 实体
 */
export function getEntityGetAllQueryParams(entity: Entity) {
    const parameters: Parameters = {};
    if (!entity)
        return parameters;

    function addJDLQueryParam(property: EntityProperty, operator: string, type?: string) {
        const name = `${property.name}.${operator}`;
        const schema = dataTypesMap[property.$ref];

        const parameter = {
            name,
            in: 'query',
            required: false,
            schema: Object.assign({}, schema),
            entityFieldId: property.id,
        };

        if (type === 'array')
            parameter.schema = { type: 'array', items: Object.assign({}, schema) };
        else if (type === 'boolean')
            parameter.schema = { type: 'boolean', format: '' };

        parameters[name] = parameter;
    }

    parameters.page = {
        name: 'page',
        in: 'query',
        required: false,
        schema: { type: 'integer', format: 'int' },
    };

    parameters.size = {
        name: 'size',
        in: 'query',
        required: false,
        schema: { type: 'integer', format: 'int' },
    };

    parameters.sort = {
        name: 'sort',
        in: 'query',
        required: false,
        schema: {
            type: 'array',
            items: { type: 'string', format: '' },
        },
    };

    entity.propertyList.forEach((property) => {
        if (!property.$ref) {
            console.error('property.$ref undefined', entity.name, property.name);
            return;
        }

        const schema = dataTypesMap[property.$ref];
        if (!property.$ref.startsWith('#/basicTypes/') && schema.type !== 'enum') {
            // 这里有枚举和非枚举的问题
            property = new EntityProperty({
                name: property.name + 'Id',
                $ref: '#/basicTypes/Long',
            });
        }

        addJDLQueryParam(property, 'equals');
        addJDLQueryParam(property, 'notEquals');
        addJDLQueryParam(property, 'in', 'array');
        addJDLQueryParam(property, 'notIn', 'array');
        addJDLQueryParam(property, 'specified', 'boolean');

        if (
            property.$ref === '#/basicTypes/Integer'
            || property.$ref === '#/basicTypes/Long'
            || property.$ref === '#/basicTypes/Decimal'
        ) {
            addJDLQueryParam(property, 'greaterThan');
            addJDLQueryParam(property, 'greaterThanOrEqual');
            addJDLQueryParam(property, 'lessThan');
            addJDLQueryParam(property, 'lessThanOrEqual');
        }

        if (
            property.$ref === '#/basicTypes/Date'
            || property.$ref === '#/basicTypes/Time'
            || property.$ref === '#/basicTypes/DateTime'
        ) {
            addJDLQueryParam(property, 'greaterThan');
            addJDLQueryParam(property, 'greaterThanOrEqual');
            addJDLQueryParam(property, 'lessThan');
            addJDLQueryParam(property, 'lessThanOrEqual');
        }

        if (
            property.$ref === '#/basicTypes/String'
            || property.$ref === '#/basicTypes/Text'
        ) {
            addJDLQueryParam(property, 'contains');
            addJDLQueryParam(property, 'doesNotContain');
        }
    });

    return parameters;
}

/**
 * 获取实体相关的所有导出请求参数
 * @param entity 实体
 */
function getEntityExportQueryParams(entity: Entity) {
    const parameters = getEntityGetAllQueryParams(entity);
    parameters.fields = {
        name: 'fields',
        in: 'query',
        required: false,
        schema: dataTypesMap['#/basicTypes/String'],
    };
    return parameters;
}

/**
 * 获取实体某个属性相关的所有接口参数
 * @param property 实体属性
 * @param dataTypesMap
 */
export function getEntityPropertyParams(property: EntityProperty) {
    const parameters: Parameters = {};
    if (!property)
        return parameters;

    function addJDLQueryParam(
        property: EntityProperty,
        operator: string, type?: string,
    ) {
        const name = `${property.name}.${operator}`;
        const schema = dataTypesMap[property.$ref];

        const parameter: Parameter = {
            name,
            in: 'query',
            required: false,
            schema: Object.assign({}, schema),
            entityFieldId: property.id,
        };

        if (type === 'array')
            parameter.schema = { type: 'array', items: Object.assign({}, schema) };
        else if (type === 'boolean')
            parameter.schema = { type: 'boolean', format: '' };

        parameters[name] = parameter;
    }

    if (!property.$ref) {
        console.error('property.$ref undefined', property.name);
        return;
    }

    const schema = dataTypesMap[property.$ref];
    if (!property.$ref.startsWith('#/basicTypes/') && schema.type !== 'enum') {
        // 这里有枚举和非枚举的问题
        property = new EntityProperty({
            name: property.name + 'Id',
            $ref: '#/basicTypes/Long',
        });
    }

    addJDLQueryParam(property, 'equals');
    addJDLQueryParam(property, 'notEquals');
    addJDLQueryParam(property, 'in', 'array');
    addJDLQueryParam(property, 'notIn', 'array');
    addJDLQueryParam(property, 'specified', 'boolean');

    if (property.$ref === '#/basicTypes/Integer' || property.$ref === '#/basicTypes/Long' || property.$ref === '#/basicTypes/Decimal') {
        addJDLQueryParam(property, 'greaterThan');
        addJDLQueryParam(property, 'greaterThanOrEqual');
        addJDLQueryParam(property, 'lessThan');
        addJDLQueryParam(property, 'lessThanOrEqual');
    }

    if (property.$ref === '#/basicTypes/Date' || property.$ref === '#/basicTypes/Time' || property.$ref === '#/basicTypes/DateTime') {
        addJDLQueryParam(property, 'greaterThan');
        addJDLQueryParam(property, 'greaterThanOrEqual');
        addJDLQueryParam(property, 'lessThan');
        addJDLQueryParam(property, 'lessThanOrEqual');
    }

    if (property.$ref === '#/basicTypes/String' || property.$ref === '#/basicTypes/Text') {
        addJDLQueryParam(property, 'contains');
        addJDLQueryParam(property, 'doesNotContain');
    }

    return parameters;
}

/**
 * 获取实体相关的所有接口
 * @param entity 实体
 */
export function getEntityInterfaces(entity: Entity) {
    const pluralized = pluralize(entity.name);
    const service = entity.dataNode.service;

    return [
        {
            data: {
                name: `getAll${firstUpperCase(pluralized)}`,
                method: 'GET',
                path: `/api/${kebabCase(pluralized)}`,
                description: `getAll${firstUpperCase(pluralized)}`,
                parameters: JSON.stringify(getEntityGetAllQueryParams(entity)),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOfPageOf${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: resolverPageOf,
        },
        {
            data: {
                name: `get${firstUpperCase(entity.name)}`,
                method: 'GET',
                path: `/api/${kebabCase(pluralized)}/{id}`,
                description: `get${firstUpperCase(entity.name)}`,
                parameters: JSON.stringify({
                    id: {
                        name: 'id',
                        in: 'path',
                        required: true,
                        schema: { type: 'integer', format: 'long' },
                    },
                }),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOf${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: resolverNormal,
        },
        {
            data: {
                name: `create${firstUpperCase(entity.name)}`,
                method: 'POST',
                path: `/api/${kebabCase(pluralized)}`,
                description: `create${firstUpperCase(entity.name)}`,
                parameters: '{}',
                requestBody: JSON.stringify({
                    description: '',
                    content: {
                        'application/json': {
                            schema: {
                                $ref: `#/${service.id}/${entity.id}`,
                            },
                        },
                    },
                }),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOf${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: {},
        },
        {
            data: {
                name: `delete${firstUpperCase(entity.name)}`,
                method: 'DELETE',
                path: `/api/${kebabCase(pluralized)}/{id}`,
                description: `delete${firstUpperCase(entity.name)}`,
                parameters: JSON.stringify({
                    id: {
                        name: 'id',
                        in: 'path',
                        required: true,
                        schema: { type: 'integer', format: 'long' },
                    },
                }),
                responses: '{}',
            },
            resolver: {},
        },
        {
            data: {
                name: `update${firstUpperCase(entity.name)}`,
                method: 'PUT',
                path: `/api/${kebabCase(pluralized)}`,
                description: `update${firstUpperCase(entity.name)}`,
                parameters: '{}',
                requestBody: JSON.stringify({
                    description: '',
                    content: {
                        'application/json': {
                            schema: {
                                $ref: `#/${service.id}/${entity.id}`,
                            },
                        },
                    },
                }),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOf${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: {},
        },
        {
            data: {
                name: `count${firstUpperCase(entity.name)}`,
                method: 'GET',
                path: `/api/${kebabCase(pluralized)}/count`,
                description: `count${firstUpperCase(entity.name)}`,
                parameters: JSON.stringify(getEntityGetAllQueryParams(entity)),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOflong${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: {},
        },
        {
            data: {
                name: `import${firstUpperCase(entity.name)}`,
                method: 'POST',
                path: `/api/${kebabCase(pluralized)}/import`,
                description: `import${firstUpperCase(entity.name)}`,
                parameters: '{}',
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOflong${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: {},
        },
        {
            data: {
                name: `export${firstUpperCase(entity.name)}`,
                method: 'GET',
                path: `/api/${kebabCase(pluralized)}/export`,
                description: `export${firstUpperCase(entity.name)}`,
                parameters: JSON.stringify(getEntityExportQueryParams(entity)),
                responses: JSON.stringify({
                    200: {
                        description: '',
                        content: {
                            'application/json': {
                                schema: {
                                    $ref: `<#/ApiReturnOflong${firstUpperCase(entity.name)}>`,
                                },
                            },
                        },
                    },
                }),
            },
            resolver: {},
        },
    ];
}
