package com.ruoyi.dxp.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

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.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.dxp.constant.DxpConstants;
import com.ruoyi.dxp.constant.DxpRedisKeyConstants;
import com.ruoyi.dxp.constant.DxpSyncStatusConstants;
import com.ruoyi.dxp.domain.DataIntegration;
import com.ruoyi.dxp.domain.Table;
import com.ruoyi.dxp.domain.TableColumn;
import com.ruoyi.dxp.dto.ResApiDbConnDto;
import com.ruoyi.dxp.dto.ResApiDbListDto;
import com.ruoyi.dxp.dto.ResApiPageDto;
import com.ruoyi.dxp.dto.ResApiQueryDto;
import com.ruoyi.dxp.dto.redis.RedisDatabaseDto;
import com.ruoyi.dxp.mapper.DataIntegrationMapper;
import com.ruoyi.dxp.mapper.TableColumnMapper;
import com.ruoyi.dxp.mapper.TableMapper;
import com.ruoyi.dxp.service.DatabaseInitThread;
import com.ruoyi.dxp.service.ITableService;
import com.ruoyi.dxp.utils.DatabaseUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.dxp.mapper.DatabaseMapper;
import com.ruoyi.dxp.domain.Database;
import com.ruoyi.dxp.service.IDatabaseService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 数据源Service业务层处理
 *
 * @author jy
 * @date 2022-11-06
 */
@Service
public class DatabaseServiceImpl implements IDatabaseService {
    @Autowired
    private DatabaseMapper databaseMapper;

    @Autowired
    private TableMapper tableMapper;

    @Autowired
    private ITableService tableService;

    @Autowired
    private TableColumnMapper tableColumnMapper;

    @Autowired
    private RedisCache redisCache;

    /**
     * 查询数据源
     *
     * @param id 数据源主键
     * @return 数据源
     */
    @Override
    public Database selectDatabaseById(Long id) {
        return databaseMapper.selectDatabaseById(id);
    }

    @Autowired
    private DataIntegrationMapper dataIntegrationMapper;

    /**
     * 查询数据源列表
     *
     * @param database 数据源
     * @return 数据源
     */
    @Override
    public List<Database> selectDatabaseList(Database database) {
        return databaseMapper.selectDatabaseList(database);
    }

    /**
     * 新增数据源
     *
     * @param database 数据源
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult insertDatabase(Database database) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        database.setCreateTime(DateUtils.getNowDate());
        database.setCreateBy(loginUser.getUsername());
        database.setStatus(DxpSyncStatusConstants.WAIT_SYNC);

        // 保存数据库
        this.databaseMapper.insertDatabase(database);

        // 检查数据库连接字符串
        ResApiDbConnDto config = new ResApiDbConnDto();
        config.setId(database.getId());
        config.setConnection(database.getConnectionString());
        config.setType(database.getDatabaseType());
        config.setMode("add");
        config.setDbName(database.getDatabaseName());
        R<String> connReslut = DatabaseUtils.conn(config);
        if (connReslut.getCode() != HttpStatus.SUCCESS) {
            throw new Exception(connReslut.getMsg());
        }

        // 写入redis
        this.writeToRedis();

        // 异步拉取数据库结构
        DatabaseInitThread batchCreateThread = new DatabaseInitThread(database, databaseMapper, tableMapper, tableColumnMapper, loginUser.getUsername());
        batchCreateThread.start();

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

    /**
     * 同步数据库结构
     *
     * @param id 数据库id
     * @return
     */
    @Override
    public AjaxResult sync(Long id) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Database database = this.databaseMapper.selectDatabaseById(id);
        if (database == null) {
            return AjaxResult.error("数据源不存在");
        }

        if (database.getStatus().equals(DxpSyncStatusConstants.WAIT_SYNC)) {
            return AjaxResult.error("当前数据源同步中");
        }

        // 检查数据库连接字符串
        ResApiDbConnDto config = new ResApiDbConnDto();
        config.setId(database.getId());
        config.setConnection(database.getConnectionString());
        config.setType(database.getDatabaseType());
        config.setMode("add");
        config.setDbName(database.getDatabaseName());
        R<String> connReslut = DatabaseUtils.conn(config);
        if (connReslut.getCode() != HttpStatus.SUCCESS) {
            return AjaxResult.error(connReslut.getMsg());
        }

