package com.gitee.pifeng.monitoring.ui.business.web.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.dialect.DriverUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.pifeng.monitoring.common.constant.DbEnums;
import com.gitee.pifeng.monitoring.common.constant.ZeroOrOneConstants;
import com.gitee.pifeng.monitoring.common.domain.Result;
import com.gitee.pifeng.monitoring.common.dto.BaseRequestPackage;
import com.gitee.pifeng.monitoring.common.dto.BaseResponsePackage;
import com.gitee.pifeng.monitoring.plug.core.Sender;
import com.gitee.pifeng.monitoring.ui.business.web.dao.IMonitorDbDao;
import com.gitee.pifeng.monitoring.ui.business.web.entity.MonitorDb;
import com.gitee.pifeng.monitoring.ui.business.web.service.IMonitorDbService;
import com.gitee.pifeng.monitoring.ui.business.web.vo.HomeDbVo;
import com.gitee.pifeng.monitoring.ui.business.web.vo.LayUiAdminResultVo;
import com.gitee.pifeng.monitoring.ui.business.web.vo.MonitorDbVo;
import com.gitee.pifeng.monitoring.ui.constant.DbDriverClassConstants;
import com.gitee.pifeng.monitoring.ui.constant.UrlConstants;
import com.gitee.pifeng.monitoring.ui.constant.WebResponseConstants;
import com.gitee.pifeng.monitoring.ui.core.UiPackageConstructor;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.hyperic.sigar.SigarException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 数据库表服务实现类
 * </p>
 *
 * @author 皮锋
 * @custom.date 2020-12-19
 */
@Service
public class MonitorDbServiceImpl extends ServiceImpl<IMonitorDbDao, MonitorDb> implements IMonitorDbService {

    /**
     * UI端包构造器
     */
    @Autowired
    private UiPackageConstructor uiPackageConstructor;

    /**
     * 数据库表数据访问对象
     */
    @Autowired
    private IMonitorDbDao monitorDbDao;

    /**
     * <p>
     * 获取数据库列表
     * </p>
     *
     * @param current      当前页
     * @param size         每页显示条数
     * @param connName     数据库连接名
     * @param url          数据库URL
     * @param dbType       数据库类型
     * @param isOnline     数据库状态
     * @param monitorEnv   监控环境
     * @param monitorGroup 监控分组
     * @return 简单分页模型
     * @author 皮锋
     * @custom.date 2020/12/19 17:37
     */
    @Override
    public Page<MonitorDbVo> getMonitorDbList(Long current, Long size, String connName, String url, String dbType,
                                              String isOnline, String monitorEnv, String monitorGroup) {
        // 查询数据库
        IPage<MonitorDb> ipage = new Page<>(current, size);
        LambdaQueryWrapper<MonitorDb> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 只查询部分字段
        lambdaQueryWrapper.select(MonitorDb::getId, MonitorDb::getConnName, MonitorDb::getUrl,
                MonitorDb::getDbType, MonitorDb::getIsOnline, MonitorDb::getOfflineCount,
                MonitorDb::getDbDesc, MonitorDb::getInsertTime, MonitorDb::getUpdateTime,
                MonitorDb::getMonitorEnv, MonitorDb::getMonitorGroup);
        if (StringUtils.isNotBlank(connName)) {
            lambdaQueryWrapper.like(MonitorDb::getConnName, connName);
        }
        if (StringUtils.isNotBlank(url)) {
            lambdaQueryWrapper.like(MonitorDb::getUrl, url);
        }
        if (StringUtils.isNotBlank(dbType)) {
            lambdaQueryWrapper.like(MonitorDb::getDbType, dbType);
        }
        if (StringUtils.isNotBlank(isOnline)) {
            // -1 用来表示状态未知
            if (StringUtils.equals(isOnline, ZeroOrOneConstants.MINUS_ONE)) {
                // 状态为 null 或 空字符串
                lambdaQueryWrapper.and(wrapper -> wrapper.isNull(MonitorDb::getIsOnline).or().eq(MonitorDb::getIsOnline, ""));
            } else {
                lambdaQueryWrapper.eq(MonitorDb::getIsOnline, isOnline);
            }
        }
        if (StringUtils.isNotBlank(monitorEnv)) {
            lambdaQueryWrapper.eq(MonitorDb::getMonitorEnv, monitorEnv);
        }
        if (StringUtils.isNotBlank(monitorGroup)) {
            lambdaQueryWrapper.eq(MonitorDb::getMonitorGroup, monitorGroup);
        }
        IPage<MonitorDb> monitorDbPage = this.monitorDbDao.selectPage(ipage, lambdaQueryWrapper);
        List<MonitorDb> monitorDbs = monitorDbPage.getRecords();
        // 转换成数据库信息表现层对象
        List<MonitorDbVo> monitorDbVos = Lists.newLinkedList();
        for (MonitorDb monitorDb : monitorDbs) {
            MonitorDbVo monitorDbVo = MonitorDbVo.builder().build().convertFor(monitorDb);
            monitorDbVos.add(monitorDbVo);
        }
        // 设置返回对象
        Page<MonitorDbVo> monitorDbVoPage = new Page<>();
        monitorDbVoPage.setRecords(monitorDbVos);
        monitorDbVoPage.setTotal(monitorDbPage.getTotal());
        return monitorDbVoPage;
    }

