package com.xjrsoft.module.form.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.DbUtil;
import cn.hutool.db.meta.MetaUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.model.generator.ComponentConfig;
import com.xjrsoft.common.utils.DatasourceUtil;
import com.xjrsoft.module.authority.service.IDataAuthTableRelationService;
import com.xjrsoft.module.form.dto.AddFormCodeFirstDto;
import com.xjrsoft.module.form.dto.AddFormDataFirstDto;
import com.xjrsoft.module.form.dto.UpdateFormCodeFirstDto;
import com.xjrsoft.module.form.dto.UpdateFormDataFirstDto;
import com.xjrsoft.module.form.entity.FormDesignConfig;
import com.xjrsoft.module.form.entity.FormHistory;
import com.xjrsoft.module.form.entity.FormTemplate;
import com.xjrsoft.module.form.mapper.FormHistoryMapper;
import com.xjrsoft.module.form.mapper.FormTemplateMapper;
import com.xjrsoft.module.form.service.IFormTemplateService;
import com.xjrsoft.module.form.utils.FromTemplateUtil;
import com.xjrsoft.module.generator.entity.TableConfig;
import com.xjrsoft.module.generator.entity.TableStructureConfig;
import com.xjrsoft.module.generator.utils.SqlUtil;
import com.xjrsoft.module.system.service.IDatabaselinkService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 表单设计模板  服务实现类
 * </p>
 *
 * @author tzx
 * @since 2022-05-09
 */
@Service
@AllArgsConstructor
public class FormTemplateServiceImpl extends MPJBaseServiceImpl<FormTemplateMapper, FormTemplate> implements IFormTemplateService {


    private final FormTemplateMapper formTemplateMapper;

    private final FormHistoryMapper formHistoryMapper;

    private final IDatabaselinkService databaselinkService;

    private final ObjectMapper objectMapper;

    private final IDataAuthTableRelationService dataAuthTableRelationService;