        database.setStatus(DxpSyncStatusConstants.WAIT_SYNC);
        this.databaseMapper.updateDatabase(database);

        // 同步删除关联的字段
        TableColumn reqC = new TableColumn();
        reqC.setDatabaseId(id);
        List<Long> columnIds = tableColumnMapper.selectTableColumnList(reqC).stream().map(t -> {
            return t.getId();
        }).collect(Collectors.toList());
        if (columnIds != null && columnIds.size() > 0) {
            tableColumnMapper.deleteTableColumnByIds(columnIds.toArray(new Long[0]));
        }

        // 开始异步同步
        DatabaseInitThread batchCreateThread = new DatabaseInitThread(database, databaseMapper, tableMapper, tableColumnMapper, loginUser.getUsername());
        batchCreateThread.start();

        return AjaxResult.success();
    }

    /**
     * 修改数据源
     *
     * @param database 数据源
     * @return 结果
     */
    @Override
    public AjaxResult updateDatabase(Database database) {
        Database old = databaseMapper.selectDatabaseById(database.getId());
        LoginUser loginUser = SecurityUtils.getLoginUser();
        old.setAliasName(database.getAliasName());
        old.setCacheFlag(database.getCacheFlag());
        old.setUpdateTime(DateUtils.getNowDate());
        old.setUpdateBy(loginUser.getUsername());
        databaseMapper.updateDatabase(old);

        // 写入redis
        this.writeToRedis();
        return AjaxResult.success();
    }

    /**
     * 删除数据源信息
     *
     * @param id 数据源主键
     * @return 结果
     */
    @Override
    public AjaxResult deleteDatabaseById(Long id) {
        // 检查是否有关联
        DataIntegration req = new DataIntegration();
        req.setDatabaseId(id);
        List<DataIntegration> dataIntegrations = dataIntegrationMapper.selectDataIntegrationList(req);
        if (dataIntegrations != null && dataIntegrations.size() > 0) {
            return AjaxResult.error("当前数据源已经关联数据，不能删除");
        }
        databaseMapper.deleteDatabaseById(id);

        // 同步删除关联的表及列
        Table reqT = new Table();
        reqT.setDatabaseId(id);
        List<Long> tableIds = tableMapper.selectTableList(reqT).stream().map(t -> {
            return t.getId();
        }).collect(Collectors.toList());
        if (tableIds != null && tableIds.size() > 0) {
            for (Long tableId : tableIds) {
                tableService.deleteTableById(tableId);
            }
        }

        return AjaxResult.success();
    }

    /**
     * 写入redis
     */
    @Override
    public void writeToRedis() {
        // 查询所有正常的数据库
        Database req = new Database();
        req.setStatus(DxpConstants.STATUS_VALID);

        List<Database> databases = databaseMapper.selectDatabaseList(req);
        if (databases != null && databases.size() > 0) {
            List<RedisDatabaseDto> redisDatabaseDtos = new ArrayList<>();
            for (Database db : databases) {
                RedisDatabaseDto redisDatabaseDto = new RedisDatabaseDto();
                redisDatabaseDto.setId(db.getId());
                redisDatabaseDto.setType(db.getDatabaseType());
                redisDatabaseDto.setMode("add");
                redisDatabaseDto.setDbName(db.getDatabaseName());
                redisDatabaseDto.setConnection(db.getConnectionString());
                redisDatabaseDtos.add(redisDatabaseDto);
            }
            redisCache.deleteObject(DxpRedisKeyConstants.DATABASE);
            redisCache.setCacheList(DxpRedisKeyConstants.DATABASE, redisDatabaseDtos);
        }
    }

    /**
     * 从远程同步数据结构
     */
    @Override
    public void syncAll() {
        // 查询所有正常的数据库
        Database req = new Database();
        req.setStatus(DxpConstants.STATUS_VALID);

        List<Database> databases = databaseMapper.selectDatabaseList(req);
        if (databases != null && databases.size() > 0) {
            for (Database database : databases) {
                this.sync(database.getId());
            }
        }
    }
}
