package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.VAppDriver;
import com.bocloud.cmp.entity.enums.BocloudStatus;
import com.bocloud.cmp.model.VAppModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.OperateResult.OperateCategory;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.VAppService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.task.ResourceTaskNotice;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import org.apache.commons.lang3.StringUtils;
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 java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * vapp业务访问
 *
 * @author HP
 */
@Service("vAppService")
public class VAppServiceImpl implements VAppService {

    private static OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;
    private Logger logger = LoggerFactory.getLogger(VAppServiceImpl.class);
    @Autowired
    private VAppRepository repository;
    @Autowired
    private ServerRepository serverRepository;
    @Autowired
    private ClusterRepository clusterRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private VAppDriver vAppDriver;
    @Autowired
    private VdcRepository vdcRepository;
    @Autowired
    private ResourcePoolRepository poolRepository;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private ResourceTaskNotice resourceTaskNotice;

    @Override
    public BocloudResult<List<VApp>> list(Long parentId, Long poolId, Long vendorId) {
        try {
            List<VApp> vapps = repository.list(parentId, poolId, vendorId);
            for (VApp vapp : vapps) {
                if ("CLUSTER".equals(vapp.getTargetCategory())) {
                    Cluster cluster = clusterRepository.query(vapp.getTargetId());
                    if (null != cluster) {
                        vapp.setTargetName(cluster.getName());
                    }
                } else if ("HOST".equals(vapp.getTargetCategory())) {
                    Server server = serverRepository.query(vapp.getTargetId());
                    if (null != server) {
                        vapp.setTargetName(server.getName());
                    }
                }
                List<VApp> children = repository.list(vapp.getId(), null, vendorId);
                vapp.setChildren(ListTool.isEmpty(children) ? "[]" :JSONObject.toJSONString(children));
            }
            return new BocloudResult<>(true, vapps, "查询vapp成功");
        } catch (Exception e) {
            logger.error("list resource pool fail:", e);
            return new BocloudResult<>(false, "查询vapp失败");
        }
    }

