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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.HostDriver;
import com.bocloud.cmp.model.HostModel;
import com.bocloud.cmp.params.fusionsphere.HostOperationParams;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.IDFactory;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.fusionsphere.FusionSphereHostService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.task.ResourceTaskNotice;
import com.bocloud.ims.service.utils.BeanUtils;
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.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 org.springframework.util.StringUtils;

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: FusionSphereHostServiceImpl
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2021/10/20 10:20
 */
@Slf4j
@Service
public class FusionSphereHostServiceImpl implements FusionSphereHostService {

    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 ServerRepository serverRepository;
    @Autowired
    private HostDriver hostDriver;
    @Autowired
    private ServerConfigRepository serverConfigRepository;
    @Autowired
    private ResourceTaskNotice resourceTaskNotice;


    @Override
    public BocloudResult<GridBean<Server>> 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 = serverRepository.count(params);
            List<Server> list = this.serverRepository.list(page, rows, params, sorter);
            for (Server server : list) {
                ServerConfig config = serverConfigRepository.getBySid(server.getId());
                server.setCpuAmount(config.getCpuAmount());
                server.setCpuHz(config.getCpuHz());
                server.setCpuUsed(config.getCpuUsed());
                server.setMemoryCapacity(config.getMemoryCapacity());
                server.setMemUsed(config.getMemUsed());
            }
            GridBean<Server> gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            log.error("list Server error!", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Server> detail(Long id, RequestContext context) {
        try {
            Server entity = serverRepository.query(id);
            Assert.notNull(entity, "资源不存在");
            return new BocloudResult<>(true, entity, "查询详情成功");
        } catch (Exception e) {
            log.error("detail Server fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> create(Server entityParams, RequestContext context) {
        try {
            CloudVendor vendor = vendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            Vdc vdc = vdcRepository.query(entityParams.getVdcId());
            Assert.notNull(vdc, "站点不存在");
            Cluster cluster = clusterRepository.query(entityParams.getClusterId());

            if (null != serverRepository.getByName(entityParams.getVdcId(), entityParams.getName())) {
                throw new RuntimeException("资源已存在");
            }

            HostModel model = new HostModel();
            model.setName(entityParams.getName());
            model.setRemark(entityParams.getRemark());
            model.setHostIp(entityParams.getServiceIp());
            if (!ObjectUtils.isEmpty(cluster)) {
                model.setClusterUuid(cluster.getClusterVal());
            }
            model.setVdcUri(vdc.getUri());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".host";
                String message = "创建资源失败";
                BocloudResult result = hostDriver.create(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        message = "创建资源成功";
                        HostModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), HostModel.class);
                        BeanUtils.copyPropertiesIgnoreNull(newModel, entityParams);
                        entityParams.setCreatorId(context.getTarget());
                        entityParams.setMenderId(context.getTarget());
                        entityParams.setPowerState(newModel.getStatus());
                        entityParams.setResourceId(IDFactory.instance().uuid());
                        serverRepository.save(entityParams);
                        //通知主机资源
                        Server serverToTask = serverRepository.query(entityParams.getId());
                        if(null != serverToTask) {
                            resourceTaskNotice.sendPhysicalMessage(serverToTask, ResourceTaskNotice.Action.ADD);
                        }

                        ServerConfig entity = new ServerConfig();
                        entity.setServerId(entityParams.getId());
                        entity.setCpuAmount(newModel.getCpuNum());
                        entity.setCpuHz(newModel.getCpuHz());
                        entity.setCpuUsed(newModel.getCpuUsed());
                        entity.setMemoryCapacity(newModel.getMemTotal());
                        entity.setMemUsed(newModel.getMemUsed());
                        serverConfigRepository.save(entity);
                    } 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(Server modifyParams, RequestContext context) {
        try {
            Server entity = serverRepository.query(modifyParams.getId());
            Assert.notNull(entity, "数据不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");

            HostModel model = new HostModel();
            model.setName(modifyParams.getName());
            model.setRemark(modifyParams.getRemark());
            model.setUri(entity.getUri());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".cluster";
                String message = "更新Server失败";
                BocloudResult result = hostDriver.modify(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        entity.setName(modifyParams.getName());
                        entity.setRemark(modifyParams.getRemark());
                        serverRepository.update(entity);
                        //发送消息给cop
                        resourceTaskNotice.sendPhysicalMessage(entity, ResourceTaskNotice.Action.UPDATE);
                        message = "更新Server成功";
                    } else {
                        log.error("更新Server失败：" + 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 Server 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 Server error!", e);
            return new BocloudResult<>(false, "更新失败:" + e.getMessage());
        }
    }

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

            HostModel model = new HostModel();
            model.setUri(entity.getUri());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".server";
                String message = "删除Server失败";
                BocloudResult result = hostDriver.remove(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        message = "删除Server成功";
                        serverRepository.remove(id, context.getTarget());
                        serverConfigRepository.removeBySid(id);
                        //通知主机资源
                        resourceTaskNotice.sendPhysicalMessage(entity, ResourceTaskNotice.Action.DEL);
                    } else {
                        log.error("删除Server失败：" + 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 Server 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 Server error!", e);
            return new BocloudResult<>(false, "删除失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> operations(HostOperationParams params, RequestContext context) {
        String action = params.getAction();
        switch (action) {
            case "startMaintain":
            case "endMaintain":
                return maintain(params, context);
            case "move":
                return moveTo(params, context);
            default:
                return new BocloudResult<Void>(false, "Unknown Operation！");
        }
    }

    public BocloudResult<Void> maintain(HostOperationParams params, RequestContext context) {
        try {
            Server entity = serverRepository.query(params.getId());
            Assert.notNull(entity, "数据不存在");
            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");

            HostModel model = new HostModel();
            model.setUri(entity.getUri());
            model.setMaintained(params.getMaintained());

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".server";
                String message = "维护操作失败";
                BocloudResult result = hostDriver.maintain(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        message = "维护操作成功";
                        entity.setMaintained(params.getMaintained());
                        serverRepository.update(entity);
                    } 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("maintain server 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("maintain server error!", e);
            return new BocloudResult<>(false, "维护操作失败:" + e.getMessage());
        }
    }

    public BocloudResult<Void> moveTo(HostOperationParams params, RequestContext context) {
        try {
            Server entity = serverRepository.query(params.getId());
            Assert.notNull(entity, "数据不存在");
            Cluster cluster = new Cluster();
            if (!StringUtils.isEmpty(params.getClusterId())) {
                cluster = clusterRepository.query(params.getClusterId());
                Assert.notNull(cluster, "集群不存在");
            }

            CloudVendor vendor = vendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");

            HostModel model = new HostModel();
            model.setUri(entity.getUri());
            model.setUuid(entity.getUuid());
            BocloudResult result = hostDriver.moveTo(vendor.getUuid(), model, cluster.getClusterVal());
            // 更新数据
            if (result.isSuccess()) {
                entity.setClusterId(cluster.getId());
                entity.setClusterName(cluster.getName());
            //    entity.setUri(cluster.getUri());
                entity.setVdcId(null == cluster.getVdcId() ? entity.getVdcId() : cluster.getVdcId());
                entity.setVdcName(null == cluster.getVdcName() ? entity.getVdcName() : cluster.getVdcName());
                serverRepository.update(entity);
                //发送消息给cop
                resourceTaskNotice.sendPhysicalMessage(entity, ResourceTaskNotice.Action.UPDATE);
            }else {
                return new BocloudResult<>(false, "移动操作失败:" + result.getMessage());
            }
            return new BocloudResult<>(true, "操作成功");
        } catch (Exception e) {
            log.error("moveTo server error!", e);
            return new BocloudResult<>(false, "移动操作失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult listByStorageDeviceId(Long id, RequestContext context) {
        try {
            List<Server> entity = serverRepository.listByStorageDeviceId(id);
            return new BocloudResult<>(true, entity, "查询详情成功");
        } catch (Exception e) {
            log.error("listByStorageDeviceId Server fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }
}