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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.monitor.MetricAlertDriver;
import com.bocloud.cmp.model.azure.monitor.MetricAlertModel;
import com.bocloud.cmp.model.azure.monitor.MetricCriteriaModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.IDFactory;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.common.Project;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.ResourceGroup;
import com.bocloud.ims.entity.resource.azure.db.AzureDatabaseServer;
import com.bocloud.ims.entity.resource.azure.monitor.MetricAlert;
import com.bocloud.ims.entity.resource.azure.monitor.MetricAlertResourceRelation;
import com.bocloud.ims.entity.resource.azure.monitor.MetricCriteria;
import com.bocloud.ims.entity.resource.azure.redis.CloudRedis;
import com.bocloud.ims.interfaces.resource.azure.monitor.MetricAlertService;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ResourceGroupRepository;
import com.bocloud.ims.repository.resource.azure.db.CloudDatabaseServerRepository;
import com.bocloud.ims.repository.resource.azure.monitor.MetricAlertRepository;
import com.bocloud.ims.repository.resource.azure.monitor.MetricAlertResourceRelationRepository;
import com.bocloud.ims.repository.resource.azure.monitor.MetricCriteriaRepository;
import com.bocloud.ims.repository.resource.azure.redis.CloudRedisRepository;
import com.bocloud.ims.service.internal.SmsProjectService;
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.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 org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

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

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: MetricAlertServiceImpl
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2021/1/8 14:10
 */
@Service("metricAlertService")
public class MetricAlertServiceImpl implements MetricAlertService {

    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;
    private static Logger logger = LoggerFactory.getLogger(MetricAlertServiceImpl.class);
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ResourceGroupRepository resourceGroupRepository;
    @Autowired
    private MetricAlertRepository metricAlertRepository;
    @Autowired
    private MetricCriteriaRepository metricCriteriaRepository;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private MetricAlertDriver metricAlertDriver;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private MetricAlertResourceRelationRepository metricAlertResourceRelationRepository;
    @Autowired
    private CloudRedisRepository cloudRedisRepository;
    @Autowired
    private CloudDatabaseServerRepository cloudDatabaseServerRepository;

