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

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.ObjectRelation;
import com.bocloud.ims.entity.resource.azure.monitor.Alarm;
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.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ObjectRelationRepository;
import com.bocloud.ims.repository.resource.azure.monitor.AlarmRepository;
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.core.CurrentService;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Maps;
import org.apache.commons.collections.CollectionUtils;
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.annotation.Scheduled;
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: AzureAutoMateAlarm
 * @Description:同步告警
 * @author: zhenpingLi/李振平
 * @date: 2021/1/18 13:56
 */
@Component
public class AzureAutoMateAlarm {

    private static Logger logger = LoggerFactory.getLogger(AzureAutoMateAlarm.class);
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private AlarmDriver alarmDriver;
    @Autowired
    private AlarmRepository alarmRepository;
    @Autowired
    private ObjectRelationRepository objectRelationRepository;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private MetricAlertRepository metricAlertRepository;
    @Autowired
    private MetricCriteriaRepository metricCriteriaRepository;
    @Autowired
    private CloudRedisRepository cloudRedisRepository;

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private CurrentService service;


    @Scheduled(cron = "0 */30 * * * ?")
    protected void execute() throws Exception {
        if (service.isLeader()) {
            autoMateAlarmExecute();
        }
    }

    private void autoMateAlarmExecute() throws Exception {
        logger.info("开始执行同步警报信息......");
        List<String> types = Arrays.asList(new String[]{"AZURE"});
        List<CloudVendor> cloudVendors = cloudVendorRepository.listByTypes(types, null);
        for (CloudVendor vendor : cloudVendors) {
            taskExecutor.execute(() -> {
                logger.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();
                    }
                    logger.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);
                    logger.info("{}->平台警报同步完成！", vendor.getName());
                } catch (Exception e) {
                    logger.error(vendor.getName() + ":平台警报获取失败：", e);
                }
            });
        }
    }

    private void syncAlarms(List<AlarmModel> alarmModels, CloudVendor vendor, Map<String, MetricAlert> alertMap) throws Exception {
        if (ListTool.isEmpty(alarmModels)) {
            alarmRepository.removeByVid(vendor.getId());
            return;
        }
        Map<String, Alarm> map = new HashMap<>();
        List<Alarm> alarms = alarmRepository.listByVendorId(vendor.getId());
        for (Alarm alarm : alarms) {
            map.put(alarm.getAlarmId().toLowerCase(), alarm);
        }
        for (AlarmModel model : alarmModels) {
            //获取资源类型
            String resourceType = model.getTarget().split("/")[6].toLowerCase();
            Alarm entity = new Alarm();
            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()) {
                    logger.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()) {
                    logger.info("save警报规则UUID:{}", decode);
                }
                this.sendMessage(entity, vendor, resourceType);
            }
            map.remove(model.getAlarmId().toLowerCase());
        }
        for (Map.Entry<String, Alarm> entry : map.entrySet()) {
            alarmRepository.delete(entry.getValue().getId());
        }
    }

    private void getUser(Alarm entity, CloudVendor vendor, String resourceType) throws Exception {
        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();
                    ObjectRelation objectRelation = objectRelationRepository.get(cloudServer.getId(), "Computer");
                    if (!ObjectUtils.isEmpty(objectRelation)) {
                        if (RequestContext.Catalog.valueOf(objectRelation.getCatalog()) == RequestContext.Catalog.User) {
                            userId = objectRelation.getTarget();
                        }
                    }
                }
                break;
            case "microsoft.cache":
                CloudRedis cloudRedis = cloudRedisRepository.get(vendor.getId(), entity.getTarget());
                tenantId = cloudRedis.getTenantId();
                projectId = cloudRedis.getProjectId();
                userId = cloudRedis.getUserId();
                break;
            default:
                break;
        }
        entity.setTenantId(tenantId);
        entity.setProjectId(projectId);
        entity.setUserId(userId);
    }

    private void sendMessage(Alarm entity, CloudVendor vendor, String resourceType) throws Exception {
        RequestContext context = new RequestContext();
        String msgContent = null;
        String emailContent = null;
        //azure指标警报
        MetricAlert metricAlert = metricAlertRepository.query(entity.getMetricAlertId());
        switch (resourceType) {
            case "microsoft.compute":
                CloudServer cloudServer = cloudServerRepository.get(vendor.getId(), entity.getTarget());
                //对于新增的告警信息进行站内信推送
                if (!ObjectUtils.isEmpty(cloudServer)) {
                    ObjectRelation objectRelation = objectRelationRepository.get(cloudServer.getId(), "Computer");
                    if (!ObjectUtils.isEmpty(objectRelation) && !ObjectUtils.isEmpty(metricAlert)) {
                        context.setTarget(objectRelation.getTarget());
                        context.setCatalog(RequestContext.Catalog.valueOf(objectRelation.getCatalog()));
                        context.setTenant(cloudServer.getTenantId());
                        context.setProject(cloudServer.getProjectId());
                        msgContent = "虚拟机:" + cloudServer.getName() + "触发警报请及时查看,警报规则:" + metricAlert.getName() + " ";
                        emailContent = "虚拟机:" + cloudServer.getName() + "触发警报请及时查看警报规则:" + metricAlert.getName() + " ";
                        this.getMessage(metricAlert, msgContent, emailContent);
                    } else if (!ObjectUtils.isEmpty(objectRelation) && ObjectUtils.isEmpty(metricAlert)) {
                        context.setTarget(objectRelation.getTarget());
                        context.setCatalog(RequestContext.Catalog.valueOf(objectRelation.getCatalog()));
                        context.setTenant(cloudServer.getTenantId());
                        context.setProject(cloudServer.getProjectId());
                        msgContent = "虚拟机:" + cloudServer.getName() + "触发告警请及时查看";
                        emailContent = "虚拟机:" + cloudServer.getName() + "触发告警请及时查看";
                    }
                } else {
                    logger.info("告警资源不存在：{}" + entity.getTarget());
                }
                break;
            case "microsoft.cache":
                CloudRedis cloudRedis = cloudRedisRepository.get(vendor.getId(), entity.getTarget());
                if (!ObjectUtils.isEmpty(cloudRedis)) {
                    if (!ObjectUtils.isEmpty(cloudRedis.getUserId())) {
                        context.setTarget(cloudRedis.getUserId());
                        context.setCatalog(RequestContext.Catalog.User);
                    } else {
                        context.setTarget(cloudRedis.getTenantId());
                        context.setCatalog(RequestContext.Catalog.Tenant);
                    }
                    context.setTenant(cloudRedis.getTenantId());
                    context.setProject(cloudRedis.getProjectId());
                    if (!ObjectUtils.isEmpty(metricAlert)) {
                        msgContent = "Redis:" + cloudRedis.getName() + "触发警报请及时查看,警报规则:" + metricAlert.getName() + " ";
                        emailContent = "Redis:" + cloudRedis.getName() + "触发警报请及时查看警报规则:" + metricAlert.getName() + " ";
                        this.getMessage(metricAlert, msgContent, emailContent);
                    } else {
                        msgContent = "Redis:" + cloudRedis.getName() + "触发告警请及时查看";
                        emailContent = "Redis:" + cloudRedis.getName() + "触发告警请及时查看";
                    }
                } else {
                    logger.info("告警资源不存在：{}" + entity.getTarget());
                }
                break;
            default:
                break;
        }
        NotifyMessage message = new NotifyMessage(context, "触发警报", msgContent, emailContent,
                true, true);
        amqpTemplate.convertAndSend("event.notice.queue", message);
    }

    private void getMessage(MetricAlert metricAlert, String msgContent, String emailContent) {
        List<MetricCriteria> metricCriteria = metricCriteriaRepository.listByMetricAlertId(metricAlert.getId());
        if (!CollectionUtils.isEmpty(metricCriteria)) {
            String criteria1Text = " ";
            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;
            }
            msgContent = msgContent + criteria1Text;
            emailContent = emailContent + criteria1Text;
        }
    }


}