package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.common.HeatDriver;
import com.bocloud.cmp.model.CloudStackParam;
import com.bocloud.cmp.model.HeatStackModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.CloudServerService;
import com.bocloud.ims.interfaces.resource.CloudStackService;
import com.bocloud.ims.interfaces.resource.LoadBalancerService;
import com.bocloud.ims.interfaces.resource.SyncService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
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.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

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

/**
 * cloud stack impl
 *
 * @author ymh
 * @时间 2019年11月13日
 */
@Service("cloudStackService")
public class CloudStackServiceImpl implements CloudStackService {

    private static Logger logger = LoggerFactory.getLogger(CloudStackServiceImpl.class);
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private CloudStackRepository cloudStackRepository;
    @Autowired
    private HeatDriver heatDriver;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    @Lazy
    private SyncService syncService;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    @Lazy
    private CloudServerService cloudServerService;
    @Autowired
    private AutoScalingRepository autoScalingRepository;
    @Autowired
    private OSTenantRepository osTenantRepository;
    @Autowired
    private CloudStackEventRepository cloudStackEventRepository;
    @Autowired
    private LoadBalancerService loadBalancerService;

    @Override
    public BocloudResult<GridBean<CloudStack>> list(Pager pager) {
        List<CloudStack> list = Lists.newArrayList();
        GridBean gridBean = null;
        try {
            int page = pager.getPage();
            int rows = pager.getRows();
            List<Param> params = pager.getParams();
            Map<String, String> sorter = pager.getSorter();
            boolean simple = pager.getSimple();
            sorter.put("gmtCreate", Common.ONE);
            int total = cloudStackRepository.count(params);
            if (simple) {
                List<SimpleBean> beans = cloudStackRepository.list(params, sorter);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = cloudStackRepository.list(page, rows, params, sorter);
                gridBean = GridHelper.getBean(page, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list cloudStack failure:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }

    }

    @Override
    public BocloudResult create(CloudStack cloudStack, RequestContext context) {
        try {
            CloudVendor cloudVendor = cloudVendorRepository.query(cloudStack.getVendorId());
            Assert.notNull(cloudVendor, "云平台不存在！");
            String template = cloudStack.getTemplate();
            Assert.notNull(template, "模板信息错误！");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String operate = cloudVendor.getType().toLowerCase() + ".stack";
                    String content = "申请栈资源失败";
                    BocloudResult bocloudResult = new BocloudResult<>(false, content);
                    String path = CloudVendor.class.getSimpleName() + "_" + cloudVendor.getId();
                    try (BocloudACLock lock = lockFactory.getACLock(path)) {
                        if (!lock.acquire(10, TimeUnit.SECONDS)) {
                            logger.error("栈资源创建终止，当前云平台存在同步任务！");
                            resourceEventPublisher
                                    .send(new OperateResult(bocloudResult.isSuccess(), "当前平台存在同步任务，请稍后进行此操作...",
                                            operate, MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                                            OperateResult.OperateCategory.ResourceEvent, context));
                            return;
                        }
                        HeatStackModel heatStackModel = new HeatStackModel();
                        BeanUtils.copyProperties(cloudStack, heatStackModel);
                        bocloudResult = heatDriver.createStack(heatStackModel, cloudVendor.getUuid(),
                                cloudStack.getRegionId());
                        if (bocloudResult.isSuccess()) {
                            HeatStackModel stackModel = (HeatStackModel) bocloudResult.getData();
                            CloudStack stack = new CloudStack();
                            BeanUtils.copyProperties(stackModel, stack);
                            stack.setCreatorId(context.getTarget());
                            stack.setMenderId(context.getTarget());
                            stack.setVendorId(cloudStack.getVendorId());
                            cloudStackRepository.save(stack);
                            syncStackEvent(stackModel, cloudVendor.getId(), stack.getId());
                            // 如果当前模板状态为创建完成 则同步云平台数据
                            if ("CREATE_COMPLETE".equalsIgnoreCase(stackModel.getStatus())) {
                                syncService.sync(cloudVendor.getId(), context, true);
                            }
                            content = "资源创建成功！";
                        } else {
                            content = bocloudResult.getMessage();
                        }
                    } catch (Exception e) {
                        content = e.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), content, operate,
                            MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                            OperateResult.OperateCategory.ResourceEvent, context));
                }
            }).start();
            return new BocloudResult<>(true, "申请栈资源任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("新建失败：" + e.getMessage());
            return new BocloudResult<>(false, "新建失败：" + e.getMessage());
        }
    }

    @Override
    public void syncStackEvent(HeatStackModel stackModel, Long vendorId, Long stackId) {
        try {
            List<CloudStackEvent> heatEvents = cloudStackEventRepository.listByStackId(stackId, vendorId);
            Map<String, CloudStackEvent> eventMap = new HashMap<String, CloudStackEvent>();
            if (!heatEvents.isEmpty()) {
                for (CloudStackEvent cloudStackEvent : heatEvents) {
                    eventMap.put(cloudStackEvent.getEventId(), cloudStackEvent);
                }
            }
            String events = stackModel.getEvents();
            if (!StringUtils.isEmpty(events)) {
                List<JSONObject> stackEvents = JSONObject.parseArray(events, JSONObject.class);
                CloudStackEvent cloudStackEvent = null;
                for (JSONObject jsonObject : stackEvents) {
                    String stackEventId = jsonObject.getString("id");
                    if (!eventMap.containsKey(stackEventId)) {
                        cloudStackEvent = new CloudStackEvent();
                        cloudStackEvent.setStackId(stackId);
                        cloudStackEvent.setVendorId(vendorId);
                        cloudStackEvent.setReason(jsonObject.getString("reason"));
                        cloudStackEvent.setLocalReourceId(jsonObject.getString("localReourceId"));
                        cloudStackEvent.setPhysicalResourcId(jsonObject.getString("physicalResourceId"));
                        cloudStackEvent.setResourceName(jsonObject.getString("resourceName"));
                        cloudStackEvent.setResourceStatus(jsonObject.getString("resourceStatus"));
                        cloudStackEvent.setEventId(stackEventId);
                        cloudStackEvent.setEventTime(
                                jsonObject.getDate("time") == null ? new Date() : jsonObject.getDate("time"));
                        cloudStackEventRepository.save(cloudStackEvent);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("获取伸缩组事件异常：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult modify(CloudStack cloudStack, Long userId) {
        String path = CloudStack.class.getSimpleName() + "_" + cloudStack.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            return new BocloudResult<>(true, "开发中！");
        } catch (Exception e) {
            logger.error("编辑失败：" + e.getMessage());
            return new BocloudResult<>(false, "编辑失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        try {
            CloudStack cloudStack = cloudStackRepository.query(id);
            Assert.notNull(cloudStack, "删除失败");
            CloudVendor cloudVendor = cloudVendorRepository.query(cloudStack.getVendorId());
            Assert.notNull(cloudVendor, "云平台不存在！");
            new Thread(new Runnable() {
                /*
                 * (non-Javadoc)
                 *
                 * @see java.lang.Runnable#run()
                 */
                @Override
                public void run() {
                    String operate = cloudVendor.getType().toLowerCase() + ".stack";
                    String content = "删除失败！";
                    BocloudResult bocloudResult = new BocloudResult<>(false, content);
                    String path = CloudStack.class.getSimpleName() + "_" + id;
                    try (BocloudACLock lock = lockFactory.getACLock(path)) {
                        Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
                        HeatStackModel heatStackModel = new HeatStackModel();
                        heatStackModel.setName(cloudStack.getName());
                        heatStackModel.setStackUuid(cloudStack.getStackUuid());
                        String projectName = null;
                        OSTenant osTenant = osTenantRepository.getByTenantUuid(cloudStack.getTenantUuid(),
                                cloudVendor.getId());
                        if (null != osTenant) {
                            projectName = osTenant.getName();
                        }
                        bocloudResult = heatDriver.removeStack(heatStackModel, projectName, cloudVendor.getUuid(),
                                null);
                        if (bocloudResult.isSuccess()) {
                            cloudStackRepository.remove(id, context.getTarget());
                            removeResource(cloudStack, cloudVendor, context);
                            content = "资源删除成功！";
                        } else {
                            content = bocloudResult.getMessage();
                        }
                    } catch (Exception e) {
                        content = e.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), content, operate,
                            MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                            OperateResult.OperateCategory.ResourceEvent, context));
                }
            }).start();
            return new BocloudResult<>(true, "删除任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("删除失败：" + e.getMessage());
            return new BocloudResult<>(false, "删除失败：" + e.getMessage());
        }
    }

    private void removeResource(CloudStack cloudStack, CloudVendor cloudVendor, RequestContext context)
            throws Exception {
        Set<CloudServer> resourceServers = new HashSet<CloudServer>();
        String resources = cloudStack.getResources();
        List<JSONObject> parseArray = JSON.parseArray(resources, JSONObject.class);
        if (null != parseArray && parseArray.size() > 0) {
            for (JSONObject jsonObject : parseArray) {
                if ("OS::Nova::Server".equalsIgnoreCase(jsonObject.getString("type"))) {
                    String physicalResourceId = jsonObject.getString("physicalResourceId");
                    CloudServer cloudServer = cloudServerRepository.getByInstanceId(physicalResourceId);
                    if (null != cloudServer) {
                        resourceServers.add(cloudServer);
                    }
                }
            }
        }
        List<CloudServer> cloudServers = cloudServerRepository.queryByEnv(cloudStack.getId(), cloudVendor.getId());
        if (null != cloudServers && !cloudServers.isEmpty()) {
            resourceServers.addAll(cloudServers);
        }
        for (CloudServer cloudServer : resourceServers) {
            cloudServerService.remove(cloudServer.getId(), null, context);
        }
        // 如果栈是通过伸缩创建的 则删除伸缩组
        List<AutoScaling> autoScalings = autoScalingRepository.listByStackId(cloudStack.getId(), cloudVendor.getId());
        if (null != autoScalings && autoScalings.size() > 0) {
            for (AutoScaling autoScaling : autoScalings) {
                autoScalingRepository.remove(autoScaling.getId(), context.getTarget());
                if (autoScaling.getUseLb()) {
                    loadBalancerService.remove(autoScaling.getLbId(), context);
                }
            }
        }
        cloudStackEventRepository.removeByStackId(cloudStack.getId(), cloudVendor.getId());
    }

    @Override
    public BocloudResult<CloudStack> detail(Long id) {
        try {
            CloudStack cloudStack = cloudStackRepository.query(id);
            Assert.notNull(cloudStack, "数据不存在！");
            return new BocloudResult<>(true, cloudStack, "success!");
        } catch (Exception e) {
            logger.error("查询失败：" + e.getMessage());
            return new BocloudResult<>(false, "查询失败：" + e.getMessage());
        }
    }

    public BocloudResult resume(Long id, RequestContext context) {
        String path = CloudStack.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            CloudStack cloudStack = cloudStackRepository.query(id);
            Assert.notNull(cloudStack, "栈信息异常");
            CloudVendor cloudVendor = cloudVendorRepository.query(cloudStack.getVendorId());
            Assert.notNull(cloudVendor, "云平台不存在");
            new Thread(() -> {
                String operate = cloudVendor.getType().toLowerCase() + ".stack";
                String content = "恢复失败！";
                BocloudResult bocloudResult = new BocloudResult<>(false, content);
                try {
                    HeatStackModel heatStackModel = new HeatStackModel();
                    BeanUtils.copyProperties(cloudStack, heatStackModel);
                    bocloudResult = heatDriver.resumeStack(heatStackModel, cloudVendor.getUuid(),
                            cloudStack.getRegionId());
                    if (bocloudResult.isSuccess()) {
                        HeatStackModel stackModel = (HeatStackModel) bocloudResult.getData();
                        cloudStack.setMenderId(context.getTarget());
                        cloudStack.setStatus(stackModel.getStatus());
                        cloudStackRepository.update(cloudStack);
                        syncStackEvent(stackModel, cloudVendor.getId(), cloudStack.getId());
                        content = "恢复成功！";
                    } else {
                        content = bocloudResult.getMessage();
                    }
                } catch (Exception e) {
                    content = e.getMessage();
                }
                resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), content, operate,
                        MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            }).start();
            return new BocloudResult<>(true, "挂起任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("恢复失败：" + e.getMessage());
            return new BocloudResult<>(false, "恢复失败：" + e.getMessage());
        }
    }

    public BocloudResult suspend(Long id, RequestContext context) {
        String path = CloudStack.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            CloudStack cloudStack = cloudStackRepository.query(id);
            Assert.notNull(cloudStack, "栈信息异常");
            CloudVendor cloudVendor = cloudVendorRepository.query(cloudStack.getVendorId());
            Assert.notNull(cloudVendor, "云平台不存在");
            new Thread(() -> {
                String operate = cloudVendor.getType().toLowerCase() + ".stack";
                String content = "挂起失败！";
                BocloudResult bocloudResult = new BocloudResult<>(false, content);
                try {
                    HeatStackModel heatStackModel = new HeatStackModel();
                    BeanUtils.copyProperties(cloudStack, heatStackModel);
                    bocloudResult = heatDriver.suspendStack(heatStackModel, cloudVendor.getUuid(),
                            cloudStack.getRegionId());
                    if (bocloudResult.isSuccess()) {
                        HeatStackModel stackModel = (HeatStackModel) bocloudResult.getData();
                        cloudStack.setMenderId(context.getTarget());
                        cloudStack.setStatus(stackModel.getStatus());
                        cloudStackRepository.update(cloudStack);
                        syncStackEvent(stackModel, cloudVendor.getId(), cloudStack.getId());
                        content = "挂起成功！";
                    } else {
                        content = bocloudResult.getMessage();
                    }
                } catch (Exception e) {
                    content = e.getMessage();
                }
                resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), content, operate,
                        MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            }).start();
            return new BocloudResult<>(true, "挂起任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("挂起失败：" + e.getMessage());
            return new BocloudResult<>(false, "挂起失败：" + e.getMessage());
        }
    }

    public BocloudResult check(Long id, RequestContext context) {
        String path = CloudStack.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            CloudStack cloudStack = cloudStackRepository.query(id);
            Assert.notNull(cloudStack, "栈信息异常");
            CloudVendor cloudVendor = cloudVendorRepository.query(cloudStack.getVendorId());
            Assert.notNull(cloudVendor, "平台不存在");
            new Thread(() -> {
                String operate = cloudVendor.getType().toLowerCase() + ".stack";
                String content = "栈检查失败！";
                BocloudResult bocloudResult = new BocloudResult<>(false, content);
                try {
                    HeatStackModel heatStackModel = new HeatStackModel();
                    BeanUtils.copyProperties(cloudStack, heatStackModel);
                    bocloudResult = heatDriver.cheakStack(heatStackModel, cloudVendor.getUuid(),
                            cloudStack.getRegionId());
                    if (bocloudResult.isSuccess()) {
                        HeatStackModel stackModel = (HeatStackModel) bocloudResult.getData();
                        cloudStack.setMenderId(context.getTarget());
                        cloudStack.setStatus(stackModel.getStatus());
                        cloudStackRepository.update(cloudStack);
                        syncStackEvent(stackModel, cloudVendor.getId(), cloudStack.getId());
                        content = "栈检查成功！";
                    } else {
                        content = bocloudResult.getMessage();
                    }
                } catch (Exception e) {
                    content = e.getMessage();
                }
                resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), content, operate,
                        MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            }).start();
            return new BocloudResult<>(true, "栈检查任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("栈检查失败：" + e.getMessage());
            return new BocloudResult<>(false, "栈检查失败：" + e.getMessage());
        }
    }

	@Override
	public BocloudResult operation(Long id, RequestContext context, CloudStackParam cloudStackParam) {
		String action = cloudStackParam.getAction();
		if ("resume".equalsIgnoreCase(action)) {
			return this.resume(id, context);
		}
		if ("suspend".equalsIgnoreCase(action)) {
			return this.suspend(id, context);
		}
		if ("check".equalsIgnoreCase(action)) {
			return this.check(id, context);
		}
		return new BocloudResult<>(false, "无效操作！");
	}

    @Override
    public BocloudResult batchRemove(List<Long> ids, RequestContext context) {
        if (ListTool.isEmpty(ids)) {
            return new BocloudResult<>(false, "数据不存在！");
        }
        try {
            for (Long id : ids) {
                CloudStack cloudStack = cloudStackRepository.query(id);
                if (null == cloudStack) {
                    continue;
                }
                this.remove(id, context);
            }
        } catch (Exception e) {
            logger.error("batch action server fail:", e);
            return new BocloudResult<>(false, "批量任务下发失败");
        }
        return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
    }

    @Override
    public BocloudResult<GridBean<CloudStackEvent>> stackEventList(Pager pager) {
        List<CloudStackEvent> list = Lists.newArrayList();
        GridBean gridBean = null;
        try {
            int page = pager.getPage();
            int rows = pager.getRows();
            List<Param> params = pager.getParams();
            Map<String, String> sorter = pager.getSorter();
            boolean simple = pager.getSimple();
            sorter.put("eventTime", Common.ONE);
            int total = cloudStackEventRepository.count(params);
            if (simple) {
            } else {
                list = cloudStackEventRepository.list(page, rows, params, sorter);
                gridBean = GridHelper.getBean(page, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list cloudStackEvent failure:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }

    }

}
