package com.bocloud.ims.service.scheduler.cloudalarm.azure;

import com.bocloud.cmp.driver.monitor.AlarmDriver;
import com.bocloud.cmp.model.AlarmModel;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.ListTool;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.azure.monitor.AzureAlarm;
import com.bocloud.ims.entity.resource.azure.monitor.MetricAlert;
import com.bocloud.ims.entity.resource.azure.monitor.MetricCriteria;
import com.bocloud.ims.entity.resource.azure.redis.CloudRedis;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.azure.monitor.AzureAlarmRepository;
import com.bocloud.ims.repository.resource.azure.monitor.MetricAlertRepository;
import com.bocloud.ims.repository.resource.azure.monitor.MetricCriteriaRepository;
import com.bocloud.ims.repository.resource.azure.redis.CloudRedisRepository;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
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.Component;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: AzureAlarmUtils
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2021/9/8 15:34
 */
@Component
@Slf4j
public class AzureAlarmUtils {

    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private AlarmDriver alarmDriver;
    @Autowired
    private AzureAlarmRepository alarmRepository;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private MetricAlertRepository metricAlertRepository;
    @Autowired
    private MetricCriteriaRepository metricCriteriaRepository;
    @Autowired
    private CloudRedisRepository cloudRedisRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private LockFactory lockFactory;

    /**
     * Azure 同步告警
     *
     * @param typeVendors
     */
    public void alarm(List<CloudVendor> typeVendors) {
        for (CloudVendor vendor : typeVendors) {
            taskExecutor.execute(() -> {
                log.info("开始获取:{}->平台警报", vendor.getName());
                String path = CloudVendor.class.getSimpleName() + "_alarm_" + vendor.getId();
                BocloudResult bocloudResult = alarmDriver.list(vendor.getUuid(), null);
                try (BocloudACLock lock = lockFactory.getACLock(path)) {
                    Assert.isTrue(lock.acquire(1, TimeUnit.HOURS), "请求超时");
                    List<AlarmModel> billModels = new ArrayList<>();
                    if (bocloudResult.isSuccess()) {
                        billModels = (List<AlarmModel>) bocloudResult.getData();
                    }
                    log.info("{}->平台警报数据量:{}", vendor.getName(), billModels.size());
                    Map<String, MetricAlert> alertMap = Optional
                            .ofNullable(metricAlertRepository.listByVendorId(vendor.getId()))
                            .map(entitys -> entitys.stream()
                                    .collect(Collectors.toMap(entity -> entity.getUuid().toLowerCase(),
                                            entity -> entity)))
                            .orElse(Maps.newHashMap());
                    syncAlarms(billModels, vendor, alertMap);
                    log.info("{}->平台警报同步完成！", vendor.getName());
                } catch (Exception e) {
                    log.error(vendor.getName() + ":平台警报获取失败：", e);
                }
            });
        }

    }

    public void syncAlarms(List<AlarmModel> alarmModels, CloudVendor vendor, Map<String, MetricAlert> alertMap) {
        if (ListTool.isEmpty(alarmModels)) {
            alarmRepository.removeByVid(vendor.getId());
            return;
        }
        Map<String, AzureAlarm> map = new HashMap<>();
        List<AzureAlarm> alarms = alarmRepository.listByVendorId(vendor.getId());
        for (AzureAlarm alarm : alarms) {
            map.put(alarm.getAlarmId().toLowerCase(), alarm);
        }
        for (AlarmModel model : alarmModels) {
            //获取资源类型
            String resourceType = model.getTarget().split("/")[6].toLowerCase();
            AzureAlarm entity = new AzureAlarm();
            if (map.containsKey(model.getAlarmId().toLowerCase())) {
                entity = map.get(model.getAlarmId().toLowerCase());
                BeanUtils.copyProperties(model, entity);
                this.getUser(entity, vendor, resourceType);
                //中文返回是编码后的
                String decode = URLDecoder.decode(model.getMetricAlertUuid());
                Optional.ofNullable(alertMap.get(decode.toLowerCase()))
                        .map(alert -> alert.getId()).ifPresent(entity::setMetricAlertId);
                alarmRepository.update(entity);
                if (null == entity.getMetricAlertId()) {
                    log.info("update警报规则UUID:{}", decode);
                }
            } else {
                BeanUtils.copyProperties(model, entity);
                entity.setVendorId(vendor.getId());
                entity.setCreatorId(vendor.getCreatorId());
                this.getUser(entity, vendor, resourceType);

                //中文返回是编码后的
                String decode = URLDecoder.decode(model.getMetricAlertUuid());
                Optional.ofNullable(alertMap.get(decode.toLowerCase()))
                        .map(alert -> alert.getId()).ifPresent(entity::setMetricAlertId);
                alarmRepository.save(entity);
                if (null == entity.getMetricAlertId()) {
                    log.info("save警报规则UUID:{}", decode);
                }
                this.sendMessage(entity, vendor, resourceType);
            }
            map.remove(model.getAlarmId().toLowerCase());
        }
        for (Map.Entry<String, AzureAlarm> entry : map.entrySet()) {
            alarmRepository.delete(entry.getValue().getId());
        }
    }

