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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.ServerDriver;
import com.bocloud.cmp.driver.network.NetworkCardDriver;
import com.bocloud.cmp.model.NetworkCardModel;
import com.bocloud.cmp.model.ServerModel;
import com.bocloud.cmp.params.fusionsphere.NetworkCardOperationParams;
import com.bocloud.cmp.provider.utils.IPAddress;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.*;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.fusionsphere.FusionSphereNetworkCardService;
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 com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

/**
 * @ClassName: FusionSphereNetworkCardServiceImpl
 * @Description:
 * @Author lxy
 * @Date 2021/10/27 16:33
 */
@Slf4j
@Service
public class FusionSphereNetworkCardServiceImpl implements FusionSphereNetworkCardService {
    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;

    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private SecurityGroupRepository securityGroupRepository;
    @Autowired
    private NetworkCardRepository networkCardRepository;
    @Autowired
    private ServerSgroupRepository serverSgroupRepository;
    @Autowired
    private ServerDriver serverDriver;
    @Autowired
    private PortGroupRepository portGroupRepository;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private NetworkCardDriver networkCardDriver;
    @Autowired
    private IpRepository ipRepository;
    @Autowired
    private IpPoolRepository ipPoolRepository;
    @Autowired
    private ResourceTaskNotice resourceTaskNotice;

    @Override
    public BocloudResult operations(NetworkCardOperationParams params, RequestContext context) {
        if (null == params) {
            return ResultTools.formatErrResult();
        }
        String action = params.getAction();
        switch (action) {
            case "addSgroup":
                return addSgroup(params, context);
            case "removeSgroup":
                return removeSgroup(params, context);

            default:
                return new BocloudResult<Void>(false, "Unknown Operation！");
        }
    }

