package com.ruoyi.dxp.service.impl;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.dxp.constant.DxpConstants;
import com.ruoyi.dxp.constant.DxpSyncStatusConstants;
import com.ruoyi.dxp.domain.*;
import com.ruoyi.dxp.dto.*;
import com.ruoyi.dxp.dto.database.*;
import com.ruoyi.dxp.mapper.*;
import com.ruoyi.dxp.service.IDataIntegrationService;
import com.ruoyi.dxp.service.IDataItemService;
import com.ruoyi.dxp.service.INoticeService;
import com.ruoyi.dxp.service.ITableService;
import com.ruoyi.dxp.utils.DatabaseUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 整合数据Service业务层处理
 *
 * @author jy
 * @date 2022-11-12
 */
@Service
public class DataIntegrationServiceImpl implements IDataIntegrationService {
    @Autowired
    private DataIntegrationMapper dataIntegrationMapper;

    @Autowired
    private DataItemMapper dataItemMapper;

    @Autowired
    private DatabaseMapper databaseMapper;

    @Autowired
    private TableMapper tableMapper;

    @Autowired
    private ITableService tableService;

    @Autowired
    private TableColumnMapper tableColumnMapper;

    @Autowired
    private DataShareMapper dataShareMapper;

    @Autowired
    private IDataItemService dataItemService;

    @Autowired
    private INoticeService noticeService;

    /**
     * 查询整合数据
     *
     * @param id 整合数据主键
     * @return 整合数据
     */
    @Override
    public DataIntegrationDto selectDataIntegrationById(Long id) {
        DataIntegrationDto dataIntegrationDto = new DataIntegrationDto();

        // 查询整合数据
        DataIntegration dataIntegration = dataIntegrationMapper.selectDataIntegrationById(id);
        BeanUtils.copyProperties(dataIntegration, dataIntegrationDto);

        // 查询整合数据项
        DataItem req = new DataItem();
        req.setDataId(id);
        List<DataItem> dataItems = this.dataItemMapper.selectDataItemList(req);
        dataIntegrationDto.setDataItems(dataItems);

        return dataIntegrationDto;
    }

    /**
     * 查询整合数据列表
     *
     * @param dataIntegration 整合数据
     * @return 整合数据
     */
    @Override
    public List<DataIntegration> selectDataIntegrationList(DataIntegration dataIntegration) {
        return dataIntegrationMapper.selectDataIntegrationList(dataIntegration);
    }

    /**
     * 新增整合数据
     *
     * @param dataIntegration 整合数据
     * @return 结果
     */
    @Override
    @Transactional
    public AjaxResult insertDataIntegration(DataIntegration dataIntegration) {
        dataIntegration.setStatus(DxpConstants.STATUS_VALID);
        // 检查参数
        if (dataIntegration.getDatabaseId() == null) {
            return AjaxResult.error("数据源不能为空");
        }

        Database database = this.databaseMapper.selectDatabaseById(dataIntegration.getDatabaseId());
        if (database == null) {
            return AjaxResult.error("数据源不存在");
        }

        Table table = this.tableMapper.selectTableById(dataIntegration.getTableId());
        if (table == null) {
            return AjaxResult.error("表不存在");
        }

        if (StringUtils.isEmpty(dataIntegration.getDataName())) {
            dataIntegration.setDataName(table.getTableName() + "挂载数据");
        }

        dataIntegration.setTableName(table.getTableName());

        if (table.getDatabaseId() != dataIntegration.getDatabaseId()) {
            return AjaxResult.error("提交表不属于提交的数据源");
        }

        if (dataIntegration.getTableId() == null) {
            return AjaxResult.error("数据表不能为空");
        }

        // 保存整合数据
        LoginUser loginUser = SecurityUtils.getLoginUser();
        dataIntegration.setCreateBy(loginUser.getUsername());
        dataIntegration.setCreateTime(DateUtils.getNowDate());
        dataIntegrationMapper.insertDataIntegration(dataIntegration);

        // 保存数据项
        TableColumn tableColumnReq = new TableColumn();
        tableColumnReq.setTableId(dataIntegration.getTableId());
        List<TableColumn> tableColumns = this.tableColumnMapper.selectTableColumnList(tableColumnReq);
        createDataItem(dataIntegration, tableColumns, loginUser);
        return AjaxResult.success(dataIntegration.getId());
    }

