package cn.stylefeng.team.meta.modular.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.roses.kernel.rule.util.SortUtils;
import cn.stylefeng.team.meta.api.CreateTableSqlParseApi;
import cn.stylefeng.team.meta.api.DatabaseMetadataApi;
import cn.stylefeng.team.meta.api.constants.MetaConstants;
import cn.stylefeng.team.meta.api.entity.MetaDefinition;
import cn.stylefeng.team.meta.api.entity.MetaField;
import cn.stylefeng.team.meta.api.entity.MetaModel;
import cn.stylefeng.team.meta.api.enums.FieldQueryTypeEnum;
import cn.stylefeng.team.meta.api.enums.MetaFieldTypeEnum;
import cn.stylefeng.team.meta.api.exception.MetaException;
import cn.stylefeng.team.meta.api.pojo.MetaFieldDTO;
import cn.stylefeng.team.meta.api.pojo.MetaFieldShowDTO;
import cn.stylefeng.team.meta.api.pojo.SubTableMetaFieldDTO;
import cn.stylefeng.team.meta.api.pojo.TableParseInfo;
import cn.stylefeng.team.meta.api.pojo.db.DatabaseMetadataRequest;
import cn.stylefeng.team.meta.api.pojo.db.DatabaseTableDTO;
import cn.stylefeng.team.meta.api.pojo.table.GenerationTableAlter;
import cn.stylefeng.team.meta.api.pojo.table.TableAlterLog;
import cn.stylefeng.team.meta.modular.context.DatabaseConnectionContext;
import cn.stylefeng.team.meta.modular.enums.MetaFieldExceptionEnum;
import cn.stylefeng.team.meta.modular.factory.AlterFieldFactory;
import cn.stylefeng.team.meta.modular.factory.CreateTableMetaFactory;
import cn.stylefeng.team.meta.modular.factory.FieldFactory;
import cn.stylefeng.team.meta.modular.factory.ValidateFieldFactory;
import cn.stylefeng.team.meta.modular.mapper.MetaFieldMapper;
import cn.stylefeng.team.meta.modular.pojo.GenerationFieldSql;
import cn.stylefeng.team.meta.modular.pojo.request.MetaFieldRequest;
import cn.stylefeng.team.meta.modular.pojo.request.SqlImportRequest;
import cn.stylefeng.team.meta.modular.service.MetaDefinitionService;
import cn.stylefeng.team.meta.modular.service.MetaFieldService;
import cn.stylefeng.team.meta.modular.service.MetaModelService;
import cn.stylefeng.team.meta.sdk.CreateTableSqlParseFactory;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class MetaFieldServiceImpl extends ServiceImpl<MetaFieldMapper, MetaField> implements MetaFieldService {

    @Resource
    private MetaDefinitionService metaDefinitionService;

    @Resource
    private MetaModelService metaModelService;

    @Resource
    private MetaFieldService metaFieldService;

    @Resource
    private DatabaseMetadataApi databaseMetadataApi;

    @Override
    public PageResult<MetaField> findPage(MetaFieldRequest metaFieldRequest) {
        LambdaQueryWrapper<MetaField> wrapper = createWrapper(metaFieldRequest);
        Page<MetaField> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    @Override
    public void removeDefinitionFieldList(List<Long> definitionIdList) {
        if (ObjectUtil.isEmpty(definitionIdList)) {
            return;
        }

        LambdaUpdateWrapper<MetaField> classDefinitionFieldLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        classDefinitionFieldLambdaUpdateWrapper.in(MetaField::getMetaDefinitionId, definitionIdList);
        this.remove(classDefinitionFieldLambdaUpdateWrapper);
    }

    @Override
    public List<MetaField> getFieldList(MetaFieldRequest metaFieldRequest) {

        Long metaDefinitionId = metaFieldRequest.getMetaDefinitionId();
        MetaDefinition newestDefinition = metaDefinitionService.getById(metaDefinitionId);
        if (newestDefinition == null) {
            return new ArrayList<>();
        }

        Integer fieldQueryType = metaFieldRequest.getFieldQueryType();
        if (ObjectUtil.isEmpty(fieldQueryType)) {
            return this.quickGetFieldList(null, ListUtil.of(newestDefinition.getMetaDefinitionId()));
        } else {
            String realFlag;
            if (FieldQueryTypeEnum.REAL.getCode().equals(fieldQueryType)) {
                realFlag = YesOrNotEnum.Y.getCode();
            } else {
                realFlag = YesOrNotEnum.N.getCode();
            }

            return this.quickGetFieldList(realFlag, ListUtil.of(newestDefinition.getMetaDefinitionId()));
        }
    }

    @Override
    public List<MetaField> simpleGetFieldList(MetaFieldRequest metaFieldRequest) {

        Long metaDefinitionId = metaFieldRequest.getMetaDefinitionId();
        MetaDefinition newestDefinition = metaDefinitionService.getById(metaDefinitionId);
        if (newestDefinition == null) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<MetaField> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MetaField::getMetaDefinitionId, metaDefinitionId);

        wrapper.orderByAsc(MetaField::getFldSort);

        wrapper.select(MetaField::getFieldId, MetaField::getFieldCode, MetaField::getDescription, MetaField::getRealFlag);

        return this.list(wrapper);
    }

    @Override
    public List<MetaField> quickGetFieldList(Long definitionId) {
        return this.quickGetFieldList(ListUtil.list(false, definitionId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMetaField(Long metaModelId, List<TableAlterLog> tableAlterLogList) {

        MetaDefinition modelNewestDefinition = metaDefinitionService.findModelNewestDefinition(metaModelId);
        if (modelNewestDefinition == null) {
            return;
        }

        LambdaQueryWrapper<MetaField> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MetaField::getMetaDefinitionId, modelNewestDefinition.getMetaDefinitionId());
        queryWrapper.eq(MetaField::getRealFlag, YesOrNotEnum.Y.getCode());
        List<MetaField> metaFieldList = this.list(queryWrapper);

        List<MetaField> calcAlterResult = AlterFieldFactory.calcAlterResult(modelNewestDefinition, metaFieldList, tableAlterLogList);

        LambdaUpdateWrapper<MetaField> metaFieldLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        metaFieldLambdaUpdateWrapper.eq(MetaField::getMetaDefinitionId, modelNewestDefinition.getMetaDefinitionId());
        metaFieldLambdaUpdateWrapper.eq(MetaField::getRealFlag, YesOrNotEnum.Y.getCode());
        this.remove(metaFieldLambdaUpdateWrapper);

        this.getBaseMapper().insertBatchSomeColumn(calcAlterResult);
    }

    @Override
    public List<GenerationTableAlter> getGenerationTableAlterList(List<TableAlterLog> tableAlterLogList) {

        List<GenerationTableAlter> generationTableAlterList = new ArrayList<>();
        for (TableAlterLog tableAlterLog : tableAlterLogList) {

            GenerationTableAlter generationTableAlter = new GenerationTableAlter();
            BeanUtil.copyProperties(tableAlterLog, generationTableAlter, CopyOptions.create().ignoreError());

            GenerationFieldSql temp = CreateTableMetaFactory.commonGetMysqlConfig(tableAlterLog.getFieldType(),
                    tableAlterLog.getFieldLength(),
                    tableAlterLog.getDecimalLength(),
                    tableAlterLog.getNotEmptyFlag(),
                    tableAlterLog.getDefaultValue());
            generationTableAlter.setMysqlType(temp.getMysqlType());
            generationTableAlter.setMysqlLength(temp.getMysqlLength());
            generationTableAlter.setMysqlNull(temp.getMysqlNull());
            generationTableAlter.setMysqlCharacterSet(temp.getMysqlCharacterSet());
            generationTableAlter.setDefaultValue(temp.getDefaultValue());

            if (StrUtil.isNotBlank(tableAlterLog.getAfterFieldCode())) {
                if (tableAlterLog.getAfterFieldCode().equalsIgnoreCase("-1")) {
                    generationTableAlter.setAfterSnippet("FIRST");
                } else {
                    generationTableAlter.setAfterSnippet("AFTER `" + generationTableAlter.getAfterFieldCode() + "`");
                }
            }

            generationTableAlterList.add(generationTableAlter);
        }

        return generationTableAlterList;
    }

    @Override
    public List<MetaField> quickGetFieldList(List<Long> definitionIdList) {
        return this.quickGetFieldList(null, definitionIdList);
    }

    @Override
    public List<MetaField> quickGetFieldList(String realFlag, List<Long> definitionIdList) {

        LambdaQueryWrapper<MetaField> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MetaField::getMetaDefinitionId, definitionIdList);

        if (ObjectUtil.isNotEmpty(realFlag)) {
            wrapper.eq(MetaField::getRealFlag, realFlag);
        }

        wrapper.orderByAsc(MetaField::getFldSort);

        wrapper.select(MetaField::getFieldId, MetaField::getBusinessCategoryId,
                MetaField::getBusinessId, MetaField::getMetaModelId, MetaField::getMetaDefinitionId, MetaField::getFieldCode,
                MetaField::getFieldType, MetaField::getFieldLength, MetaField::getDecimalLength, MetaField::getNotEmptyFlag,
                MetaField::getKeyFlag, MetaField::getDefaultValue, MetaField::getRealFlag,
                MetaField::getDescription, MetaField::getDefaultFormComponent, MetaField::getFldSort);

        return this.list(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMainModelLinkSubTableVirtualField(Long modelId, List<SubTableMetaFieldDTO> subTableMetaFieldDTOList) {

        MetaModel metaModel = metaModelService.getById(modelId);
        if (metaModel == null) {
            return;
        }

        List<MetaField> alreadyHaveFieldList = metaFieldService.quickGetFieldList(metaModel.getCurrentDefinitionId());

        BigDecimal maxSort = alreadyHaveFieldList.stream().map(MetaField::getFldSort).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);

        List<MetaField> newFieldList = new ArrayList<>();
        for (SubTableMetaFieldDTO tableMetaFieldDTO : subTableMetaFieldDTOList) {

            String fieldCode = tableMetaFieldDTO.getTableName() + MetaConstants.DEFAULT_MAIN_MODEL_LINK_TABLE_FIELD_SUFFIX;

            for (MetaField alreadyField : alreadyHaveFieldList) {
                if (alreadyField.getFieldCode().equals(fieldCode)) {
                    break;
                }
            }

            MetaField metaField = new MetaField();
            metaField.setFieldId(IdWorker.getId());
            metaField.setBusinessCategoryId(metaModel.getBusinessCategoryId());
            metaField.setBusinessId(metaModel.getBusinessId());
            metaField.setMetaModelId(metaModel.getMetaModelId());
            metaField.setMetaDefinitionId(metaModel.getCurrentDefinitionId());
            metaField.setFieldCode(fieldCode);
            metaField.setFieldType(MetaFieldTypeEnum.LIST.getCode());
            metaField.setFieldLength(0);
            metaField.setDecimalLength(0);
            metaField.setNotEmptyFlag(YesOrNotEnum.N.getCode());
            metaField.setKeyFlag(YesOrNotEnum.N.getCode());
            metaField.setRealFlag(YesOrNotEnum.N.getCode());
            metaField.setDescription(tableMetaFieldDTO.getTableDescription());
            metaField.setFldSort(maxSort.add(new BigDecimal(100)));
            newFieldList.add(metaField);
        }

        if (!newFieldList.isEmpty()) {
            this.saveBatch(newFieldList);
        }
    }

    @Override
    public List<MetaField> getFieldListByModelId(String realFlag, Long metaModelId) {

        MetaModel metaModel = metaModelService.getById(metaModelId);
        if (metaModel == null) {
            return new ArrayList<>();
        }

        return metaFieldService.quickGetFieldList(realFlag, ListUtil.of(metaModel.getCurrentDefinitionId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateFieldList(MetaFieldRequest metaFieldRequest) {

        Long metaDefinitionId = metaFieldRequest.getMetaDefinitionId();
        MetaDefinition currentDefinitionId = metaDefinitionService.getById(metaDefinitionId);

        List<MetaField> metaFieldList = metaFieldRequest.getMetaFieldList();
        List<MetaField> newMetaFieldList = FieldFactory.createMetaField(currentDefinitionId, metaFieldList);

        ValidateFieldFactory.validateRepeatField(newMetaFieldList);

        LambdaUpdateWrapper<MetaField> metaFieldLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        metaFieldLambdaUpdateWrapper.eq(MetaField::getMetaDefinitionId, metaDefinitionId);
        this.remove(metaFieldLambdaUpdateWrapper);

        this.getBaseMapper().insertBatchSomeColumn(newMetaFieldList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int sqlImport(SqlImportRequest sqlImportRequest) {

        CreateTableSqlParseApi createTableSqlParseApi = CreateTableSqlParseFactory.createApi(sqlImportRequest.getDbType());

        String createTableSqlStrs = sqlImportRequest.getCreateTableSql();

        String[] tableList = createTableSqlStrs.split(";\n");

        int successNumber = 0;
        List<TableParseInfo> tableParseInfoList = new ArrayList<>();
        for (String createTableSql : tableList) {
            TableParseInfo tableParseInfo = null;
            try {
                tableParseInfo = createTableSqlParseApi.parseToFields(createTableSql);
            } catch (Exception e) {

                continue;
            }

            List<MetaField> tempMetaFieldList = tableParseInfo.getMetaFieldList();
            if (ObjectUtil.isEmpty(tempMetaFieldList)) {
                throw new ServiceException(MetaFieldExceptionEnum.IMPORT_FAIL_FOR_EXIST_NULL_FIELD);
            }
            TableParseInfo finalTableParseInfo = tableParseInfo;
            tempMetaFieldList.stream().filter(metaField ->
                            metaField.getKeyFlag().equals(YesOrNotEnum.Y.getCode()))
                    .findFirst()
                    .orElseThrow(() -> new MetaException(MetaFieldExceptionEnum.IMPORT_FAIL_FOR_NOT_EXIST_PRIMARY_KEY, finalTableParseInfo.getTableName()));
            tableParseInfoList.add(tableParseInfo);
        }

        for (TableParseInfo tableParseInfo : tableParseInfoList) {

            MetaDefinition metaDefinition = this.metaModelService.quickAddModelAndDefinition(sqlImportRequest.getBusinessCategoryId(),
                    sqlImportRequest.getBusinessId(), tableParseInfo.getTableComment(), tableParseInfo.getTableName());

            List<MetaField> metaFieldList = tableParseInfo.getMetaFieldList();
            MetaFieldRequest metaFieldRequest = new MetaFieldRequest();
            metaFieldRequest.setMetaDefinitionId(metaDefinition.getMetaDefinitionId());
            metaFieldRequest.setMetaFieldList(metaFieldList);
            metaFieldService.saveOrUpdateFieldList(metaFieldRequest);

            successNumber++;
        }

        return successNumber;
    }

    @Override
    public List<MetaField> getDefaultFieldList(Integer defaultFieldType) {
        return FieldFactory.createDefaultMetaField(defaultFieldType);
    }

    @Override
    public void customBatchSave(List<MetaField> metaFieldList) {
        this.getBaseMapper().insertBatchSomeColumn(metaFieldList);
    }

    @Override
    public List<MetaFieldShowDTO> getFieldCollectionByModelIdList(MetaFieldRequest metaFieldRequest) {

        List<Long> metaDefinitionIdList = metaFieldRequest.getMetaDefinitionIdList();

        LambdaQueryWrapper<MetaField> metaFieldLambdaQueryWrapper = new LambdaQueryWrapper<>();
        metaFieldLambdaQueryWrapper.in(MetaField::getMetaDefinitionId, metaDefinitionIdList);
        metaFieldLambdaQueryWrapper.select(MetaField::getFieldId, MetaField::getFieldCode, MetaField::getDescription,
                MetaField::getMetaModelId, MetaField::getNotEmptyFlag);
        List<MetaField> metaFieldList = this.list(metaFieldLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(metaFieldList)) {
            return new ArrayList<>();
        }

        Map<String, MetaField> metaFieldListMerge = new LinkedHashMap<>();
        for (MetaField metaField : metaFieldList) {
            if (!metaFieldListMerge.containsKey(metaField.getFieldCode())) {
                metaFieldListMerge.put(metaField.getFieldCode(), metaField);
            }
        }

        List<MetaFieldShowDTO> metaFieldShowDTOS = new ArrayList<>();
        for (MetaField metaField : metaFieldListMerge.values()) {

            MetaFieldShowDTO metaFieldShowDTO = new MetaFieldShowDTO();
            metaFieldShowDTO.setFieldId(metaField.getFieldId());
            metaFieldShowDTO.setFieldCode(metaField.getFieldCode());
            metaFieldShowDTO.setFieldShowName(metaField.getDescription());
            metaFieldShowDTO.setNotEmptyFlag(metaField.getNotEmptyFlag());

            metaFieldShowDTO.setModelName(metaModelService.getMetaModelNameById(metaField.getMetaModelId()));

            metaFieldShowDTOS.add(metaFieldShowDTO);
        }

        return metaFieldShowDTOS;
    }

    @Override
    public List<MetaField> getBatchMetaFieldList(List<Long> metaDefinitionIdList) {

        LambdaQueryWrapper<MetaField> metaFieldLambdaQueryWrapper = new LambdaQueryWrapper<>();

        metaFieldLambdaQueryWrapper.in(MetaField::getMetaDefinitionId, metaDefinitionIdList);

        metaFieldLambdaQueryWrapper.select(MetaField::getMetaDefinitionId, MetaField::getFieldId, MetaField::getFieldCode,
                MetaField::getDescription, MetaField::getFieldType, MetaField::getRealFlag);

        return this.list(metaFieldLambdaQueryWrapper);
    }

    @Override
    public void updateVirtualField(MetaFieldRequest metaFieldRequest) {

        Long metaDefinitionId = metaFieldRequest.getMetaDefinitionId();
        MetaDefinition currentDefinitionId = metaDefinitionService.getById(metaDefinitionId);

        LambdaUpdateWrapper<MetaField> metaFieldLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        metaFieldLambdaUpdateWrapper.eq(MetaField::getMetaDefinitionId, metaDefinitionId);
        metaFieldLambdaUpdateWrapper.eq(MetaField::getRealFlag, YesOrNotEnum.N.getCode());
        this.remove(metaFieldLambdaUpdateWrapper);

        List<MetaField> metaFieldList = metaFieldRequest.getMetaFieldList();
        if (ObjectUtil.isEmpty(metaFieldList)) {
            return;
        }
        List<MetaField> newMetaFieldList = FieldFactory.createMetaField(currentDefinitionId, metaFieldList);
        FieldFactory.fillVirtualFieldInfo(newMetaFieldList);

        this.getBaseMapper().insertBatchSomeColumn(newMetaFieldList);
    }

    @Override
    public void cleanBusinessFields(Long businessId) {
        LambdaQueryWrapper<MetaField> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MetaField::getBusinessId, businessId);
        this.remove(wrapper);
    }

    @Override
    public List<MetaField> findList(MetaFieldRequest metaFieldRequest) {
        LambdaQueryWrapper<MetaField> wrapper = this.createWrapper(metaFieldRequest);
        return this.list(wrapper);
    }

    @Override
    public List<MetaFieldDTO> getModelFieldInfoList(Set<Long> metaFieldIdList) {

        LambdaQueryWrapper<MetaField> metaFieldLambdaQueryWrapper = new LambdaQueryWrapper<>();
        metaFieldLambdaQueryWrapper.in(MetaField::getFieldId, metaFieldIdList);
        metaFieldLambdaQueryWrapper.select(MetaField::getFieldId, MetaField::getFieldCode, MetaField::getFieldType,
                MetaField::getNotEmptyFlag, MetaField::getKeyFlag, MetaField::getDescription);
        List<MetaField> list = this.list(metaFieldLambdaQueryWrapper);

        List<Long> metaFieldIdListConvert = CollectionUtil.list(true, metaFieldIdList);
        List<MetaField> metaFields = SortUtils.sortListByObjectKey(list, metaFieldIdListConvert);

        return BeanUtil.copyToList(metaFields, MetaFieldDTO.class, CopyOptions.create().ignoreError());
    }

    @Override
    public List<MetaField> getBusinessMainModelFields(Long businessId) {

        MetaModel mainMetaModel = this.metaModelService.getMainMetaModel(businessId);

        Long currentDefinitionId = mainMetaModel.getCurrentDefinitionId();


        LambdaQueryWrapper<MetaField> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MetaField::getMetaDefinitionId, currentDefinitionId);
        wrapper.notIn(MetaField::getFieldCode, MetaConstants.COMMON_VIEW_RENDER_EXCLUDE);
        wrapper.eq(MetaField::getRealFlag, YesOrNotEnum.Y.getCode());
        wrapper.orderByAsc(MetaField::getFldSort);

        wrapper.select(MetaField::getFieldCode, MetaField::getKeyFlag, MetaField::getDescription, MetaField::getMetaModelId);

        return this.list(wrapper);
    }

    @Override
    public DatabaseTableDTO getTableFieldList(String tableName) {

        DatabaseMetadataRequest databaseMetadataRequest = DatabaseConnectionContext.currentDatabaseInfo();
        databaseMetadataRequest.setTableNameList(ListUtil.list(false, tableName));

        List<DatabaseTableDTO> tableList = databaseMetadataApi.getTableListByNameList(databaseMetadataRequest);
        if (ObjectUtil.isEmpty(tableList)) {
            return null;
        }

        return tableList.get(0);
    }

    private LambdaQueryWrapper<MetaField> createWrapper(MetaFieldRequest metaFieldRequest) {
        LambdaQueryWrapper<MetaField> queryWrapper = new LambdaQueryWrapper<>();

        String fieldCode = metaFieldRequest.getFieldCode();
        queryWrapper.like(ObjectUtil.isNotEmpty(fieldCode), MetaField::getFieldCode, fieldCode);

        return queryWrapper;
    }
}