package cn.fateverse.query.service.impl;

import cn.fateverse.query.entity.*;
import cn.fateverse.query.entity.bo.TableBo;
import cn.fateverse.query.mapper.TableColumnMapper;
import cn.fateverse.query.mapper.TableMapper;
import cn.fateverse.query.mapper.TableRelationalMappingMapper;
import cn.fateverse.query.mapper.TableRoleMapper;
import cn.hutool.core.bean.BeanUtil;
import cn.fateverse.common.core.entity.Option;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.Result;
import cn.fateverse.query.entity.dto.ImportDto;
import cn.fateverse.query.entity.dto.TableColumnDto;
import cn.fateverse.query.entity.dto.TableDto;
import cn.fateverse.query.entity.dto.TableRelationalMappingDto;
import cn.fateverse.query.entity.vo.TableVo;
import cn.fateverse.query.entity.query.TableQuery;
import cn.fateverse.query.service.DynamicDataSourceTableService;
import cn.fateverse.query.service.TableService;
import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.common.mybatis.utils.PageUtils;
import cn.fateverse.common.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 自定义查询表格维护 Controller
 *
 * @author clay
 * @date 2023-07-16
 */
@Slf4j
@Service
public class TableServiceImpl implements TableService {

    private final TableMapper tableMapper;

    private final TableRoleMapper tableRoleMapper;

    private final TableColumnMapper tableColumnMapper;

    private final TableRelationalMappingMapper relationalMappingMapper;

    private final DynamicDataSourceTableService dynamicDataSourceTableService;

    public TableServiceImpl(TableMapper tableMapper,
                            TableColumnMapper tableColumnMapper,
                            TableRelationalMappingMapper relationalMappingMapper,
                            TableRoleMapper tableRoleMapper,
                            DynamicDataSourceTableService dynamicDataSourceTableService) {
        this.tableMapper = tableMapper;
        this.tableColumnMapper = tableColumnMapper;
        this.relationalMappingMapper = relationalMappingMapper;
        this.tableRoleMapper = tableRoleMapper;
        this.dynamicDataSourceTableService = dynamicDataSourceTableService;
    }

    @Override
    public TableDataInfo<TableVo> searchList(TableQuery query) {
        PageUtils.startPage();
        List<Table> list = tableMapper.selectList(query);
        return PageUtils.convertDataTable(list, TableVo::toTableVo);
    }

    @Override
    public List<Option> searchOption(Long datasourceId) {
        TableQuery query = new TableQuery();
        query.setDataSourceId(datasourceId);
        List<Table> tables = tableMapper.selectList(query);
        if (null == tables || tables.isEmpty()) {
            return new ArrayList<>();
        }
        return tables.stream().map(table ->
                Option.builder()
                        .value(table.getTableId())
                        .label(table.getTableName() + ":" + table.getTableComment())
                        .build()
        ).collect(Collectors.toList());
    }