    /**
     * 挂载
     *
     * @param dataIntegrationBatch
     * @return
     */
    @Override
    @Transactional
    public AjaxResult batchDataIntegration(DataIntegrationBatch dataIntegrationBatch) {
        for (Long tableId : dataIntegrationBatch.getTableIds()) {
            DataIntegration dataIntegration = new DataIntegration();
            dataIntegration.setDatabaseId(dataIntegrationBatch.getDatabaseId());
            dataIntegration.setTableId(tableId);
            dataIntegration.setIsMetadata(DxpConstants.NO);
            this.insertDataIntegration(dataIntegration);
        }

        return AjaxResult.success();
    }

    /**
     * 修改整合数据
     *
     * @param dataIntegration 整合数据
     * @return 结果
     */
    @Override
    @Transactional
    public int updateDataIntegration(DataIntegration dataIntegration) {
        DataIntegration newData = this.dataIntegrationMapper.selectDataIntegrationById(dataIntegration.getId());
        LoginUser loginUser = SecurityUtils.getLoginUser();
        newData.setInfoId(dataIntegration.getInfoId());
        newData.setProvideOrganization(dataIntegration.getProvideOrganization());
        newData.setTag(dataIntegration.getTag());
        newData.setRemark(dataIntegration.getRemark());
        newData.setCatalogueId(dataIntegration.getCatalogueId());
        newData.setDataName(dataIntegration.getDataName());
        newData.setUpdateBy(loginUser.getUsername());
        newData.setUpdateTime(DateUtils.getNowDate());
        int result = dataIntegrationMapper.updateDataIntegration(newData);

        // 删除已有的字段
        dataItemService.deleteDataItemByTypeAndDataId(DxpConstants.DATA_TYPE_INTEGRATION, dataIntegration.getId());

        // 重新保存数据字段
        TableColumn tableColumnReq = new TableColumn();
        tableColumnReq.setTableId(dataIntegration.getTableId());
        List<TableColumn> tableColumns = this.tableColumnMapper.selectTableColumnList(tableColumnReq);
        createDataItem(dataIntegration, tableColumns, loginUser);

        // 检查整合数据，共享数据是否有更新
        this.compareIntegrationAndShare(dataIntegration.getId());
        return result;
    }

    /**
     * 删除整合数据信息
     *
     * @param id 整合数据主键
     * @return 结果
     */
    @Override
    public AjaxResult deleteDataIntegrationById(Long id) {
        // 检查是否关联了共享数据
        DataShare sdReq = new DataShare();
        sdReq.setDataId(id);
        List<DataShare> dataShares = dataShareMapper.selectDataShareList(sdReq);
        if (dataShares != null && dataShares.size() > 0) {
            return AjaxResult.error("当前整合数据关联了共享数据，不能删除");
        }

        DataIntegration dataIntegration = dataIntegrationMapper.selectDataIntegrationById(id);
        if (dataIntegration == null) {
            return AjaxResult.error("数据不存在");
        }

        // 删除整合数据
        dataIntegrationMapper.deleteDataIntegrationById(id);

        // 删除数据字段
        dataItemService.deleteDataItemByTypeAndDataId(DxpConstants.DATA_TYPE_INTEGRATION, id);

        // 元数据同步删除数据表
        if (dataIntegration.getIsMetadata().equals(DxpConstants.YES)) {
            // 删除本地数据表
            tableService.deleteTableById(dataIntegration.getTableId());

            // 删除远程数据表
            DatabaseUtils.deleteTable(dataIntegration.getDatabaseId(), dataIntegration.getTableName());
        }

        return AjaxResult.success();
    }

