package com.bocloud.ims.service.resource.azure.db;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.azure.db.DbVnetRuleDriver;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.azure.db.DbFirewallRuleModel;
import com.bocloud.cmp.model.azure.db.DbVnetRuleModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Subnet;
import com.bocloud.ims.entity.resource.Vpc;
import com.bocloud.ims.entity.resource.azure.db.AzureDatabaseServer;
import com.bocloud.ims.entity.resource.azure.db.DbVnetRule;
import com.bocloud.ims.interfaces.resource.azure.db.DbVnetRuleService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.SubnetRepository;
import com.bocloud.ims.repository.resource.VpcRepository;
import com.bocloud.ims.repository.resource.azure.db.CloudDatabaseServerRepository;
import com.bocloud.ims.repository.resource.azure.db.DbVnetRuleRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: DbVnetRuleServiceImpl
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2021/5/20 17:09
 */
@Service
public class DbVnetRuleServiceImpl implements DbVnetRuleService {

    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;

    private static Logger logger = LoggerFactory.getLogger(DbVnetRuleServiceImpl.class);

    @Autowired
    private DbVnetRuleRepository dbVnetRuleRepository;
    @Autowired
    private CloudDatabaseServerRepository cloudDatabaseRepository;
    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private DbVnetRuleDriver dbVnetRuleDriver;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private SubnetRepository subnetRepository;
    @Autowired
    private VpcRepository vpcRepository;

    @Override
    public BocloudResult<GridBean<DbVnetRule>> list(Pager pager, RequestContext context) {
        List<Param> params = Optional.ofNullable(pager.getParams()).orElse(new ArrayList<>());
        Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
        Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
        Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
        Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
        try {
            int total = dbVnetRuleRepository.count(params);
            List<DbVnetRule> list = this.dbVnetRuleRepository.list(page, rows, params, sorter);
            for (DbVnetRule dbVnetRule : list) {
                Subnet subnet = subnetRepository.query(dbVnetRule.getSubnetId());
                if (null != subnet) {
                    dbVnetRule.setSubnetCidr(subnet.getCidr());
                    dbVnetRule.setSubnetName(subnet.getName());
                }
                Vpc vpc = vpcRepository.query(dbVnetRule.getVpcId());
                if (null != vpc) {
                    dbVnetRule.setVpcName(vpc.getName());
                }
            }
            GridBean<DbVnetRule> gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list AzureDatabase error!", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> create(DbVnetRule entityParams, RequestContext context) {
        try {
            AzureDatabaseServer database = cloudDatabaseRepository.query(entityParams.getDatabaseServerId());
            Assert.notNull(database, "规则所属服务器不存在");
            CloudVendor vendor = vendorRepository.query(database.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            Subnet subnet = subnetRepository.query(entityParams.getSubnetId());
            Assert.notNull(subnet, "子网不存在");
            Vpc vpc = vpcRepository.query(subnet.getNetworkId());
            Assert.notNull(vpc, "VPC不存在");

            if (null != dbVnetRuleRepository.getByName(entityParams.getDatabaseServerId(), entityParams.getName())) {
                return new BocloudResult(false, "名称已存在");
            }

            DbVnetRuleModel model = new DbVnetRuleModel();
            BeanUtils.copyProperties(entityParams, model);
            model.setResourceGroupName(database.getResourceGroupName());
            model.setDatabaseServerName(database.getName());
            model.setDatabaseServerUuid(database.getDatabaseServerUuid());
            model.setSubnetUuid(subnet.getSubnetUuid());
            model.setSubnetName(subnet.getName());
            model.setVpcUuid(vpc.getVpcId());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".databasevnetrule";
                String message = "创建Vnet规则失败";
                BocloudResult result = dbVnetRuleDriver.create(vendor.getUuid(), database.getRegionId(), model);
                try {
                    if (result.isSuccess()) {
                        message = "尊敬的客户，您好！您的MySQL Vnet规则【" + entityParams.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建成功，请登录控制台查看及管理。";
                        DbFirewallRuleModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), DbFirewallRuleModel.class);
                        BeanUtils.copyProperties(newModel, entityParams);
                        entityParams.setVpcId(subnet.getVpcId());
                        dbVnetRuleRepository.save(entityParams);
                    } else {
                        logger.error("创建Vnet规则失败：" + result.getMessage());
                        message = "尊敬的客户，您好！您的MySQL Vnet规则【" + entityParams.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建失败，失败信息：" + result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                    //站内信推送
                    NotifyMessage notifyMessage = new NotifyMessage(context, "MySQL防火墙:", message, null,
                            true, false);
                    amqpTemplate.convertAndSend("event.notice.queue", notifyMessage);
                } catch (Exception e) {
                    logger.error("create DatabaseVnetRule fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap(
                            "vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("create DatabaseVnetRule error!", e);
            return new BocloudResult(false, "创建失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {
        String path = AzureDatabaseServer.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DbVnetRule entity = dbVnetRuleRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            AzureDatabaseServer database = cloudDatabaseRepository.query(entity.getDatabaseServerId());
            Assert.notNull(database, "规则所属服务器不存在");
            CloudVendor vendor = vendorRepository.query(database.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            DbVnetRuleModel model = new DbVnetRuleModel();
            model.setResourceGroupName(database.getResourceGroupName());
            model.setDatabaseServerName(database.getName());
            model.setName(entity.getName());
            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".databasevnetrule";
                String message = "删除Vnet规则失败";
                BocloudResult result = dbVnetRuleDriver.remove(vendor.getUuid(), database.getRegionId(), model);
                try {
                    if (result.isSuccess()) {
                        message = "删除Vnet规则成功";
                        dbVnetRuleRepository.delete(id);
                    } else {
                        logger.error("删除Vnet规则失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    logger.error("remove DatabaseVnetRule fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("remove DatabaseVnetRule error!", e);
            return new BocloudResult(false, "删除失败:" + e.getMessage());
        }
    }
}