package com.bocloud.ims.service.resource.fusionsphere;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.ClusterDriver;
import com.bocloud.cmp.driver.monitor.MonitorDriver;
import com.bocloud.cmp.model.ClusterModel;
import com.bocloud.cmp.model.fusionsphere.FusionSphereMonitorRealTimeModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Cluster;
import com.bocloud.ims.entity.resource.Vdc;
import com.bocloud.ims.interfaces.resource.fusionsphere.FusionSphereClusterService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ClusterRepository;
import com.bocloud.ims.repository.resource.VdcRepository;
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 lombok.extern.slf4j.Slf4j;
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.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: FusionSphereClusterServiceImpl
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2021/10/20 10:19
 */
@Slf4j
@Service
public class FusionSphereClusterServiceImpl implements FusionSphereClusterService {

    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private ClusterRepository clusterRepository;
    @Autowired
    private VdcRepository vdcRepository;
    @Autowired
    private ClusterDriver clusterDriver;
    @Autowired
    private MonitorDriver monitorDriver;

    @Override
    public BocloudResult<GridBean<Cluster>> 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());
        Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
        Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
        try {
            int total = clusterRepository.count(params);
            List<Cluster> list = this.clusterRepository.list(page, rows, params, sorter);
            for (Cluster cluster : list) {
                CloudVendor vendor = vendorRepository.query(cluster.getVendorId());
                if (ObjectUtils.isEmpty(vendor)) {
                    log.error("get cloudVendor failure");
                    continue;
                }
                // 站点
                Vdc vdc = vdcRepository.query(cluster.getVdcId());
                if (ObjectUtils.isEmpty(vdc)) {
                    log.error("get vdc failure");
                    continue;
                }
                FusionSphereMonitorRealTimeModel monitorModel2 = new FusionSphereMonitorRealTimeModel();
                monitorModel2.setSiteUri(vdc.getUri());
                monitorModel2.setUrn(cluster.getClusterVal());
                List<String> metricIds = new ArrayList<>();
                metricIds.add("cpu_usage");
                metricIds.add("mem_usage");
                monitorModel2.setMetricId(metricIds);
                BocloudResult result = monitorDriver.metrics(vendor.getUuid(), monitorModel2, vdc.getUri());
                if (result.isFailed()) {
                    continue;
                }
                String rs = (String) result.getData();
                JSONObject resultjsonobject = JSONObject.parseObject(rs);
                JSONArray items = resultjsonobject.getJSONArray("items");
                JSONObject item = items.getJSONObject(0);
                JSONArray values = item.getJSONArray("value");
                for (int i = 0; i < values.size(); i++) {
                    JSONObject jsonObject = (JSONObject) values.get(i);
                    if ("cpu_usage".equals(jsonObject.getString("metricId"))) {
                        cluster.setCpuUsage(jsonObject.getString("metricValue"));
                    }
                    if ("mem_usage".equals(jsonObject.getString("metricId"))) {
                        cluster.setMemUsage(jsonObject.getString("metricValue"));
                    }
                }

            }

            GridBean<Cluster> gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            log.error("list Cluster error!", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Cluster> detail(Long id, RequestContext context) {
        try {
            Cluster entity = clusterRepository.query(id);
            Assert.notNull(entity, "资源不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            BocloudResult result = clusterDriver.resourceStatistics(vendor.getUuid(), entity.getUri());
            if (result.isSuccess()) {
                JSONObject object = JSONObject.parseObject((String)result.getData());
                Optional.ofNullable(object).ifPresent(obj -> {
                    {
                        Optional.ofNullable(obj.getJSONObject("cpuResource")).ifPresent(cpuResource -> {
                            entity.setCpuUsed(cpuResource.getLong("allocatedSizeMHz"));
                            entity.setCpuTotal(cpuResource.getLong("totalSizeMHz"));
                        });
                        Optional.ofNullable(obj.getJSONObject("memResource")).ifPresent(memResource -> {
                            Optional.ofNullable(memResource.getBigDecimal("totalSizeMB"))
                                    .map(totalSizeMB -> totalSizeMB.divide(new BigDecimal("1024"),2,BigDecimal.ROUND_HALF_UP).doubleValue())
                                    .ifPresent(entity::setMemTotal);
                            Optional.ofNullable(memResource.getBigDecimal("allocatedSizeMB"))
                                    .map(allocatedSizeMB -> allocatedSizeMB.divide(new BigDecimal("1024"),2,BigDecimal.ROUND_HALF_UP).doubleValue())
                                    .ifPresent(entity::setMemUsed);
                        });
                    }
                });
            }
            return new BocloudResult<>(true, entity, "查询详情成功");
        } catch (Exception e) {
            log.error("detail Cluster fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> create(Cluster entityParams, RequestContext context) {
        try {
            CloudVendor vendor = vendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            Vdc vdc = vdcRepository.query(entityParams.getVdcId());
            Assert.notNull(vdc, "站点不存在");
            if (null != clusterRepository.getByName(entityParams.getName(), entityParams.getVdcId())) {
                throw new RuntimeException("资源已存在");
            }

            ClusterModel model = new ClusterModel();
            model.setName(entityParams.getName());
            model.setArch(entityParams.getArch());
            model.setResStrategy(entityParams.getResStrategy());
            model.setRemark(entityParams.getRemark());

            model.setVdcUri(vdc.getUri());
            model.setParentObjUrn(vdc.getVdcVal());
            model.setIsEnableHa(entityParams.getIsEnableHa());
            model.setIsEnableDrs(entityParams.getIsEnableDrs());
            model.setIsEnableImc(entityParams.getIsEnableImc());
            model.setIsMemOvercommit(entityParams.getIsMemOvercommit());
            model.setIsAutoAdjustNuma(entityParams.getIsAutoAdjustNuma());
            model.setEnableGuestNuma(entityParams.getEnableGuestNuma());
            model.setEnableIoTailor(entityParams.getEnableIOTailor());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".cluster";
                String message = "创建资源失败";
                BocloudResult result = clusterDriver.create(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        message = "创建资源成功";
                        ClusterModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ClusterModel.class);
                        BeanUtils.copyProperties(newModel, entityParams);
                        clusterRepository.save(entityParams);
                    } else {
                        log.error("创建资源失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    log.error("create resource fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap(
                            "vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("create resource error!", e);
            return new BocloudResult<>(false, "创建失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> modify(Cluster modifyParams, RequestContext context) {
        try {
            Cluster entity = clusterRepository.query(modifyParams.getId());
            Assert.notNull(entity, "数据不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");

            ClusterModel model = new ClusterModel();
            model.setUri(entity.getUri());
            model.setName(modifyParams.getName());
            model.setArch(modifyParams.getArch());
            model.setRemark(modifyParams.getRemark());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".cluster";
                String message = "更新cluster失败";
                BocloudResult result = clusterDriver.modify(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        entity.setName(modifyParams.getName());
                        entity.setArch(modifyParams.getArch());
                        entity.setRemark(modifyParams.getRemark());
                        clusterRepository.update(entity);
                        message = "更新cluster成功";
                    } else {
                        log.error("更新cluster失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    log.error("modify cluster fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("modify cluster error!", e);
            return new BocloudResult<>(false, "更新失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {
        String path = Cluster.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Cluster entity = clusterRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".cluster";
                String message = "删除Cluster失败";
                BocloudResult result = clusterDriver.remove(vendor.getUuid(), entity.getUri());
                try {
                    if (result.isSuccess()) {
                        message = "删除Cluster成功";
                        clusterRepository.remove(id, context.getTarget());
                    } else {
                        log.error("删除Cluster失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    log.error("remove Cluster fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("remove Cluster error!", e);
            return new BocloudResult<>(false, "删除失败:" + e.getMessage());
        }
    }
}