    @Override
    @SneakyThrows
    public Boolean addDataFirst(AddFormDataFirstDto dto) {
        FormTemplate template = BeanUtil.toBean(dto, FormTemplate.class);

        List<ComponentConfig> list = dto.getFormJson().getFormJson().getList();
        Optional<TableConfig> mainTableConfigOptional = dto.getFormJson().getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();

        if (!mainTableConfigOptional.isPresent()) {
            throw new MyException("主表不存在");
        }

        List<TableStructureConfig> tableStructureConfigs = FromTemplateUtil.wrapperTableStructureConfig(list, mainTableConfigOptional.get());
        dto.getFormJson().setTableStructureConfigs(tableStructureConfigs);
        template.setFormJson(JSONUtil.toJsonStr(dto.getFormJson(), JSONConfig.create().setIgnoreNullValue(false)));
        // 创建数据权限字段
        createDataAuthField(mainTableConfigOptional.get().getTableName(), dto.getFormJson());
        // 保存数据权限关系
        if (BooleanUtils.isTrue(dto.getFormJson().getIsDataAuth())) {
            saveDataAuth(dto.getFormJson(), tableStructureConfigs);
        }
        return formTemplateMapper.insert(template) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public Boolean addCodeFirst(AddFormCodeFirstDto dto) {
        FormTemplate template = BeanUtil.toBean(dto, FormTemplate.class);
        List<TableStructureConfig> tableStructureConfigs = dto.getFormJson().getTableStructureConfigs();

        //因为是codefirst 代码优先/界面优先
        //因为要统一自定义表单存入到数据库的json格式  所以 在这里需要将codefirst的表结构配置json格式转换成datafirst的json格式
        // TableStructureConfig -> TableConfig
        String databaseId = dto.getFormJson().getDatabaseId();
        dto.getFormJson().setTableConfigs(FromTemplateUtil.wrapperTableConfig(databaseId, tableStructureConfigs));

        //因为实体类里面 formJson 是字符串类型 所以必须手动赋值一下   此时的formjson  是转换过tableconfig的
        template.setFormJson(JSONUtil.toJsonStr(dto.getFormJson(), JSONConfig.create().setIgnoreNullValue(false)));

        createTable(tableStructureConfigs, databaseId);
        // 保存数据权限关系
        if (BooleanUtils.isTrue(dto.getFormJson().getIsDataAuth())) {
            saveDataAuth(dto.getFormJson(), tableStructureConfigs);
        }
        return formTemplateMapper.insert(template) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateDataFirst(UpdateFormDataFirstDto dto) {
        FormTemplate template = BeanUtil.toBean(dto, FormTemplate.class);

        List<ComponentConfig> list = dto.getFormJson().getFormJson().getList();
        Optional<TableConfig> mainTableConfigOptional = dto.getFormJson().getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();

        if (!mainTableConfigOptional.isPresent()) {
            throw new MyException("主表不存在");
        }

        List<TableStructureConfig> tableStructureConfigs = FromTemplateUtil.wrapperTableStructureConfig(list, mainTableConfigOptional.get());
        dto.getFormJson().setTableStructureConfigs(tableStructureConfigs);

        template.setFormJson(JSONUtil.toJsonStr(dto.getFormJson(), JSONConfig.create().setIgnoreNullValue(false)));

        // 创建数据权限字段
        createDataAuthField(mainTableConfigOptional.get().getTableName(), dto.getFormJson());

        FormHistory formHistory = new FormHistory();
        formHistory.setFormId(template.getId());
        formHistory.setFormJson(template.getFormJson());
        formHistoryMapper.insert(formHistory);
        // 保存数据权限关系
        saveDataAuth(dto.getFormJson(), tableStructureConfigs);
        return formTemplateMapper.updateById(template) > 0;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public Boolean updateCodeFirst(UpdateFormCodeFirstDto dto) {
        FormTemplate template = BeanUtil.toBean(dto, FormTemplate.class);
        List<TableStructureConfig> tableStructureConfigs = dto.getFormJson().getTableStructureConfigs();

        // 获取表单旧配置
//        FormTemplate oldTemplate = this.getById(dto.getId());
//        FormDesignConfig oldFormDesignConfig = objectMapper.readValue(oldTemplate.getFormJson(), FormDesignConfig.class);
        //因为是codefirst 代码优先/界面优先
        //因为要统一自定义表单存入到数据库的json格式  所以 在这里需要将codefirst的表结构配置json格式转换成datafirst的json格式
        // TableStructureConfig -> TableConfig
        dto.getFormJson().setTableConfigs(new ArrayList<>());

        String databaseId = dto.getFormJson().getDatabaseId();
        DbType dbType = DatasourceUtil.getDbType(databaseId);
        boolean isUpperCase = ArrayUtils.contains(new DbType[]{DbType.ORACLE, DbType.ORACLE_12C, DbType.DM}, dbType);
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            //因为要统一自定义表单存入到数据库的json格式  所以 在这里需要将codefirst的表结构配置json格式转换成datafirst的json格式
            // TableStructureConfig -> TableConfig
            TableConfig tableConfig = new TableConfig();
            tableConfig.setTableName(tableStructureConfig.getTableName());
            tableConfig.setIsMain(tableStructureConfig.getIsMain());
            tableConfig.setPkField(isUpperCase ? StringUtils.upperCase(GlobalConstant.DEFAULT_PK) : GlobalConstant.DEFAULT_PK);
            tableConfig.setPkType(GlobalConstant.DEFAULT_PK_TYPE);
            tableConfig.setRelationField(tableStructureConfig.getIsMain() ? null : isUpperCase ? StringUtils.upperCase(GlobalConstant.DEFAULT_FK) : GlobalConstant.DEFAULT_FK);
            tableConfig.setRelationTableField(tableStructureConfig.getIsMain() ? null : isUpperCase ? StringUtils.upperCase(GlobalConstant.DEFAULT_PK) : GlobalConstant.DEFAULT_PK);
            dto.getFormJson().getTableConfigs().add(tableConfig);
        }

        createTable(tableStructureConfigs, databaseId);
        template.setFormJson(JSONUtil.toJsonStr(dto.getFormJson(), JSONConfig.create().setIgnoreNullValue(false)));

        FormHistory formHistory = new FormHistory();
        formHistory.setFormId(template.getId());
        formHistory.setFormJson(template.getFormJson());
        formHistoryMapper.insert(formHistory);

        // 保存数据权限关系
        saveDataAuth(dto.getFormJson(), tableStructureConfigs);
        return formTemplateMapper.updateById(template) > 0;
    }


    /**
     * 创建表
     * 修改此方法逻辑，一定要对比一下 GeneratorServiceImpl 中的 同名方法  是否 也需要修改！
     *
     * @param tableStructureConfigs 表结构配置
     * @param databaseId            数据库id
     */
    private void createTable(List<TableStructureConfig> tableStructureConfigs, String databaseId) throws SQLException {
        //------------------------------------根据配置建表开始------------------------------------------
        {
            //判断是否为默认数据源
            if (StrUtil.equalsIgnoreCase(databaseId, GlobalConstant.DEFAULT_DATASOURCE_KEY)) {
                List<String> createTableSqls = DatasourceUtil.wrapperCreateTableSql(tableStructureConfigs, DatasourceUtil.getDataSourceMasterDbType());

                for (String sql : createTableSqls) {
                    DbUtil.use(DatasourceUtil.getDatasourceMaster()).execute(sql);
                }
            } else {
                DbType dbType = databaselinkService.getDbType(databaseId);
                List<String> createTableSqls = DatasourceUtil.wrapperCreateTableSql(tableStructureConfigs, dbType);
                for (String sql : createTableSqls) {
                    DbUtil.use(DatasourceUtil.getDataSource(databaseId)).execute(sql);
                }
            }

        }
        //------------------------------------根据配置建表结束------------------------------------------
    }

    /**
     * 保存表和数据权限关联关系
     * @param formDesignConfig
     * @param tableStructureConfigs
     */
    private void saveDataAuth(FormDesignConfig formDesignConfig, List<TableStructureConfig> tableStructureConfigs) {
        Optional<TableStructureConfig> maiTableOpt = tableStructureConfigs.stream().filter(TableStructureConfig::getIsMain).findFirst();
        List<Long> dataAuthList = formDesignConfig.getDataAuthList().stream().map(Long::valueOf).collect(Collectors.toList());
        dataAuthTableRelationService.saveDataAuthTableRelations(maiTableOpt.get().getTableName(), dataAuthList);
    }

    @SneakyThrows
    private void createDataAuthField(String tableName, FormDesignConfig formDesignConfig) {
        if (BooleanUtils.isTrue(formDesignConfig.getIsDataAuth())) {
            // 添加权限字段 rule_user_id
            String databaseId = formDesignConfig.getDatabaseId();
            DataSource dataSource = DatasourceUtil.getDataSource(databaseId);
            String[] columnNames = MetaUtil.getColumnNames(dataSource, tableName);
            if (!ArrayUtils.contains(columnNames, GlobalConstant.AUTH_USER_ID)) {
                DbType dbType = databaselinkService.getDbType(databaseId);
                Db.use(dataSource).executeBatch(SqlUtil.buildAddDataAuthFieldSqls(dbType, tableName));
            }
        }
    }
}