    /**
     * <p>
     * 编辑数据库信息
     * </p>
     *
     * @param monitorDbVo 数据库信息
     * @return layUiAdmin响应对象：如果数据库中已经存在，LayUiAdminResultVo.data="exist"；
     * 如果编辑成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/12/19 19:56
     */
    @Override
    public LayUiAdminResultVo editMonitorDb(MonitorDbVo monitorDbVo) {
        // 去掉无用的空格
        monitorDbVo.setUrl(StringUtils.trim(monitorDbVo.getUrl()));
        // 根据url，查询数据库中是否已经存在此url的记录
        LambdaQueryWrapper<MonitorDb> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 去掉它自己这条记录
        lambdaQueryWrapper.ne(MonitorDb::getId, monitorDbVo.getId());
        lambdaQueryWrapper.eq(MonitorDb::getUrl, monitorDbVo.getUrl());
        Integer count = this.monitorDbDao.selectCount(lambdaQueryWrapper);
        // 已经存在
        if (count != null && count > 0) {
            return LayUiAdminResultVo.ok(WebResponseConstants.EXIST);
        }
        // 转成数据库表实体对象
        MonitorDb monitorDb = monitorDbVo.convertTo();
        // 数据库类型
        String dbType = monitorDb.getDbType();
        monitorDb.setDriverClass(DriverUtil.identifyDriver(dbType));
        // Redis数据库
        if (StringUtils.equalsIgnoreCase(DbEnums.Redis.name(), dbType)) {
            monitorDb.setDriverClass(DbDriverClassConstants.REDIS_DRIVER_CLASS);
        }
        // mongo数据库
        if (StringUtils.equalsIgnoreCase(DbEnums.Mongo.name(), dbType)) {
            monitorDb.setDriverClass(DbDriverClassConstants.MONGO_DRIVER_CLASS);
        }
        if (StringUtils.isBlank(monitorDb.getPassword())) {
            // mybatis-plus不会更新值为null字段
            monitorDb.setPassword(null);
        }
        if (StringUtils.isBlank(monitorDbVo.getMonitorEnv())) {
            monitorDb.setMonitorEnv(null);
        }
        if (StringUtils.isBlank(monitorDbVo.getMonitorGroup())) {
            monitorDb.setMonitorGroup(null);
        }
        monitorDb.setUpdateTime(new Date());
        // 更新
        int result = this.monitorDbDao.updateById(monitorDb);
        if (result == 1) {
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 添加数据库信息
     * </p>
     *
     * @param monitorDbVo 数据库信息
     * @return layUiAdmin响应对象：如果数据库中已经存在，LayUiAdminResultVo.data="exist"；
     * 如果添加成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/12/19 20:05
     */
    @Override
    public LayUiAdminResultVo addMonitorDb(MonitorDbVo monitorDbVo) {
        // 去掉无用的空格
        monitorDbVo.setUrl(StringUtils.trim(monitorDbVo.getUrl()));
        // 根据url，查询数据库中是否已经存在此url的记录
        LambdaQueryWrapper<MonitorDb> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(MonitorDb::getUrl, monitorDbVo.getUrl());
        Integer count = this.monitorDbDao.selectCount(lambdaQueryWrapper);
        // 已经存在
        if (count != null && count > 0) {
            return LayUiAdminResultVo.ok(WebResponseConstants.EXIST);
        }
        // 转成数据库表实体对象
        MonitorDb monitorDb = monitorDbVo.convertTo();
        // 数据库类型
        String dbType = monitorDb.getDbType();
        monitorDb.setDriverClass(DriverUtil.identifyDriver(dbType));
        // Redis数据库
        if (StringUtils.equalsIgnoreCase(DbEnums.Redis.name(), dbType)) {
            monitorDb.setDriverClass(DbDriverClassConstants.REDIS_DRIVER_CLASS);
        }
        // mongo数据库
        if (StringUtils.equalsIgnoreCase(DbEnums.Mongo.name(), dbType)) {
            monitorDb.setDriverClass(DbDriverClassConstants.MONGO_DRIVER_CLASS);
        }
        if (StringUtils.isBlank(monitorDbVo.getMonitorEnv())) {
            monitorDb.setMonitorEnv(null);
        }
        if (StringUtils.isBlank(monitorDbVo.getMonitorGroup())) {
            monitorDb.setMonitorGroup(null);
        }
        monitorDb.setOfflineCount(0);
        monitorDb.setInsertTime(new Date());
        int result = this.monitorDbDao.insert(monitorDb);
        if (result == 1) {
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 删除数据库信息
     * </p>
     *
     * @param monitorDbVos 数据库信息
     * @return layUiAdmin响应对象：如果删除成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/12/19 20:59
     */
    @Override
    public LayUiAdminResultVo deleteMonitorDb(List<MonitorDbVo> monitorDbVos) {
        List<Long> ids = Lists.newArrayList();
        for (MonitorDbVo monitorDbVo : monitorDbVos) {
            ids.add(monitorDbVo.getId());
        }
        this.monitorDbDao.deleteBatchIds(ids);
        return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
    }

    /**
     * <p>
     * 获取home页的数据库信息
     * </p>
     *
     * @return home页的数据库信息表现层对象
     * @author 皮锋
     * @custom.date 2020/12/19 21:43
     */
    @Override
    public HomeDbVo getHomeDbInfo() {
        // 数据库正常率统计
        Map<String, Object> map = this.monitorDbDao.getDbNormalRateStatistics();
        return HomeDbVo.builder()
                .dbSum(NumberUtil.parseInt(map.get("dbSum").toString()))
                .dbConnectSum(NumberUtil.parseInt(map.get("dbConnectSum").toString()))
                .dbDisconnectSum(NumberUtil.parseInt(map.get("dbDisconnectSum").toString()))
                .dbUnsentSum(NumberUtil.parseInt(map.get("dbUnsentSum").toString()))
                .dbConnectRate(NumberUtil.round(map.get("dbConnectRate").toString(), 2).toString())
                .build();
    }

    /**
     * <p>
     * 测试数据库连通性
     * </p>
     *
     * @param monitorDbVo 数据库信息
     * @return layUiAdmin响应对象：网络连通性
     * @throws SigarException Sigar异常
     * @throws IOException    IO异常
     * @author 皮锋
     * @custom.date 2022/10/9 10:16
     */
    @Override
    public LayUiAdminResultVo testMonitorDb(MonitorDbVo monitorDbVo) throws SigarException, IOException {
        // 封装请求数据
        JSONObject extraMsg = new JSONObject();
        extraMsg.put("url", StringUtils.trim(monitorDbVo.getUrl()));
        extraMsg.put("dbType", monitorDbVo.getDbType());
        extraMsg.put("username", monitorDbVo.getUsername());
        extraMsg.put("password", monitorDbVo.getPassword());
        BaseRequestPackage baseRequestPackage = this.uiPackageConstructor.structureBaseRequestPackage(extraMsg);
        // 从服务端获取数据
        String resultStr = Sender.send(UrlConstants.TEST_MONITOR_DB_URL, baseRequestPackage.toJsonString());
        BaseResponsePackage baseResponsePackage = JSON.parseObject(resultStr, BaseResponsePackage.class);
        Result result = baseResponsePackage.getResult();
        String msg = result.getMsg();
        boolean isConnected = Boolean.parseBoolean(msg);
        if (isConnected) {
            msg = WebResponseConstants.SUCCESS;
        } else {
            msg = WebResponseConstants.FAIL;
        }
        return LayUiAdminResultVo.ok(msg);
    }

}