    @Override
    public BocloudResult<String> create(VApp vapp, RequestContext context) {
        try {
            CloudVendor vendor = cloudVendorRepository.query(vapp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            Vdc vdc = vdcRepository.query(vapp.getVdcId());
            Assert.notNull(vdc, "数据中心不存在");
            VAppModel model = new VAppModel();
            BeanUtils.copyProperties(vapp, model);
            model.setVdcName(vdc.getName());
            Long poolId = vapp.getPoolId();
            if (null != poolId) {
                VmResourcePool pool = poolRepository.query(poolId);
                Assert.notNull(pool, "资源池不存在");
                model.setPoolVal(pool.getPoolVal());
            }
            Long parentId = vapp.getParentId();
            if (null != parentId) {
                VApp parent = repository.query(parentId);
                Assert.notNull(parent, "父VApp不存在");
                model.setParentName(parent.getName());
                poolId = parent.getPoolId();
            }
            final Long poolIdData = poolId;
            new Thread(() -> {
                String operate = vendor.getType().toLowerCase() + ".vapp";
                String content = "vapp创建失败";
                BocloudResult result = vAppDriver.create(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        content = "vapp创建成功";
                        VAppModel vAppModel =
                            JSONObject.parseObject(JSONObject.toJSONString(result.getData()), VAppModel.class);
                        VApp newVapp = new VApp();
                        BeanUtils.copyProperties(vAppModel, newVapp);
                        if (!StringUtils.isEmpty(newVapp.getHostName())) {
                            Server server = serverRepository.getByName(newVapp.getHostName(), vendor.getId());
                            if (null != server) {
                                newVapp.setTargetId(server.getId());
                            }
                        } else {
                            Cluster cluster = clusterRepository.getByVal(newVapp.getTargetVal(), vapp.getVdcId());
                            if (null != cluster) {
                                newVapp.setTargetId(cluster.getId());
                            }
                        }
                        VmResourcePool pool = poolRepository.queryByPoolVal(newVapp.getPoolVal(), vapp.getVdcId());
                        if (null != pool) {
                            newVapp.setPoolId(pool.getId());
                        }
                        newVapp.setVendorId(vendor.getId());
                        newVapp.setVdcId(vapp.getVdcId());
                        newVapp.setCreatorId(context.getTarget());
                        newVapp.setPoolId(poolIdData);
                        VApp parent = repository.queryByVal(newVapp.getParentVal(), vdc.getId());
                        newVapp.setParentId(0L);
                        if (null != parent) {
                            newVapp.setParentId(parent.getId());
                        }
                        repository.save(newVapp);
                    } else {
                        content = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    logger.error("Get error message:", e);
                    resourceEventPublisher.send(new OperateResult(false, content, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            }).start();
            return new BocloudResult<>(true, "vapp创建任务已下发，正在执行");
        } catch (Exception e) {
            logger.error("Get error message：{}:", e);
            return new BocloudResult<>(false, "vapp创建失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<String> modify(VApp vapp, RequestContext context) {
        String path = VApp.class.getSimpleName() + "_" + vapp.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            VApp database = repository.query(vapp.getId());
            Assert.notNull(database, "数据不存在");
            CloudVendor vendor = cloudVendorRepository.query(database.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            Vdc vdc = vdcRepository.query(database.getVdcId());
            Assert.notNull(vdc, "数据中心不存在");
            VAppModel model = new VAppModel();
            BeanUtils.copyProperties(vapp, model);
            model.setVdcName(vdc.getName());
            model.setOldName(database.getName());
            new Thread(() -> {
                String operate = vendor.getType().toLowerCase() + ".vapp";
                String content = "vapp编辑失败";
                BocloudResult result = vAppDriver.modify(vendor.getUuid(), model);
                try {
                    if (result.isSuccess()) {
                        content = "vapp编辑成功";
                        VApp newVapp = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), VApp.class);
                        vapp.setName(newVapp.getName());
                        vapp.setConfig(newVapp.getConfig());
                        vapp.setTargetCategory(newVapp.getTargetCategory());
                        vapp.setTargetVal(newVapp.getTargetVal());
                        if (!StringUtils.isEmpty(newVapp.getHostName())) {
                            Server server = serverRepository.getByName(newVapp.getHostName(), vendor.getId());
                            if (null != server) {
                                vapp.setTargetId(server.getId());
                            }
                        } else {
                            Cluster cluster = clusterRepository.getByVal(newVapp.getTargetVal(), vapp.getVdcId());
                            if (null != cluster) {
                                vapp.setTargetId(cluster.getId());
                            }
                        }
                        VmResourcePool pool = poolRepository.queryByPoolVal(newVapp.getPoolVal(), vapp.getVdcId());
                        if (null != pool) {
                            vapp.setPoolId(pool.getId());
                        }
                        vapp.setVappVal(newVapp.getVappVal());
                        vapp.setPoolVal(newVapp.getPoolVal());
                        vapp.setSummary(newVapp.getSummary());
                        vapp.setVappConfig(newVapp.getVappConfig());
                        vapp.setVmInstanceIds(newVapp.getVmInstanceIds());
                        vapp.setTags(newVapp.getTags());
                        vapp.setMenderId(context.getTarget());
                        vapp.setParentVal(newVapp.getParentVal());
                        VApp parent = repository.queryByVal(newVapp.getParentVal(), vdc.getId());
                        vapp.setParentId(0L);
                        if (null != parent) {
                            vapp.setParentId(parent.getId());
                        }
                        repository.update(vapp);
                    } else {
                        content = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    logger.error("Get error message:", e);
                    resourceEventPublisher.send(new OperateResult(false, "vapp编辑失败", operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            }).start();
            return new BocloudResult<>(true, "vapp更新任务已下发，正在执行");
        } catch (Exception e) {
            logger.error("Update resource pool fail:", e);
            return new BocloudResult<>(false, "修改vapp失败");
        }
    }

    @Override
    public BocloudResult<String> remove(Long id, Long userId) {
        String path = VmResourcePool.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            VApp vapp = repository.query(id);
            Assert.notNull(vapp, "数据不存在");
            List<VApp> children = repository.listByParentId(id);
            if (!ListTool.isEmpty(children)) {
                return new BocloudResult<>(false, "该vapp下包含子vapp，请先删除子vapp");
            }
            CloudVendor vendor = cloudVendorRepository.query(vapp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            BocloudResult result = vAppDriver.remove(vendor.getUuid(), vapp.getName());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            repository.remove(id, userId);
            return new BocloudResult<>(true, "vapp删除成功");
        } catch (Exception e) {
            logger.error("Get error message:{}", e);
            return new BocloudResult<>(false, "vapp删除失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<VApp> detail(Long id) {
        try {
            VApp vapp = repository.query(id);
            if ("CLUSTER".equals(vapp.getTargetCategory())) {
                Cluster cluster = clusterRepository.query(vapp.getTargetId());
                if (null != cluster) {
                    vapp.setTargetName(cluster.getName());
                }
            } else if ("HOST".equals(vapp.getTargetCategory())) {
                Server server = serverRepository.query(vapp.getTargetId());
                if (null != server) {
                    vapp.setTargetName(server.getName());
                }
            }
            return new BocloudResult<>(true, vapp, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message:{}", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<String> exists(String name, Long parentId) {
        try {
            boolean flag = repository.exists(name, parentId);
            if (flag) {
                return new BocloudResult<>(false, "vapp名称已存在");
            }
            return new BocloudResult<>(true, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message:", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult vrps(String condition) {
        JSONObject object = JSONObject.parseObject(condition);
        String name = null;
        Long parentId = null;
        Long vendorId = null;
        Long poolId = null;
        if (null != object) {
            name = object.getString("name");
            parentId = object.getLong("parentId");
            vendorId = object.getLong("vendorId");
            poolId = object.getLong("poolId");
        }
        if (null == name || "".equals(name)) {
            return this.list(parentId, poolId, vendorId);
        } else {
            return this.exists(name, parentId);
        }
    }

    @Override
    public BocloudResult batchRemove(List<Long> idArray, RequestContext context) {
        try {
            Assert.notEmpty(idArray, "没有需要删除的数据！");
            VApp vapp = repository.query(idArray.get(0));
            Assert.notNull(vapp, "数据不存在");
            CloudVendor vendor = cloudVendorRepository.query(vapp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            // 删除成功结果
            List<BocloudResult> succList = new ArrayList<>();
            // 创建失败结果
            List<BocloudResult> failList = new ArrayList<>();
            List<Future<BocloudResult>> futures = new ArrayList<>();
            new Thread(() -> {
                try {
                    for (Long id : idArray) {
                        futures.add(taskExecutor.submit(() -> this.remove(id, context.getTarget())));
                    }
                    for (int i = 0; i < futures.size(); i++) {
                        BocloudResult result = futures.get(i).get();
                        if (result.isSuccess()) {
                            succList.add(result);
                        } else {
                            failList.add(result);
                        }
                    }
                } catch (Exception e) {
                    logger.error("Get error message：{}", e.getMessage());
                }
                String message = "VApp删除成功【" + succList.size() + "】个，失败【" + failList.size() + "】个";
                resourceEventPublisher.send(new OperateResult(succList.size() > 0 ? true : false, message,
                    vendor.getType().toLowerCase() + ".tier1", MapTools.simpleMap("vendorId", vendor.getId()),
                    OperateCategory.ResourceEvent, context));
            }).start();
            return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("Get error message：{}", e.getMessage());
            return new BocloudResult<>(false, "批量删除失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult start(Long id, RequestContext context) {
        try {
            VApp vApp = repository.query(id);
            Assert.notNull(vApp, "vApp不存在");
            CloudVendor vendor = cloudVendorRepository.query(vApp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            taskExecutor.execute(() -> {
                BocloudResult result = vAppDriver.start(vendor.getUuid(), vApp.getName());
                if (result.isSuccess()) {
                    String vmInstanceIds = vApp.getVmInstanceIds();
                    if (!StringUtils.isEmpty(vmInstanceIds)) {
                        List<String> instanceIds = JSONArray.parseArray(vmInstanceIds, String.class);
                        for (String instanceId : instanceIds) {
                            CloudServer server = cloudServerRepository.getByInstanceId(instanceId, vendor.getId());
                            if (null != server) {
                                server.setStatus(BocloudStatus.Server.RUNNING.name());
                                server.setMenderId(context.getTarget());
                                cloudServerRepository.update(server);
                                //发消息通知cop
                                resourceTaskNotice.sendLogicalMessage(server, ResourceTaskNotice.Action.UPDATE);
                            }
                        }
                    }
                }
                resourceEventPublisher.send(
                    new OperateResult(result.isSuccess(), result.getMessage(), vendor.getType().toLowerCase() + ".vapp",
                        MapTools.simpleMap("vendorId", vendor.getId()), OperateCategory.ResourceEvent, context));
            });
            return new BocloudResult(true, "打开电源任务已下发，请耐心等待处理结果...");
        } catch (Exception e) {
            return new BocloudResult(false, "打开电源失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult stop(Long id, RequestContext context) {
        try {
            VApp vApp = repository.query(id);
            Assert.notNull(vApp, "vApp不存在");
            CloudVendor vendor = cloudVendorRepository.query(vApp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            taskExecutor.execute(() -> {
                BocloudResult result = vAppDriver.stop(vendor.getUuid(), vApp.getName());
                if (result.isSuccess()) {
                    String vmInstanceIds = vApp.getVmInstanceIds();
                    if (!StringUtils.isEmpty(vmInstanceIds)) {
                        List<String> instanceIds = JSONArray.parseArray(vmInstanceIds, String.class);
                        for (String instanceId : instanceIds) {
                            CloudServer server = cloudServerRepository.getByInstanceId(instanceId, vendor.getId());
                            if (null != server) {
                                server.setStatus(BocloudStatus.Server.STOPPED.name());
                                server.setMenderId(context.getTarget());
                                cloudServerRepository.update(server);
                                //发消息通知cop
                                resourceTaskNotice.sendLogicalMessage(server, ResourceTaskNotice.Action.UPDATE);
                            }
                        }
                    }
                }
                resourceEventPublisher.send(
                    new OperateResult(result.isSuccess(), result.getMessage(), vendor.getType().toLowerCase() + ".vapp",
                        MapTools.simpleMap("vendorId", vendor.getId()), OperateCategory.ResourceEvent, context));
            });
            return new BocloudResult(true, "关闭电源任务已下发，请耐心等待处理结果...");
        } catch (Exception e) {
            return new BocloudResult(false, "关闭电源失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult suspend(Long id, RequestContext context) {
        try {
            VApp vApp = repository.query(id);
            Assert.notNull(vApp, "vApp不存在");
            CloudVendor vendor = cloudVendorRepository.query(vApp.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            taskExecutor.execute(() -> {
                BocloudResult result = vAppDriver.suspend(vendor.getUuid(), vApp.getName());
                if (result.isSuccess()) {
                    String vmInstanceIds = vApp.getVmInstanceIds();
                    if (!StringUtils.isEmpty(vmInstanceIds)) {
                        List<String> instanceIds = JSONArray.parseArray(vmInstanceIds, String.class);
                        for (String instanceId : instanceIds) {
                            CloudServer server = cloudServerRepository.getByInstanceId(instanceId, vendor.getId());
                            if (null != server) {
                                server.setStatus(BocloudStatus.Server.SUSPENDED.name());
                                server.setMenderId(context.getTarget());
                                cloudServerRepository.update(server);
                                //发消息通知cop
                                resourceTaskNotice.sendLogicalMessage(server, ResourceTaskNotice.Action.UPDATE);
                            }
                        }
                    }
                }
                resourceEventPublisher.send(
                    new OperateResult(result.isSuccess(), result.getMessage(), vendor.getType().toLowerCase() + ".vapp",
                        MapTools.simpleMap("vendorId", vendor.getId()), OperateCategory.ResourceEvent, context));
            });
            return new BocloudResult(true, "挂起任务已下发，请耐心等待处理结果...");
        } catch (Exception e) {
            return new BocloudResult(false, "挂起失败：" + e.getMessage());
        }
    }
}