    /**
     * 从数据源同步整合数据
     *
     * @param databaseId 数据源id
     */
    @Override
    public void syncFromDatabase(Long databaseId) {
        // 找到数据源下的所有表
        Table tReq = new Table();
        tReq.setDatabaseId(databaseId);
        List<Table> tableList = tableMapper.selectTableList(tReq);
        if (tableList == null || tableList.size() == 0) {
            return;
        }

        // 循环查找使用了表的整合数据
        for (Table table : tableList) {
            // 找到使用了改表的整合数据
            DataIntegration dReq = new DataIntegration();
            dReq.setDatabaseId(table.getDatabaseId());
            dReq.setTableId(table.getId());
            List<DataIntegration> dataIntegrations = dataIntegrationMapper.selectDataIntegrationList(dReq);

            if (dataIntegrations == null || dataIntegrations.size() == 0) {
                continue;
            }

            // 更新对应的整合数据
            for (DataIntegration dataIntegration : dataIntegrations) {
                this.updateDataIntegration(dataIntegration);
            }
        }
    }

    /**
     * 保存元数据
     *
     * @param metaDataSave 元数据保存对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult saveMetadata(MetaDataSave metaDataSave) throws Exception {
        Boolean isAdd = metaDataSave.getId() == null;
        metaDataSave.setStatus(DxpConstants.STATUS_VALID);
        // 参数检查
        if (metaDataSave.getDatabaseId() == null) {
            throw new SQLException("数据源不能为空");
        }

        String oldValidateFieds = "";
        if (!isAdd) {
            DataIntegration dataIntegration = dataIntegrationMapper.selectDataIntegrationById(metaDataSave.getId());
            if (dataIntegration == null) {
                throw new SQLException("待编辑元数据不存在");
            }

            oldValidateFieds = dataIntegration.getRepetitionField();
        }


        Database database = databaseMapper.selectDatabaseById(metaDataSave.getDatabaseId());
        if (database == null) {
            throw new SQLException("数据源错误");
        }

        if (StringUtils.isEmpty(metaDataSave.getTableName())) {
            throw new SQLException("表名不能为空");
        }

        // 新建时，判断表名是否存在
        if (isAdd) {
            Table req = new Table();
            req.setDatabaseId(metaDataSave.getDatabaseId());
            req.setTableName(metaDataSave.getTableName());
            List<Table> tableList = tableMapper.selectTableList(req);
            if (tableList != null && tableList.size() > 0) {
                return AjaxResult.error("当前表已经在选中的数据源中存在");
            }
        }

        if (StringUtils.isEmpty(metaDataSave.getDataName())) {
            throw new SQLException("元数据名称不能为空");
        }

        if (metaDataSave.getCatalogueId() == null) {
            throw new SQLException("元数据目录不能为空");
        }

        if (isAdd) {
            if (metaDataSave.getColumn() == null || metaDataSave.getColumn().size() == 0) {
                throw new SQLException("动态列不能为空");
            }
        }

        if (metaDataSave.getData() == null || metaDataSave.getData().size() == 0) {
            throw new SQLException("动态数据不能为空");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();

        // 新建时处理远程表和表结构
        if (isAdd) {
            // 远程建表
            List<DatabaseUtilsColnInfoDto> columns = new ArrayList<>();
            for (String column : metaDataSave.getColumn()) {
                DatabaseUtilsColnInfoDto col = new DatabaseUtilsColnInfoDto();
                col.setColName(column);
                if (column.equals("id")) {
                    col.setDataType("bigint");
                    col.setDataLength(20);
                    col.setIsIncrement(1);
                    col.setIsPk(1);
                    col.setIsRequired(1);
                } else {
                    col.setDataType("text");
//                    col.setDataLength(400);
                    col.setIsIncrement(0);
                    col.setIsPk(0);
                    col.setIsRequired(0);
                }

                columns.add(col);
            }

            DatabaseUtilsCreateTableParam createTableParam = new DatabaseUtilsCreateTableParam();
            createTableParam.setColumns(columns);
            createTableParam.setConnection(database.getConnectionString());
            createTableParam.setDbName(database.getDatabaseName());
            createTableParam.setId(database.getId());
            createTableParam.setTbName(metaDataSave.getTableName());
            createTableParam.setDbType(database.getDatabaseType());
            R<String> createResult = DatabaseUtils.createTable(createTableParam);
            if (createResult.getCode() != HttpStatus.SUCCESS) {
                throw new SQLException("远程建表失败：" + createResult.getMsg());
            }

            // 本地保存表数据
            Table table = new Table();
            table.setDatabaseId(metaDataSave.getDatabaseId());
            table.setTableName(metaDataSave.getTableName());
            table.setTableComment("元数据：" + metaDataSave.getDataName());
            table.setCreateBy(loginUser.getUsername());
            table.setCreateTime(DateUtils.getNowDate());
            tableMapper.insertTable(table);

            // 本地保存表列数据
            R<List<TableColumn>> columnListResult = DatabaseUtils.getColumnList(database.getId(), table.getTableName());
            if (columnListResult.getCode() == HttpStatus.SUCCESS) {
                // 保存列
                List<TableColumn> tableColumns = columnListResult.getData();
                if (tableColumns != null && tableColumns.size() > 0) {
                    for (TableColumn column : tableColumns) {
                        column.setDatabaseId(database.getId());
                        column.setTableId(table.getId());
                        column.setCreateTime(DateUtils.getNowDate());
                        column.setCreateBy(loginUser.getUsername());
                        this.tableColumnMapper.insertTableColumn(column);
                    }
                }
            }

            metaDataSave.setTableId(table.getId());
        }

        // 验重字段
        String repetitionField = metaDataSave.getRepetitionField();
        if (repetitionField == null) {
            repetitionField = "";
        }

        // 本次新增
        if (StringUtils.isNotEmpty(repetitionField)) {
            DatabaseUtilsIndexParam indexParam = new DatabaseUtilsIndexParam();
            indexParam.setId(database.getId());
            indexParam.setTbName(metaDataSave.getTableName());

            // 添加为数据表唯一索引
            for (String indexName : repetitionField.split(",")) {
                List<String> indexArray = new ArrayList<>();
                indexArray.add(indexName);
                indexParam.setIndexArray(indexArray);

                // 创建索引
                R<String> stringR1 = DatabaseUtils.addIndex(indexParam);
                if (stringR1.getCode() != HttpStatus.SUCCESS) {
                    throw new SQLException("创建唯一索引失败：" + stringR1.getMsg());
                }
            }
        }

        // 已经删除的
        if (!isAdd && StringUtils.isNotEmpty(oldValidateFieds)) {
            List<String> delFieds = new ArrayList<>();

            // 原来有，本次没有的
            for (String old : oldValidateFieds.split(",")) {
                if (StringUtils.isEmpty(repetitionField) || !repetitionField.contains(old)) {
                    delFieds.add(old);
                }
            }

            if (delFieds != null && delFieds.size() > 0) {
                DatabaseUtilsIndexParam delIndexParam = new DatabaseUtilsIndexParam();
                delIndexParam.setId(database.getId());
                delIndexParam.setTbName(metaDataSave.getTableName());
                delIndexParam.setIndexArray(delFieds);
                R<String> stringR = DatabaseUtils.deleteIndex(delIndexParam);
                if (stringR.getCode() != HttpStatus.SUCCESS) {
                    throw new SQLException("删除唯一索引失败：" + stringR.getMsg());
                }
            }
        }

        // 全量导入先删除原来数据
        if (metaDataSave.getMode().equals("all")) {
            // 远程删除表数据
            R<String> listR = DatabaseUtils.deleteTableData(database.getId(), metaDataSave.getTableName());
            if (listR.getCode() != HttpStatus.SUCCESS) {
                throw new SQLException("删除表数据失败：" + listR.getMsg());
            }
        }

        // 远程添加数据
        DatabaseUtilsInsertParam insertParam = new DatabaseUtilsInsertParam();
        insertParam.setId(database.getId());
        insertParam.setTbName(metaDataSave.getTableName());
        List<String> columns = new ArrayList<>(); // 为保证列顺序和值顺序一致，这里的列不使用参数，动态从值中计算
        List<DatabaseUtilsColItemDto> rows = new ArrayList<>();
        for (Object d : metaDataSave.getData()) {
            Map<String, String> map = (LinkedHashMap) d;
            List<String> list = new ArrayList<>();
            for (String key : map.keySet()) {
                if (!columns.contains(key)) {
                    columns.add(key);
                }

                list.add(map.get(key));
            }

            DatabaseUtilsColItemDto row = new DatabaseUtilsColItemDto();
            row.setList(list);
            rows.add(row);
        }

        insertParam.setColNameArray(columns);

        // 分页调用接口导入
        if (rows != null && rows.size() > 0) {
            int size = 100;
            int pageCount = rows.size() % size;
            for (int i = 0; i < pageCount; i++) {
                // 获取当页数据
                List<DatabaseUtilsColItemDto> currentRows = rows.stream().skip((i) * size).limit(size).
                        collect(Collectors.toList());
                insertParam.setRows(currentRows);
                R<String> stringR = DatabaseUtils.insertTable(insertParam);
//            if (stringR.getCode() != HttpStatus.SUCCESS) {
//                throw new SQLException("插入表数据失败：" + stringR.getMsg());
//            }
            }
        }

        // 本地保存整合数据
        DataIntegration dataIntegration = new DataIntegration();
        BeanUtils.copyProperties(metaDataSave, dataIntegration);
        dataIntegration.setIsMetadata(DxpConstants.YES);
        dataIntegration.setCreateBy(loginUser.getUsername());
        dataIntegration.setCreateTime(DateUtils.getNowDate());
        if (!isAdd) {
            dataIntegrationMapper.updateDataIntegration(dataIntegration);
        } else {
            dataIntegrationMapper.insertDataIntegration(dataIntegration);

            // 拉取新建表的数据结构
            R<List<TableColumn>> columnListResult = DatabaseUtils.getColumnList(metaDataSave.getDatabaseId(), metaDataSave.getTableName());
            if (columnListResult.getCode() != HttpStatus.SUCCESS) {
                throw new Exception(columnListResult.getMsg());
            }

            List<TableColumn> columnList = columnListResult.getData();
            for (TableColumn tableColumn : columnList) {
                tableColumn.setTableId(metaDataSave.getTableId());
            }

            // 创建明细
            createDataItem(dataIntegration, columnList, loginUser);
        }

        return AjaxResult.success(dataIntegration.getId());
    }

    /**
     * 查询元数据数据
     *
     * @param id id
     * @return
     */
    @Override
    public AjaxResult getData(Long id, Integer page, Integer pageSize) {
        DataIntegration dataIntegration = dataIntegrationMapper.selectDataIntegrationById(id);
        if (dataIntegration == null) {
            return AjaxResult.error("数据不存在");
        }

        // 获取远程数据
        DatabaseUtilsDbQueryParam queryParam = new DatabaseUtilsDbQueryParam();
        queryParam.setId(dataIntegration.getDatabaseId());
        queryParam.setTbName(dataIntegration.getTableName());
        queryParam.setPageNo(page);
        queryParam.setPageSize(pageSize);
        R<ResApiPageDto<List<Object>>> resApiPageDtoR = DatabaseUtils.queryTable(queryParam);
        if (resApiPageDtoR.getCode() != HttpStatus.SUCCESS) {
            return AjaxResult.error(resApiPageDtoR.getMsg());
        }

        return AjaxResult.success(resApiPageDtoR.getData());
    }