    @Override
    public List<Option> searchColumnOption(Long tableId) {
        List<TableColumn> tableColumns = tableColumnMapper.selectByTableId(tableId);
        if (tableColumns == null || tableColumns.isEmpty()) {
            return new ArrayList<>();
        }
        return tableColumns.stream().map(column ->
                Option.builder()
                        .value(column.getColumnName())
                        .label(column.getColumnName() + ":" + column.getColumnComment())
                        .build()).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Long> importTable(ImportDto dto) {
        List<String> tables = dto.getTables();
        Long dataSourceId = dto.getDataSourceId();
        Set<String> tableNameSet = tableMapper.selectTableNameByDataSourceId(dataSourceId);
        List<String> tableList = new ArrayList<>();
        List<String> exist = new ArrayList<>();
        for (String table : tables) {
            if (!tableNameSet.contains(table)) {
                tableList.add(table);
            } else {
                exist.add(table);
            }
        }
        if (tableList.isEmpty()) {
            return Result.error("需要新增的表在数据库中已存在!");
        }
        List<TableBo> tableBos = dynamicDataSourceTableService.searchFullTableByTableNames(tableList, dto.getDataSourceId());
        if (tableBos.isEmpty()) {
            return Result.error("当前表信息在数据库中已经不存在!");
        }
        String username = SecurityUtils.getUsername();
        Date nowDate = new Date();
        tableBos.forEach(tableBo -> {
            Table table = tableBo.getTable();
            tableMapper.insert(table);
            tableBo.getColumns().forEach(tableColumn -> {
                tableColumn.setTableId(table.getTableId());
                tableColumn.setCreateBy(username);
                tableColumn.setCreateTime(nowDate);
            });
            tableColumnMapper.batchInsert(tableBo.getColumns());
        });
        if (!exist.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            exist.forEach(str -> sb.append(str).append(","));
            return Result.ok(1001, "表:" + sb + "在数据库中已存在!", null);
        }
        if (tableBos.size() == 1) {
            return Result.ok(tableBos.get(0).getTable().getTableId());
        }
        return Result.ok();
    }

    @Override
    public TableInfo searchById(Long tableId) {
        Table table = tableMapper.selectById(tableId);
        if (null == table) {
            throw new CustomException("查询结果为空");
        }
        List<TableColumn> columnList = tableColumnMapper.selectByTableId(tableId);
        if (null == columnList) {
            columnList = new ArrayList<>();
        }
        List<TableColumnDto> columnDtoList = columnList.stream().map(column -> {
            TableColumnDto target = new TableColumnDto();
            BeanUtil.copyProperties(column, target);
            return target;
        }).collect(Collectors.toList());
        List<TableRelationalMapping> tableRelationalMappings = relationalMappingMapper.selectByMainId(tableId);
        if (null == tableRelationalMappings) {
            tableRelationalMappings = new ArrayList<>();
        }
        List<TableRelationalMappingDto> mappingDtoList = tableRelationalMappings.stream().map(TableRelationalMappingDto::toTableRelationalMappingDto).collect(Collectors.toList());
        TableDto tableDto = new TableDto();
        BeanUtils.copyProperties(table, tableDto);
        List<TableRole> tableRoleList = tableRoleMapper.selectByTableId(tableId);
        List<Long> roleIds = null;
        if (null == tableRoleList || tableRoleList.isEmpty()) {
            roleIds = new ArrayList<>();
        } else {
            roleIds = tableRoleList.stream().map(TableRole::getRoleId).collect(Collectors.toList());
        }
        return TableInfo.builder()
                .table(tableDto)
                .columnList(columnDtoList)
                .relationMappings(mappingDtoList)
                .roleIds(roleIds)
                .build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(TableInfo table) {
        Table info = table.getTable().toTable();
        info.setUpdateBy(SecurityUtils.getUsername());
        tableMapper.update(info);
        Long tableId = info.getTableId();
        if (!table.getRoleIds().isEmpty()) {
            tableRoleMapper.deleteByTableId(tableId);
            List<TableRole> roleList = table.getRoleIds().stream().map(roleId -> TableRole.builder().roleId(roleId).tableId(tableId).build()).collect(Collectors.toList());
            tableRoleMapper.batchInsert(roleList);
        }
        List<TableColumnDto> columnDtos = table.getColumnList();
        if (!columnDtos.isEmpty()) {
            List<TableColumn> columnList = columnDtos.stream().map(column -> TableColumn.builder()
                    .columnId(column.getColumnId())
                    .use(column.getUse())
                    .columnComment(column.getColumnComment())
                    .build()).collect(Collectors.toList());
            tableColumnMapper.batchUpdate(columnList);
        }
        List<TableRelationalMappingDto> relationMappings = table.getRelationMappings();
        if (null != relationMappings && !relationMappings.isEmpty()) {
            relationalMappingMapper.deleteByMainId(tableId);
            List<TableRelationalMapping> mappingList = relationMappings.stream().map(TableRelationalMappingDto::toTableRelationalMapping).collect(Collectors.toList());
            relationalMappingMapper.batchInsert(mappingList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncTable(Long tableId) {
        Table table = tableMapper.selectById(tableId);
        if (null == table) {
            throw new CustomException("操作失败!");
        }
        List<TableBo> tableBos = dynamicDataSourceTableService.searchFullTableByTableNames(Collections.singletonList(table.getTableName()), table.getDataSourceId());
        if (tableBos == null || tableBos.size() != 1) {
            throw new CustomException("同步数据失败，原表结构不存在");
        }
        List<TableColumn> dataBaseColumns = tableColumnMapper.selectByTableIds(Collections.singletonList(tableId));
        if (dataBaseColumns == null || dataBaseColumns.isEmpty()) {
            throw new CustomException("列信息查询为空!");
        }
        TableBo tableBo = tableBos.get(0);
        //同步表格信息
        if (ObjectUtils.isEmpty(table.getTableComment())) {
            table.setTableComment(tableBo.getTable().getTableComment());
            tableMapper.update(table);
        }
        List<TableColumn> sourceColumn = tableBo.getColumns();
        if (sourceColumn == null || sourceColumn.isEmpty()) {
            tableColumnMapper.deleteByTableId(tableId);
            return;
        }
        Map<String, TableColumn> dataBaseColumnMap = dataBaseColumns.stream().collect(Collectors.toMap(TableColumn::getColumnName, Function.identity()));
        Map<String, TableColumn> sourceColumnMap = sourceColumn.stream().collect(Collectors.toMap(TableColumn::getColumnName, Function.identity()));
        Set<String> dataBaseSet = dataBaseColumnMap.keySet();
        List<TableColumn> updateList = new ArrayList<>();
        List<TableColumn> insertList = new ArrayList<>();
        List<Long> removeIds = new ArrayList<>();

        sourceColumnMap.forEach((key, sourceColumnColumn) -> {
            if (dataBaseColumnMap.containsKey(key)) {
                TableColumn dataBaseColumn = dataBaseColumnMap.get(key);
                boolean flag = false;
                if (!dataBaseColumn.getPk().equals(sourceColumnColumn.getPk())) {
                    dataBaseColumn.setPk(sourceColumnColumn.getPk());
                    flag = true;
                }
                if (!dataBaseColumn.getColumnType().equals(sourceColumnColumn.getColumnType())) {
                    dataBaseColumn.setColumnType(sourceColumnColumn.getColumnType());
                    flag = true;
                }
                if (ObjectUtils.isEmpty(dataBaseColumn.getColumnComment()) && !ObjectUtils.isEmpty(sourceColumnColumn.getColumnComment())) {
                    dataBaseColumn.setColumnComment(sourceColumnColumn.getColumnComment());
                    flag = true;
                }
                if (flag) {
                    updateList.add(dataBaseColumn);
                }
                dataBaseSet.remove(key);
            } else {
                sourceColumnColumn.setTableId(tableId);
                insertList.add(sourceColumnColumn);
            }
        });
        if (!dataBaseSet.isEmpty()) {
            dataBaseSet.forEach(key -> removeIds.add(dataBaseColumnMap.get(key).getColumnId()));
        }
        if (!updateList.isEmpty()) {
            tableColumnMapper.syncUpdate(updateList);
        }
        if (!insertList.isEmpty()) {
            tableColumnMapper.batchInsert(insertList);
        }
        if (!removeIds.isEmpty()) {
            tableColumnMapper.batchRemove(removeIds);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int removeById(Long tableId) {
        tableRoleMapper.deleteByTableId(tableId);
        tableColumnMapper.deleteByTableId(tableId);
        relationalMappingMapper.deleteByMainId(tableId);
        return tableMapper.deleteById(tableId);
    }

}