package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.ServerLabelParam;
import com.bocloud.common.enums.ResourceCategory;
import com.bocloud.common.exception.BoCloudDBException;
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.LabelService;
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.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

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

@Service("labelService")
public class LabelServiceImpl implements LabelService {

    private static final Logger logger = LoggerFactory.getLogger(LabelServiceImpl.class);

    @Autowired
    private LabelRepository labelRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private ResourceEventRepository resourceEventRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private VpcRepository vpcRepository;
    @Autowired
    private FloatingIpRepository floatingIpRepository;
    @Autowired
    private ElasticIpRepository elasticIpRepository;
    @Autowired
    private VolumeRepository volumeRepository;

    @Override
    public BocloudResult<GridBean<Label>> list(Pager pager, RequestContext context) {
        List<Label> list = null;
        int total = 0;
        GridBean gridBean = null;
        try {
            List<Param> params = Optional.ofNullable(pager.getParams()).orElse(new ArrayList<>());
            Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
            Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
            total = labelRepository.count(params);
            if (simple) {
                List<Label> beans = this.labelRepository.list(params);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = labelRepository.list(page, rows, params, sorter);
                gridBean = GridHelper.getBean(page, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list label failure:", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<Label> detail(Long id) {
        try {
            Label label = labelRepository.query(id);
            return new BocloudResult<>(true, label, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message：", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    @Transactional(rollbackFor = BoCloudDBException.class)
    public BocloudResult<Label> create(Label label) {
        String path = Label.class.getSimpleName() + "_create";
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(30, TimeUnit.SECONDS), "请求超时");
            if (labelRepository.exists(label.getName(), label.getContent(), label.getCategory())) {
                return new BocloudResult<>(false, "标签已存在");
            }
            labelRepository.save(label);
            return new BocloudResult<>(true, label, "添加成功");
        } catch (Exception e) {
            logger.error("Create label error:", e);
            return new BocloudResult<>(false, "添加失败");
        }
    }

    @Override
    public BocloudResult<Label> modify(Label label) {
        String path = Label.class.getSimpleName() + "_" + label.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Label oldLabel = this.labelRepository.query(label.getId());
            if (null == oldLabel) {
                logger.warn("Label does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }
            if (!label.getName().equals(oldLabel.getName())) {
                if (labelRepository.exists(label.getName())) {
                    return new BocloudResult<>(false, "名称已存在");
                }
            }
            oldLabel.setContent(label.getContent());
            oldLabel.setName(label.getName());
            oldLabel.setCategory(label.getCategory());
            this.labelRepository.update(oldLabel);
            return new BocloudResult<>(true, "修改成功");
        } catch (Exception e) {
            logger.error("Modify label error:", e);
            return new BocloudResult<>(false, "修改失败");
        }
    }

    @Override
    public BocloudResult remove(Long id) {
        String path = Label.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Label oldLabel = this.labelRepository.query(id);
            if (null == oldLabel) {
                logger.warn("Label does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }
            this.labelRepository.remove(id);
            // 删除已经绑定的资源标签
            // processResource(oldLabel);
            return new BocloudResult<>(true, "删除成功");
        } catch (Exception e) {
            logger.error("Remove label error:", e);
            return new BocloudResult<>(false, "删除失败");
        }
    }

    @Override
    public BocloudResult createResources(ServerLabelParam serverLabelParam, RequestContext context) {
        try {
            if (null == serverLabelParam) {
                return new BocloudResult<>(false, "参数格式错误");
            }
            if (null == serverLabelParam.getVendorId() || 0L == serverLabelParam.getVendorId()) {
                return new BocloudResult<>(false, "请传入平台信息");
            }
            new Thread(() -> {
                String operate = ".serverlabel";
                Long vendorId = serverLabelParam.getVendorId();
                try {
                    List<Long> targetIds = serverLabelParam.getTargetIds();
                    Long targetId = serverLabelParam.getTargetId();
                    if (null != targetId) {
                        targetIds.add(targetId);
                        targetIds.stream().distinct();
                    }
                    String resourceLabel = serverLabelParam.getResourceLabel();
                    ResourceEvent event = null;
                    if (!ListTool.isEmpty(targetIds)) {
                        for (Long target : targetIds) {
                            switch (serverLabelParam.getType()) {
                                case COMPUTER:
                                    CloudServer cloudServer = cloudServerRepository.query(target);
                                    if (null == cloudServer) {
                                        logger.error("云主机ID [{}] 不存在", target);
                                        continue;
                                    }
                                    cloudServer.setResourceLabel(resourceLabel);
                                    cloudServerRepository.update(cloudServer);
                                    operate = ResourceCategory.COMPUTER.name();
                                    vendorId = cloudServer.getVendorId();
                                    event = new ResourceEvent(targetId, operate + "标签配置", operate + "配置标签成功：" + resourceLabel, context.getTarget(),
                                            new Date(), operate, vendorId, context.getCatalog());
                                    break;
                                case VPC:
                                    Vpc vpc = vpcRepository.query(target);
                                    if (null == vpc) {
                                        logger.error("Vpc ID [{}] 不存在", target);
                                        continue;
                                    }
                                    vpc.setResourceLabel(resourceLabel);
                                    vpcRepository.update(vpc);
                                    operate = ResourceCategory.VPC.name();
                                    vendorId = vpc.getVendorId();
                                    event = new ResourceEvent(targetId, operate + "标签配置", operate + "配置标签成功：" + resourceLabel, context.getTarget(),
                                            new Date(), operate, vendorId, context.getCatalog());
                                    break;
                                case STORAGE:
                                    Volume volume = volumeRepository.query(target);
                                    if (null == volume) {
                                        logger.error("Volume ID [{}] 不存在", target);
                                        continue;
                                    }
                                    volume.setResourceLabel(resourceLabel);
                                    volumeRepository.update(volume);
                                    operate = ResourceCategory.STORAGE.name();
                                    event = new ResourceEvent(targetId, operate + "标签配置", operate + "配置标签成功：" + resourceLabel, context.getTarget(),
                                            new Date(), operate, vendorId, context.getCatalog());
                                    break;
                                case FLOATING_IP:
                                    // 根据平台获取浮动IP
                                    CloudVendor vendor = cloudVendorRepository.query(vendorId);
                                    if (CloudProvider.ALIYUN.name().equals(vendor.getType()) || CloudProvider.HUAWEI.name().equals(vendor.getType())) {
                                        ElasticIp elasticIp = elasticIpRepository.query(target);
                                        if (null == elasticIp) {
                                            logger.error("ElasticIp ID [{}] 不存在", target);
                                            continue;
                                        }
                                        elasticIp.setResourceLabel(resourceLabel);
                                        elasticIpRepository.update(elasticIp);
                                    } else {
                                        FloatingIp floatingIp = floatingIpRepository.query(target);
                                        if (null == floatingIp) {
                                            logger.error("FloatingIp ID [{}] 不存在", target);
                                            continue;
                                        }
                                        floatingIp.setResourceLabel(resourceLabel);
                                        floatingIpRepository.update(floatingIp);
                                    }
                                    operate = ResourceCategory.FLOATING_IP.name();
                                    event = new ResourceEvent(targetId, operate + "标签配置", operate + "配置标签成功：" + resourceLabel, context.getTarget(),
                                            new Date(), operate, vendorId, context.getCatalog());
                                    break;
                                default:
                                    operate = serverLabelParam.getType().name();
                                    resourceEventPublisher.send(new OperateResult(false, "标签操作失败：不存在此类型的标签", operate,
                                            MapTools.simpleMap(Common.VENDORID, vendorId), OperateResult.OperateCategory.ResourceEvent,
                                            context));
                                    break;
                            }
                            if (null != event) {
                                resourceEventRepository.save(event);
                            }
                            operate = operate + ".serverlabel";
                            resourceEventPublisher
                                    .send(new OperateResult(true, "标签操作成功", operate, MapTools.simpleMap(Common.VENDORID, vendorId),
                                            OperateResult.OperateCategory.ResourceEvent, context));
                        }
                    }
                } catch (Exception e) {
                    resourceEventPublisher.send(new OperateResult(false, "标签操作失败：" + e.getMessage(), operate,
                            MapTools.simpleMap(Common.VENDORID, vendorId), OperateResult.OperateCategory.ResourceEvent,
                            context));
                }
            }).start();
            return new BocloudResult<String>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("资源标签操作异常：" + e.getMessage());
            return new BocloudResult<>(false, "资源标签操作异常：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult isExistsValues(List<JSONObject> parseArray) {
        try {
            for (int i = parseArray.size() - 1; i >= 0; i--) {
                JSONObject jsonObject = parseArray.get(i);
                String key = jsonObject.getString("key");
                String value = jsonObject.getString("value");
                Label label = labelRepository.getLabelByName(key);
                if (null != label) {
                    List<String> contentArray = JSON.parseArray(label.getContent(), String.class);
                    if (null != contentArray && !contentArray.contains(value)) {
                        parseArray.remove(jsonObject);
                    }
                } else {
                    parseArray.remove(jsonObject);
                }
            }
            return new BocloudResult(true, parseArray, "标签检验成功");
        } catch (Exception e) {
            logger.error("标签校验异常：" + e.getMessage());
            return new BocloudResult<>(false, "标签校验异常：" + e.getMessage());
        }
    }
}