    /**
     * 创建整合数据项
     *
     * @param dataIntegration 整合数据
     * @param tableColumns    表列
     * @param loginUser       登录用户
     */
    private void createDataItem(DataIntegration dataIntegration, List<TableColumn> tableColumns, LoginUser loginUser) {
        if (tableColumns != null && tableColumns.size() > 0) {
            for (TableColumn tableColumn : tableColumns) {
                DataItem dataItem = new DataItem();
                BeanUtils.copyProperties(tableColumn, dataItem);
                dataItem.setDataId(dataIntegration.getId());
                dataItem.setDataType(DxpConstants.DATA_TYPE_INTEGRATION);
                dataItem.setIsVisable(DxpConstants.YES);
                dataItem.setCreateBy(loginUser.getUsername());
                dataItem.setCreateTime(DateUtils.getNowDate());
                dataItem.setSort(tableColumn.getSort());
                this.dataItemMapper.insertDataItem(dataItem);
            }
        }
    }

    /**
     * 比较整合数据和共享数据是否有变化
     *
     * @param integrationId 整合数据id
     */
    private void compareIntegrationAndShare(Long integrationId) {
        // 查询整合数据对应的共享数据
        DataShare dsReq = new DataShare();
        dsReq.setDataId(integrationId);
        List<DataShare> dataShares = dataShareMapper.selectDataShareList(dsReq);
        if (dataShares == null || dataShares.size() == 0) {
            return;
        }

        // 查询整合数据明细
        DataItem req = new DataItem();
        req.setDataType(DxpConstants.DATA_TYPE_INTEGRATION);
        req.setDataId(integrationId);
        List<DataItem> integrationDataItems = dataItemMapper.selectDataItemList(req);

        // 循环检查共享数据
        for (DataShare dataShare : dataShares) {
            // 查询共享数据的明细
            DataItem sReq = new DataItem();
            sReq.setDataType(DxpConstants.DATA_TYPE_SHARE);
            sReq.setDataId(dataShare.getId());
            List<DataItem> shareDataItems = dataItemMapper.selectDataItemList(sReq);

            // 比较明细是否有变化
            if (checkChange(integrationDataItems, shareDataItems)) {
                // 设置共享数据变化标志
                dataShare.setSyncFlag(DxpSyncStatusConstants.HAS_NEW_UPDATE);
                dataShareMapper.updateDataShare(dataShare);

                // 发送通知
                NoticeSendDto sendDto = new NoticeSendDto();
                sendDto.setNoticeType("dataShareStructure");
                noticeService.sendNotice(sendDto);
            }
        }
    }