    @Override
    public BocloudResult<GridBean<MetricAlert>> 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());
        sorter.put("gmtCreate", Common.ONE);
        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 {
            List<MetricAlert> list;
            int total;
            GridBean<MetricAlert> gridBean;
            Long tenantId = context.getTenant();
            Long projectId = context.getProject();
            // 为租户下的用户
            if (null != tenantId && 0L != tenantId) {
                List<Project> projects = smsProjectService.projectsByCondition(context.getTarget(),
                        context.getCatalog());
                List<Long> projectIds = new ArrayList<>();
                // 此时查询的是全部用户全部项目的信息
                if (null != projectId && 0L != projectId) {
                    projectIds.add(projectId);
                } else if (!ListTool.isEmpty(projects)) {
                    projects.forEach(project -> projectIds.add(project.getId()));
                } else {
                    projectIds.add(-1L);
                }
                total = this.metricAlertRepository.count(params, null, projectIds, null);
                list = this.metricAlertRepository.list(page, rows, params, sorter, null, projectIds, null);
            } else {
                total = metricAlertRepository.count(params);
                list = metricAlertRepository.list(page, rows, params, sorter);
            }
            for (MetricAlert metricAlert : list) {
                //插入指标规则
                List<MetricCriteria> metricCriteria = metricCriteriaRepository.listByMetricAlertId(metricAlert.getId());
                if (!CollectionUtils.isEmpty(metricCriteria)) {
                    metricAlert.setMetricCriterias(metricCriteria);
                }

                List<Object> resources = this.getResources(metricAlert);
                metricAlert.setResources(resources);
            }
            gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list metricAlert failure:", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }

    }

    @Override
    public BocloudResult<MetricAlert> detail(Long id, RequestContext context) {
        try {
            MetricAlert entity = metricAlertRepository.query(id);
            //插入指标规则
            List<MetricCriteria> metricCriteria = metricCriteriaRepository.listByMetricAlertId(entity.getId());
            if (!CollectionUtils.isEmpty(metricCriteria)) {
                entity.setMetricCriterias(metricCriteria);
            }
//            //插入云主机
//            List<CloudServer> cloudServers = new ArrayList<>();
//            List<MetricAlertVirtualMachine> metricAlertVirtualMachines = metricAlertVirtualMachineRepository.listByMetricAlertId(entity.getId());
//            for (MetricAlertVirtualMachine metricAlertVirtualMachine : metricAlertVirtualMachines) {
//                CloudServer cloudServer = cloudServerRepository.query(metricAlertVirtualMachine.getVirtualMachineId());
//                if (!ObjectUtils.isEmpty(cloudServer)) {
//                    cloudServers.add(cloudServer);
//                }
//            }
            //            entity.setCloudServers(cloudServers);
            List<Object> resources = this.getResources(entity);
            entity.setResources(resources);
            return new BocloudResult<>(true, entity, "查询详情成功");
        } catch (Exception e) {
            logger.error("query metricAlert error!", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {
        String path = MetricAlert.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            MetricAlert entity = metricAlertRepository.query(id);
            Assert.notNull(entity, "资源不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            ResourceGroup resourceGroup = resourceGroupRepository.query(entity.getResourceGroupId());
            Assert.notNull(resourceGroup, "资源组不存在");
            MetricAlertModel model = new MetricAlertModel();
            model.setResourceGroupName(resourceGroup.getName());
            model.setName(entity.getName());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".metricalert";
                String message = "删除监控指标失败";
                BocloudResult result = metricAlertDriver.remove(vendor.getUuid(), entity.getRegionId(), model);
                try {
                    if (result.isSuccess()) {
                        message = "删除监控指标成功";
                        //删除监控指标规则
                        List<MetricCriteria> metricCriterias = metricCriteriaRepository.listByMetricAlertId(id);
                        for (MetricCriteria metricCriteria : metricCriterias) {
                            metricCriteriaRepository.remove(metricCriteria.getId());
                        }
                        //删除监控指标和资源绑定关系
                        metricAlertResourceRelationRepository.removeByMetricAlertId(id);
                        //删除监控指标
                        metricAlertRepository.remove(id, context.getTarget());
                        logger.info("监控规则删除成功！");
                    } else {
                        logger.error("删除监控指标失败：" + 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 metricAlert 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 metricAlert error!", e);
            return new BocloudResult<>(false, "删除失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult multipleQueries(String condition, RequestContext context) {
        JSONObject object = JSONObject.parseObject(condition);
        String action = object.getString("condition");
        switch (action) {
            case "byVM":
                Long vmId = object.getLong("vmId");
                return this.listByVM(vmId, context);
            default:
                break;
        }
        return new BocloudResult(false, "Unknown Operation！");
    }

    @Override
    public BocloudResult<Void> create(MetricAlert entityParams, RequestContext context) {
        logger.info("开始执行创建：{}", entityParams);
        try {
            CloudVendor vendor = vendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            if (null != metricAlertRepository.getByName(entityParams.getVendorId(), entityParams.getResourceGroupId(), entityParams.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            ResourceGroup resourceGroup = resourceGroupRepository.query(entityParams.getResourceGroupId());
            Assert.notNull(resourceGroup, "资源组不存在");
            MetricAlertModel model = new MetricAlertModel();
            BeanUtils.copyProperties(entityParams, model);
            model.setResourceGroupName(resourceGroup.getName());
            //设置资源id集合
            List<String> resourceUuids = this.getResourceUuids(entityParams);
            model.setResourceUuids(resourceUuids);
            //设置规则集合
            List<MetricCriteriaModel> metricCriteriaModels = new ArrayList<>();
            for (MetricCriteria metricCriteria : entityParams.getMetricCriterias()) {
                MetricCriteriaModel metricCriteriaModel = new MetricCriteriaModel();
                BeanUtils.copyProperties(metricCriteria, metricCriteriaModel);
                metricCriteriaModels.add(metricCriteriaModel);
            }
            model.setMetricCriteriaModels(metricCriteriaModels);

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".metric";
                String message;
                try {
                    BocloudResult result = metricAlertDriver.create(vendor.getUuid(), entityParams.getRegionId(), model);
                    if (result.isSuccess()) {
                        message = "创建指标监控成功";
                        MetricAlertModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), MetricAlertModel.class);
                        BeanUtils.copyProperties(newModel, entityParams);
                        entityParams.setCreatorId(context.getTarget());
                        entityParams.setTenantId(context.getTenant());
                        entityParams.setProjectId(context.getProject());
                        entityParams.setGmtCreate(new Date());
                        if (context.getCatalog() == RequestContext.Catalog.User) {
                            entityParams.setUserId(context.getTarget());
                        }
                        metricAlertRepository.save(entityParams);
                        //插入规则
                        List<MetricCriteriaModel> newCriteriaModels = newModel.getMetricCriteriaModels();
                        for (MetricCriteriaModel metricCriteriaModel : newCriteriaModels) {
                            MetricCriteria metricCriteria = new MetricCriteria();
                            BeanUtils.copyProperties(metricCriteriaModel, metricCriteria);
                            metricCriteria.setMetricAlertId(entityParams.getId());
                            metricCriteriaRepository.save(metricCriteria);
                        }
                        //插入资源关系
                        for (Long resourceId : entityParams.getResourceIds()) {
                            metricAlertResourceRelationRepository.save(new MetricAlertResourceRelation(entityParams.getId(), resourceId, entityParams.getTargetResourceType()));
                        }
                    } else {
                        logger.error("创建指标监控失败：" + 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("创建指标监控失败：", e);
                    resourceEventPublisher.send(new OperateResult(false, "创建指标监控失败:" + e.getMessage(), operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("create  mtricAlert error!", e);
            return new BocloudResult<>(false, "创建失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> modify(MetricAlert entityParams, RequestContext context) {
        try {
            MetricAlert metricAlert = metricAlertRepository.query(entityParams.getId());
            Assert.notNull(metricAlert, "资源不存在");
            CloudVendor vendor = vendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            ResourceGroup resourceGroup = resourceGroupRepository.query(entityParams.getResourceGroupId());
            Assert.notNull(resourceGroup, "资源组不存在");

            MetricAlertModel model = new MetricAlertModel();
            BeanUtils.copyProperties(entityParams, model);
            //设置资源组
            model.setResourceGroupName(resourceGroup.getName());
            //设置资源id集合
            List<String> resourceUuids = this.getResourceUuids(entityParams);
            model.setResourceUuids(resourceUuids);
            //设置规则集合
            List<MetricCriteriaModel> metricCriteriaModels = new ArrayList<>();
            for (MetricCriteria metricCriteria : entityParams.getMetricCriterias()) {
                MetricCriteriaModel metricCriteriaModel = new MetricCriteriaModel();
                BeanUtils.copyProperties(metricCriteria, metricCriteriaModel);
                metricCriteriaModels.add(metricCriteriaModel);
            }
            model.setMetricCriteriaModels(metricCriteriaModels);

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".metric";
                String message;
                BocloudResult result = metricAlertDriver.modify(vendor.getUuid(), entityParams.getRegionId(), model);
                if (result.isSuccess()) {
                    message = "修改指标监控成功";
                    MetricAlertModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), MetricAlertModel.class);
                    BeanUtils.copyProperties(newModel, metricAlert);
                    metricAlert.setGmtCreate(new Date());
                    metricAlert.setMenderId(context.getTarget());
                    metricAlertRepository.update(metricAlert);
                    //删除所有规则
                    metricCriteriaRepository.removeByMetricAlertId(metricAlert.getId());
                    //插入规则
                    List<MetricCriteriaModel> newCriteriaModels = newModel.getMetricCriteriaModels();
                    for (MetricCriteriaModel metricCriteriaModel : newCriteriaModels) {
                        MetricCriteria metricCriteria = new MetricCriteria();
                        BeanUtils.copyProperties(metricCriteriaModel, metricCriteria);
                        metricCriteria.setMetricAlertId(entityParams.getId());
                        metricCriteriaRepository.save(metricCriteria);
                    }
                    //删除云主机关系
                    metricAlertResourceRelationRepository.removeByMetricAlertId(metricAlert.getId());
                    //插入和云主机关系
                    for (Long resourceId : entityParams.getResourceIds()) {
                        metricAlertResourceRelationRepository.save(new MetricAlertResourceRelation(entityParams.getId(), resourceId, entityParams.getTargetResourceType()));
                    }
                } else {
                    logger.error("修改指标监控失败：" + result.getMessage());
                    message = result.getMessage();
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("modify  mtricAlert error!", e);
            return new BocloudResult<>(false, "修改失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> createBatch(MetricAlert entityParams, RequestContext context) {
        try {
            List<Long> resourceIds = entityParams.getResourceIds();
            for (Long resourceId : resourceIds) {
                String newName = entityParams.getName() + "-" + IDFactory.instance().mac();
                List<Long> newResourceIds = new ArrayList<>();
                newResourceIds.add(resourceId);
                MetricAlert newMetricAlert = new MetricAlert();
                BeanUtils.copyProperties(entityParams, newMetricAlert);
                newMetricAlert.setName(newName);
                newMetricAlert.setResourceIds(newResourceIds);
                this.create(newMetricAlert, context);
            }
            return new BocloudResult<>(true, "任务正在执行...");
        } catch (Exception e) {
            logger.error("createBatch  mtricAlert error!", e);
            return new BocloudResult<>(false, "创建失败:" + e.getMessage());
        }
    }

    public BocloudResult<List<MetricAlert>> listByVM(Long vmId, RequestContext context) {
        try {
            CloudServer cloudServer = cloudServerRepository.query(vmId);
            Assert.notNull(cloudServer, "云主机不存在");
            CloudVendor vendor = vendorRepository.query(cloudServer.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            List<MetricAlert> metricAlerts = metricAlertRepository.listByVmId(vendor.getId(), cloudServer.getId());
            return new BocloudResult<>(true, metricAlerts, "查询成功");
        } catch (Exception e) {
            logger.error("list metricAlert failure:", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    private List<Object> getResources(MetricAlert entity) {
        List<Object> objects = new ArrayList<>();
        List<MetricAlertResourceRelation> resourceRelations = metricAlertResourceRelationRepository.listByMetricAlertId(entity.getId());
        for (MetricAlertResourceRelation relation : resourceRelations) {
            switch (relation.getResourceType()) {
                case "Microsoft.Compute/virtualMachines":
                    CloudServer cloudServer = cloudServerRepository.query(relation.getResourceId());
                    if (!ObjectUtils.isEmpty(cloudServer)) {
                        objects.add(cloudServer);
                    }
                    break;
                case "Microsoft.DBForMySQL/servers":
                    AzureDatabaseServer databaseServer = cloudDatabaseServerRepository.query(relation.getResourceId());
                    if (!ObjectUtils.isEmpty(databaseServer)) {
                        objects.add(databaseServer);
                    }
                    break;
                case "Microsoft.Cache/Redis":
                    CloudRedis cloudRedis = cloudRedisRepository.query(relation.getResourceId());
                    if (!ObjectUtils.isEmpty(cloudRedis)) {
                        objects.add(cloudRedis);
                    }
                    break;
                default:
//                    CloudServer serverDefault = cloudServerRepository.query(relation.getResourceId());
//                    if (!ObjectUtils.isEmpty(serverDefault)) {
//                        objects.add(serverDefault);
//                    }
                    break;

            }

        }
        return objects;

    }

    private List<String> getResourceUuids(MetricAlert entityParams) {
        List<String> resourceUuids = new ArrayList<>();
        for (Long resourceId : entityParams.getResourceIds()) {
            switch (entityParams.getTargetResourceType()) {
                case "Microsoft.Compute/virtualMachines":
                    CloudServer server = cloudServerRepository.query(resourceId);
                    if (!ObjectUtils.isEmpty(server)) {
                        resourceUuids.add(server.getInstanceId());
                    }
                    break;
                case "Microsoft.DBForMySQL/servers":
                    AzureDatabaseServer databaseServer = cloudDatabaseServerRepository.query(resourceId);
                    if (!ObjectUtils.isEmpty(databaseServer)) {
                        resourceUuids.add(databaseServer.getDatabaseServerUuid());
                    }
                    break;
                case "Microsoft.Cache/Redis":
                    CloudRedis cloudRedis = cloudRedisRepository.query(resourceId);
                    if (!ObjectUtils.isEmpty(cloudRedis)) {
                        resourceUuids.add(cloudRedis.getRedisUuid());
                    }
                    break;
                default:
//                    CloudServer serverDefault = cloudServerRepository.query(resourceId);
//                    if (!ObjectUtils.isEmpty(serverDefault)) {
//                        resourceUuids.add(serverDefault.getInstanceId());
//                    }
                    break;
            }
        }
        return resourceUuids;
    }


}