    //todo 默认都给管理员发
    public void getUser(AzureAlarm entity, CloudVendor vendor, String resourceType) {
        Long tenantId = null;
        Long projectId = null;
//        Long userId = null;
        switch (resourceType) {
            case "microsoft.compute":
                CloudServer cloudServer = cloudServerRepository.get(vendor.getId(), entity.getTarget());
                if (!ObjectUtils.isEmpty(cloudServer)) {
                    tenantId = cloudServer.getTenantId();
                    projectId = cloudServer.getProjectId();
                }
                break;
            case "microsoft.cache":
                CloudRedis cloudRedis = cloudRedisRepository.get(vendor.getId(), entity.getTarget());
                if (!ObjectUtils.isEmpty(cloudRedis)) {
                    tenantId = cloudRedis.getTenantId();
                    projectId = cloudRedis.getProjectId();
                }
                break;
            default:
                break;
        }
        entity.setTenantId(tenantId);
        entity.setProjectId(projectId);
//        entity.setUserId(userId);
    }

    public void sendMessage(AzureAlarm entity, CloudVendor vendor, String resourceType) {
        RequestContext context = new RequestContext();
        String msgContent = null;
        String emailContent = null;
        //azure指标警报
        MetricAlert metricAlert = metricAlertRepository.query(entity.getMetricAlertId());
        if (ObjectUtils.isEmpty(metricAlert)) {
            log.info("产生告警的指标警报不存在，请同步数据！");
            return;
        }
        switch (resourceType) {
            case "microsoft.compute":
                CloudServer cloudServer = cloudServerRepository.get(vendor.getId(), entity.getTarget());
                if (!ObjectUtils.isEmpty(cloudServer)) {
                    context.setTarget(cloudServer.getCreatorId());
                    context.setCatalog(RequestContext.Catalog.Manager);
                    context.setTenant(cloudServer.getTenantId());
                    context.setProject(cloudServer.getProjectId());
                    msgContent = "云主机:" + cloudServer.getName() + "触发警报请及时查看,警报规则:" + metricAlert.getName() + " ";
                    emailContent = "云主机:" + cloudServer.getName() + "触发警报请及时查看警报规则:" + metricAlert.getName() + " ";
                } else {
                    log.info("获取到新的告警，但是云主机找不到：{}" + entity.getTarget());
                }
                break;
            case "microsoft.cache":
                CloudRedis cloudRedis = cloudRedisRepository.get(vendor.getId(), entity.getTarget());
                if (!ObjectUtils.isEmpty(cloudRedis)) {
                    context.setTarget(cloudRedis.getCreatorId());
                    context.setCatalog(RequestContext.Catalog.Manager);
                    context.setTenant(cloudRedis.getTenantId());
                    context.setProject(cloudRedis.getProjectId());
                    msgContent = "Redis:" + cloudRedis.getName() + "触发警报请及时查看,警报规则:" + metricAlert.getName() + " ";
                    emailContent = "Redis:" + cloudRedis.getName() + "触发警报请及时查看警报规则:" + metricAlert.getName() + " ";
                } else {
                    log.info("获取到新的告警，但是Redis找不到：{}" + entity.getTarget());
                }
                break;
            default:
                break;
        }
        msgContent = msgContent + this.getMessage(metricAlert);
        emailContent = emailContent + this.getMessage(metricAlert);
        NotifyMessage message = new NotifyMessage(context, "触发警报", msgContent, emailContent,
                true, true);
        amqpTemplate.convertAndSend("event.notice.queue", message);
    }

    public String getMessage(MetricAlert metricAlert) {
        String criteria1Text = " ";
        List<MetricCriteria> metricCriteria = metricCriteriaRepository.listByMetricAlertId(metricAlert.getId());
        for (MetricCriteria metricCriteria1 : metricCriteria) {
            String str = new StringBuffer()
                    .append(" |条件：")
                    .append(metricCriteria1.getMetricName())
                    .append(" ")
                    .append(metricCriteria1.getOperator())
                    .append(" ")
                    .append(metricCriteria1.getTimeAggregation())
                    .append(" ")
                    .append(metricCriteria1.getThreshold())
                    .append(" ")
                    .toString();
            criteria1Text = criteria1Text + str;
        }
        return criteria1Text;
    }


}