    /**
     * 检查是否变化
     *
     * @param sources 源数据项
     * @param targets 目标数据项
     * @return 是否变化
     */
    private boolean checkChange(List<DataItem> sources, List<DataItem> targets) {
        Boolean changFlag = false;
        if (sources == null || targets == null) {
            // 任意源或目标为空，则表示有变化
            return true;
        } else if (sources.size() == 0 && targets.size() == 0) {
            // 源和目标都没有值
            return false;
        } else if (sources.size() != targets.size()) {
            // 源和目标长度不一致
            return true;
        } else {
            // 逐字段对比
            for (DataItem source : sources) {
                DataItem findTarget = targets.stream().filter(p -> p.getColumnName().equals(source.getColumnName())).findAny().orElse(null);
                if (findTarget == null) {
                    return true;
                }

                // columnComment
                if (!StringUtils.equals(source.getColumnComment(), findTarget.getColumnComment())) {
                    return true;
                }

                // columnType
                if (!StringUtils.equals(source.getColumnType(), findTarget.getColumnType())) {
                    return true;
                }

                // isPk
                if (!StringUtils.equals(source.getIsPk(), findTarget.getIsPk())) {
                    return true;
                }

                // isIncrement
                if (!StringUtils.equals(source.getIsIncrement(), findTarget.getIsIncrement())) {
                    return true;
                }
            }

            return false;
        }
    }
}