    private BocloudResult removeSgroup(NetworkCardOperationParams params, RequestContext context) {
        String path = CloudServer.class.getSimpleName() + "_" + params.getServerId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");

            CloudServer server = cloudServerRepository.query(params.getServerId());
            Assert.notNull(server, "云主机不存在");
            CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            //fc配置安全组时需要指定网卡
            Long networkCardId = params.getNetworkCardId();
            if (null == networkCardId || networkCardId == 0) {
                return new BocloudResult<>(false, "参数格式错误");
            }
            NetworkCard networkCard = networkCardRepository.query(networkCardId);
            Assert.notNull(networkCard, "网卡不存在");
            if (!networkCard.getEnableSecurityGroup()
                    && (networkCard.getSecurityGroupId() == null
                    || networkCard.getSecurityGroupId() == 0)) {
                return new BocloudResult<>(false, "此网卡暂未绑定安全组，请重新选择！");
            }
            //默认一块网卡只有一个安全组，所以根据网卡查询安全组与云主机绑定信息
            ServerSgroup serverSgroup = serverSgroupRepository.listByNicId(networkCardId);
            Assert.notNull(serverSgroup, "暂不存在云主机网卡与安全组关系，请检查！");
            BocloudResult result = null;
            // 安全组验证
            result = serverDriver.leaveSGroup(networkCard.getUri(), null, vendor.getUuid(),
                    server.getRegionId());
            if (result.isFailed()) {
                return new BocloudResult<>(false, result.getMessage());
            }
            networkCard.setSecurityGroupId(null);
            //针对fc 网卡删除安全组，既然已经开启安全组，则不能关闭，只能将安全组id取消
            networkCard.setMenderId(context.getTarget());
            networkCard.setGmtModify(new Date());
            networkCardRepository.update(networkCard);
            serverSgroupRepository.removeByNicId(networkCardId);
            return new BocloudResult<>(true, "安全组移除成功");
        } catch (Exception e) {
            log.error("Get error message：", e);
            return new BocloudResult<>(false, "安全组移除失败");
        }
    }

    private BocloudResult addSgroup(NetworkCardOperationParams params, RequestContext context) {
        String path = CloudServer.class.getSimpleName() + "_" + params.getServerId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");

            CloudServer server = cloudServerRepository.query(params.getServerId());
            Assert.notNull(server, "云主机不存在");
            CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            // 安全组验证
            Long groupId = params.getGroupId();
            if (null == groupId || groupId == 0) {
                return new BocloudResult<>(false, "参数格式错误");
            }
            SecurityGroup group = securityGroupRepository.query(groupId);
            Assert.notNull(group, "安全组不存在");
            //fc配置安全组时需要指定网卡
            Long networkCardId = params.getNetworkCardId();
            if (null == networkCardId || networkCardId == 0) {
                return new BocloudResult<>(false, "参数格式错误");
            }
            NetworkCard networkCard = networkCardRepository.query(networkCardId);
            Assert.notNull(networkCard, "网卡不存在");
            if (networkCard.getEnableSecurityGroup()
                    && (networkCard.getSecurityGroupId() != null
                    && networkCard.getSecurityGroupId() != 0)
                    && networkCard.getSecurityGroupId().equals(group.getId())) {
                return new BocloudResult<>(false, "此网卡已经绑定此安全组，请重新选择！");
            }
            //默认一块网卡只有一个安全组，所以根据网卡查询安全组与云主机绑定信息
            ServerSgroup serverSgroup = serverSgroupRepository.listByNicId(networkCardId);
            if (null != serverSgroup) {
                if (serverSgroup.getGroupId().equals(group.getId())) {
                    return new BocloudResult<>(false, "该云主机网卡、安全组已经绑定");
                }
            }
            BocloudResult result = null;
            // 安全组验证
            result = serverDriver.joinSGroup(networkCard.getUri(), group.getGroupUuid(), vendor.getUuid(),
                    server.getRegionId());
            if (result.isFailed()) {
                return new BocloudResult<>(false, result.getMessage());
            }
            networkCard.setSecurityGroupId(groupId);
            networkCard.setEnableSecurityGroup(true);
            networkCard.setMenderId(context.getTarget());
            networkCard.setGmtModify(new Date());
            networkCardRepository.update(networkCard);
            if (null != serverSgroup) {
                serverSgroup.setGroupId(groupId);
                serverSgroupRepository.update(serverSgroup);
            } else {
                serverSgroupRepository.save(new ServerSgroup(server.getId(), groupId, networkCardId));
            }
            return new BocloudResult<>(true, "分配安全组成功");
        } catch (Exception e) {
            log.error("Get error message：", e);
            return new BocloudResult<>(false, "加入安全组失败");
        }
    }

    @Override
    public BocloudResult create(NetworkCard networkCard, RequestContext context) {
        try {
            Assert.notNull(networkCard, "参数格式有误");
            Long serverId = networkCard.getServerId();
            Long vendorId = networkCard.getVendorId();
            CloudServer cloudServer = cloudServerRepository.query(networkCard.getServerId());
            Assert.notNull(cloudServer, "云主机不存在");
            CloudVendor cloudVendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(cloudVendor, "平台信息不存在");
            PortGroup portGroup = portGroupRepository.query(networkCard.getPortGroupId());
            Assert.notNull(portGroup, "端口组信息不存在");
            SecurityGroup securityGroup = null;
            if (networkCard.getEnableSecurityGroup()
                    && networkCard.getSecurityGroupId() != null) {
                if (networkCard.getSecurityGroupId() == 0) {
                    return new BocloudResult<>(false, "选择安全组异常，请查看");
                }
                securityGroup = securityGroupRepository.query(networkCard.getSecurityGroupId());
                if (null == securityGroup) {
                    return new BocloudResult<>(false, "安全组信息不存在");
                }
            }
            SecurityGroup finalSecurityGroup = securityGroup;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String content = "网卡创建失败";
                    String operate = cloudVendor.getType().toLowerCase() + ".networkcard";
                    Boolean resultFlag = false;
                    try {
                        NetworkCardModel networkCardModel = JSON.parseObject(JSON.toJSONString(networkCard),
                                NetworkCardModel.class);
                        networkCardModel.setPortGroupUuid(portGroup.getGroupVal());
                        if (finalSecurityGroup != null) {
                            networkCardModel.setSecurityGroupUuid(finalSecurityGroup.getGroupUuid());
                        }
                        networkCardModel.setSequenceNum(networkCard.getNumber());
                        networkCardModel.setServerUri(cloudServer.getUri());
                        BocloudResult bocloudResult = networkCardDriver.create(cloudVendor.getUuid(), networkCardModel,
                                networkCard.getRegionId());
                        if (bocloudResult.isSuccess()) {
                            networkCardModel = JSON.parseObject(JSON.toJSONString(bocloudResult.getData()),
                                    NetworkCardModel.class);
                            BocloudResult serverDetail = serverDriver.detail(cloudServer.getUri(), cloudVendor.getUuid(), networkCard.getRegionId());
                            if (serverDetail.isSuccess()) {
                                ServerModel serverModel = JSONObject.parseObject(JSONObject.toJSONString(serverDetail.getData()), ServerModel.class);
                                List<NetworkCardModel> networkCards = serverModel.getNetworkCards();
                                NetworkCardModel finalNetworkCardModel = networkCardModel;
                                networkCards = networkCards.stream()
                                        .filter(nic -> nic.getNetworkcardUuid().equals(finalNetworkCardModel.getNetworkcardUuid())
                                        ).collect(Collectors.toList());
                                if (!CollectionUtils.isEmpty(networkCards)) {
                                    List<String> privateIps = new ArrayList<>();
                                    for (NetworkCardModel cardModel : networkCards) {
                                        NetworkCard card = new NetworkCard();
                                        card.setName(cardModel.getName());
                                        card.setNetworkcardUuid(cardModel.getNetworkcardUuid());
                                        card.setUri(cardModel.getUri());
                                        card.setMac(cardModel.getMac());
                                        card.setIpAddress(cardModel.getIpAddress());
                                        card.setType(cardModel.getType());
                                        card.setNumber(cardModel.getSequenceNum());
                                        card.setVringbuf(cardModel.getVringbuf());
                                        card.setQueues(cardModel.getQueues());
                                        card.setEnableSecurityGroup(cardModel.getEnableSecurityGroup());
                                        card.setVendorId(vendorId);
                                        card.setServerId(serverId);
                                        card.setCreatorId(context.getTarget());
                                        card.setGmtCreate(new Date());
                                        card.setRegionId(networkCard.getRegionId());
                                        card.setPortGroupId(portGroup.getId());
                                        card.setInstanceId(serverModel.getInstanceId());
                                        networkCardRepository.save(card);
                                        if (null != finalSecurityGroup) {
                                            card.setSecurityGroupId(finalSecurityGroup.getId());
                                            card.setEnableSecurityGroup(true);
                                            //加入云主机和安全组绑定关系
                                            ServerSgroup serverSgroup = new ServerSgroup(serverId, finalSecurityGroup.getId(), card.getId());
                                            serverSgroupRepository.save(serverSgroup);
                                            networkCardRepository.update(card);
                                        }
                                        privateIps.add(card.getIpAddress());
                                    }
                                    String oldPrivateIps = cloudServer.getPrivateIps();
                                    List<String> oldIps = JSONArray.parseArray(oldPrivateIps, String.class);
                                    oldIps.addAll(privateIps);
                                    cloudServer.setPrivateIps(JSONObject.toJSONString(oldIps));
                                    cloudServerRepository.update(cloudServer);
                                    //发送消息给cop
                                    resourceTaskNotice.sendLogicalMessage(cloudServer, ResourceTaskNotice.Action.UPDATE);
                                    UpdateIp(cloudServer);
                                    content = "网卡创建成功";
                                    resultFlag = true;
                                } else {
                                    content = "网卡创建成功,云主机查询网卡失败，请检查";
                                }
                            } else {
                                content = serverDetail.getMessage();
                            }
                        } else {
                            content = bocloudResult.getMessage();
                        }
                    } catch (Exception e) {
                        log.error("网卡创建失败 ： " + e);
                        content = e.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(resultFlag, content, operate,
                            MapTools.simpleMap(Common.VENDORID, vendorId), OperateResult.OperateCategory.ResourceEvent,
                            context));
                }
            }).start();
            return new BocloudResult<>(true, "申请网卡任务已下发...");
        } catch (Exception e) {
            log.error("create network fail:", e);
            return new BocloudResult<>(false, "创建失败");
        }
    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        String path = Network.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            NetworkCard networkCard = networkCardRepository.query(id);
            if (null == networkCard) {
                log.warn("network cart 不存在!");
                return new BocloudResult<>(false, "网卡不存在");
            }
            Long vendorId = networkCard.getVendorId();
            CloudVendor cloudVendor = cloudVendorRepository.query(vendorId);
            if (null == cloudVendor) {
                return new BocloudResult<>(false, "平台信息不存在");
            }
            BocloudResult bocloudResult = networkCardDriver.remove(cloudVendor.getUuid(),
                    networkCard.getUri(), networkCard.getRegionId());
            if (bocloudResult.isSuccess()) {
                networkCardRepository.remove(networkCard.getId(), context.getTarget());
                //更改云主机ip
                CloudServer cloudServer = cloudServerRepository.query(networkCard.getServerId());
                if (null != cloudServer) {
                    List<String> privateIps = new ArrayList<>();
                    List<NetworkCard> networkCards = networkCardRepository.listByServerId(cloudServer.getId());
                    for (NetworkCard card : networkCards) {
                        privateIps.add(card.getIpAddress());
                    }
                    cloudServer.setPrivateIps(JSONObject.toJSONString(privateIps));
                    cloudServerRepository.update(cloudServer);
                    //发送消息给cop
                    resourceTaskNotice.sendLogicalMessage(cloudServer, ResourceTaskNotice.Action.UPDATE);
                    //重置ip状态
                    ipRepository.makeFreeByTarget(cloudServer.getId(), "VM", cloudServer.getVendorId());
                    UpdateIp(cloudServer);
                }
                //安全组绑定关系
                ServerSgroup serverSgroup = serverSgroupRepository.listByNicId(id);
                if (null != serverSgroup) {
                    serverSgroupRepository.removeByNicId(id);
                }
                return new BocloudResult<>(true, "删除成功");
            } else {
                return new BocloudResult<>(false, bocloudResult.getMessage());
            }
        } catch (Exception e) {
            log.error("remove network fail:", e);
            return new BocloudResult<>(false, "删除失败");
        }
    }

    private void UpdateIp(CloudServer entity) {
        Map<String, Ip> ipMap = Optional
                .ofNullable(ipRepository.listByVendor(entity.getVendorId()))
                .map(ips -> ips.stream()
                        .collect(Collectors.toMap(ip -> ip.getIp(),
                                ip -> ip)))
                .orElse(Maps.newHashMap());

        if (entity.getIsTemplate()) {
            ipRepository.makeFreeByTarget(entity.getId(), "VM", entity.getVendorId());
        }
        if (!StringUtils.isEmpty(entity.getPrivateIps())) {
            List<String> vmIps = JSONObject.parseArray(entity.getPrivateIps(), String.class);
            for (String vmIp : vmIps) {
                if (vmIp.equals("0.0.0.0")) {
                    continue;
                }
                if (ipMap.containsKey(vmIp)) {
                    Ip ip = ipMap.get(vmIp);
                    ip.setCatalog("VM");
                    ip.setTarget(entity.getId());
                    ip.setVendorId(entity.getVendorId());
                    ip.setStatus(Ip.IpStatus.used);
                    ipRepository.update(ip);
                    ipMap.remove(vmIp);
                } else {
                    List<IpPool> pools = ipPoolRepository.listByIpVersion(null);
                    Ip ip = ipRepository.checkExist(vmIp, null, entity.getVendorId());
                    if (null == ip) {
                        ip = new Ip(vmIp, null, null, null, entity.getVendorId(), IDFactory.instance().uuid(),
                                null, Ip.IpStatus.used);
                        ip.setCatalog("VM");
                        ip.setTarget(entity.getId());
                        ipRepository.save(ip);
                    } else {
                        ip.setTarget(entity.getId());
                        ip.setStatus(Ip.IpStatus.used);
                        ip.setCatalog("VM");
                        ip.setVendorId(entity.getVendorId());
                        ipRepository.update(ip);
                    }
                }
            }
        }
    }

    @Override
    public BocloudResult<GridBean<NetworkCard>> 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 = networkCardRepository.count(params);
            List<NetworkCard> list = this.networkCardRepository.list(page, rows, params, sorter);
            for (NetworkCard networkCard : list) {
                if (null != networkCard.getPortGroupId()) {
                    Optional.ofNullable(portGroupRepository.query(networkCard.getPortGroupId())).map(PortGroup::getGroupVal).ifPresent(networkCard::setPortGroupUuid);
                    Optional.ofNullable(portGroupRepository.query(networkCard.getPortGroupId())).map(PortGroup::getName).ifPresent(networkCard::setPortGroupName);
                }
                if (null != networkCard.getSecurityGroupId()) {
                    Optional.ofNullable(securityGroupRepository.query(networkCard.getSecurityGroupId())).map(SecurityGroup::getGroupUuid).ifPresent(networkCard::setSecurityGroupUuid);
                }
            }
            GridBean<NetworkCard> gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            log.error("list NetworkCard error!", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

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