package com.bocloud.ims.service.builder;

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.NetworkInterfaceDriver;
import com.bocloud.cmp.driver.storage.VolumeDriver;
import com.bocloud.cmp.entity.enums.ResourceCategory;
import com.bocloud.cmp.model.NetworkInterfaceModel;
import com.bocloud.cmp.model.ServerModel;
import com.bocloud.cmp.model.VolumeModel;
import com.bocloud.common.enums.ResourceCatalog;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.ListTool;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.PwdRandom;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: AwsServerBuilder
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2019/10/24 11:29
 */
public class AwsServerBuilder implements ServerBuilder {

    private CloudServerRepository cloudServerRepository;
    private FlavorRepository flavorRepository;
    private ImageRepository imageRepository;
    private VolumeRepository volumeRepository;
    private SecurityGroupRepository securityGroupRepository;
    private VpcRepository vpcRepository;
    private SubnetRepository subnetRepository;
    private KeypairRepository keypairRepository;
    private NetworkInterfaceRepository networkInterfaceRepository;
    private TagRepository tagRepository;

    private ServerKeypairRepository serverKeypairRepository;
    private ServerSgroupRepository serverSgroupRepository;

    private ResourceRepository resourceRepository;
    private ResourceEventRepository resourceEventRepository;

    private NetworkInterfaceDriver interfaceDriver;
    private ServerDriver serverDriver;
    private VolumeDriver volumeDriver;
    private CloudVendor vendor;
    private ServerModel model;
    private RequestContext context;

    public AwsServerBuilder() {
    }

    public AwsServerBuilder(CloudServerRepository cloudServerRepository, FlavorRepository flavorRepository,
                            ImageRepository imageRepository, VolumeRepository volumeRepository,
                            SecurityGroupRepository securityGroupRepository, VpcRepository vpcRepository,
                            SubnetRepository subnetRepository, KeypairRepository keypairRepository,
                            NetworkInterfaceRepository networkInterfaceRepository, ServerKeypairRepository serverKeypairRepository,
                            ServerSgroupRepository serverSgroupRepository,
                            ResourceRepository resourceRepository, ResourceEventRepository resourceEventRepository,
                            NetworkInterfaceDriver interfaceDriver, ServerDriver serverDriver, VolumeDriver volumeDriver,
                            TagRepository tagRepository, CloudVendor vendor, RequestContext context) {
        this.cloudServerRepository = cloudServerRepository;
        this.flavorRepository = flavorRepository;
        this.imageRepository = imageRepository;
        this.volumeRepository = volumeRepository;
        this.securityGroupRepository = securityGroupRepository;
        this.vpcRepository = vpcRepository;
        this.subnetRepository = subnetRepository;
        this.keypairRepository = keypairRepository;
        this.networkInterfaceRepository = networkInterfaceRepository;
        this.serverKeypairRepository = serverKeypairRepository;
        this.serverSgroupRepository = serverSgroupRepository;
        this.resourceRepository = resourceRepository;
        this.resourceEventRepository = resourceEventRepository;
        this.interfaceDriver = interfaceDriver;
        this.serverDriver = serverDriver;
        this.volumeDriver = volumeDriver;
        this.tagRepository = tagRepository;
        this.vendor = vendor;
        this.context = context;
    }

    @Override
    public BocloudResult<ServerModel> prepare(JSONObject object) throws Exception {
        /* 必要参数 */
        String name = object.getString(Common.NAME);// 云主机名
        String remark = object.getString("remark");// 描述
        model = new ServerModel(name, remark);

        // 设置地域
        String regionId = object.getString("regionId");
        if (null != regionId && !"".equals(regionId)) {
            model.setRegionId(regionId);
        }
        // 设置镜像
        Image image = imageRepository.query(object.getLong("imageId"));
        if (null == image) {
            return new BocloudResult<>(false, "镜像不存在");
        }
        model.setImageUuid(image.getImageUuid());
        model.setOsCategory(image.getOsCategory());
        model.setImageType(image.getImageType());

        // 设置安全组
        String groups = object.getString("groups");
        if (null != groups && !"".equals(groups)) {
            List<String> securityGroups = JSON.parseArray(groups, String.class);
            model.setSecurityGroups(securityGroups);
        }

        // 设置规格
        Long flavorId = object.getLong("flavorId");
        Flavor flavor = flavorRepository.query(flavorId);
        if (null == flavor) {
            return new BocloudResult<>(false, "规格不存在");
        }
        model.setFlavorUuid(flavor.getFlavorUuid());
        model.setCpu(flavor.getCpu());
        model.setMemory(flavor.getMemory());

        // 设置密钥
        Long keypairId = object.getLong("keypairId");
        Keypair keypair = keypairRepository.query(keypairId);
        if (null == keypair) {
            return new BocloudResult<>(false, "密钥不存在");
        }
        model.setKeypairName(keypair.getName());

        // 设置系统盘
        String diskDevices = object.getString("diskDevices");
        List<VolumeModel> volumeModels = JSONArray.parseArray(diskDevices, VolumeModel.class);

        // 当前挂载点
        int disk = 0;
        // String device = "/dev";
        // Pattern pLinux = Pattern.compile("^/dev/sd[b-l]$");
        // Pattern pWindows = Pattern.compile("^/dev/xvd[b-l]$");
        for (VolumeModel volumeModel : volumeModels) {
            // 系统盘/dev/xvda /dev/sda1 默认第一块为系统盘
            if ("root".equals(volumeModel.getCatalog())) {
                // device = volumeModel.getDevice();
                model.setSystemDiskSize(volumeModel.getSize());
                model.setSystemDiskCategory(volumeModel.getCategory());
            } else {
            	// 如果前端没设置磁盘大小，这里报空指针，暂时判空给前端返回提示语 后续专业人士可以直接调整
            	if(null == volumeModel.getSize()) {
            		return new BocloudResult<>(false, "磁盘大小不允许为空！");
            	}
                disk += volumeModel.getSize();
            }
        }
        model.setDiskDevices(volumeModels);
        model.setDisk(disk + model.getSystemDiskSize());

        // 设置网络接口
        String nics = object.getString("nics");
        if (null != nics && !"".equals(nics)) {
            List<NetworkInterfaceModel> networkInterfaces = JSONArray.parseArray(nics, NetworkInterfaceModel.class);
            model.setNetworkInterfaces(networkInterfaces);
        }

        // 设置子网
        Long subnetId = object.getLong("subnetId");
        if (null != subnetId && 0L != subnetId) {
            Subnet subnet = subnetRepository.query(subnetId);
            if (null == subnet) {
                return new BocloudResult<>(false, "子网不存在");
            }
            model.setSubnetName(subnet.getName());
            model.setSubnetUuid(subnet.getSubnetUuid());
        }
        // 子网Ip
        if (!StringUtils.isEmpty(object.getString("managerIp"))) {
            model.setManagerIp(object.getString("managerIp"));
        }
        // 云主机标签
        if (!StringUtils.isEmpty(object.getString("labelIds"))) {
            List<Long> labelIds = JSONArray.parseArray(object.getString("labelIds"), Long.class);
            model.setLabelIds(labelIds);
        }
        model.setTerminationProtected(object.getBooleanValue("terminationProtected"));
        return new BocloudResult<>(true, model, "success");
    }

    @Override
    public BocloudResult<CloudServer> builder(CloudServer cloudServer) throws Exception {
        // 创建云主机
        BocloudResult result = serverDriver.create(vendor.getUuid(), cloudServer.getRegionId(), model);
        // 操作记录
        Long userId = context.getTarget();
        String handler = "云主机创建";
        ResourceEvent event = null;
        if (result.isSuccess()) {
            event = new ResourceEvent(cloudServer.getId(), handler, "成功", userId, new Date(), "VM", vendor.getId(),
                    context.getCatalog());
            String content = JSONObject.toJSONString(result.getData());
            CloudServer server = JSONObject.parseObject(content, CloudServer.class);
            cloudServer.setName(server.getName());
            cloudServer.setRegionId(server.getRegionId());
            cloudServer.setInstanceId(server.getInstanceId());
            cloudServer.setStatus(server.getStatus());
            cloudServer.setPublicIps(server.getPublicIps());
            cloudServer.setPrivateIps(server.getPrivateIps());
            cloudServer.setVpcUuid(server.getVpcUuid());
            cloudServer.setOsName(server.getOsName());
            cloudServer.setOsCategory(server.getOsCategory());
            cloudServer.setOsVersion(server.getOsVersion());
            cloudServer.setZone(server.getZone());
            cloudServer.setSystemDiskCategory(model.getSystemDiskCategory());
            cloudServer.setSystemDiskSize(model.getSystemDiskSize());
            cloudServer.setSecurityGroupId(JSON.toJSONString(server.getSecurityGroups()));
            cloudServer.setSubnetUuid(server.getSubnetUuid());
            cloudServer.setFlavorUuid(model.getFlavorUuid());
            cloudServer.setCpu(model.getCpu());
            cloudServer.setMemory(model.getMemory());
            cloudServer.setImageUuid(model.getImageUuid());
            cloudServer.setDisk(model.getSystemDiskSize());
            cloudServer.setVpcUuid(server.getVpcUuid());

            // 新创建的云主机 秘钥 同步不回来 所以直接采用model中的秘钥数据
            String keypairName = model.getKeypairName();
            if (null != keypairName && !"".equals(keypairName)) {
                List<String> keyPairList = new ArrayList<>();
                keyPairList.add(keypairName);
                cloudServer.setKeypairName(JSON.toJSONString(keyPairList));
            }

            // 保存标签
            List<JSONObject> tags = server.getTags();
            if (!ListTool.isEmpty(tags)) {
                for (JSONObject object : tags) {
                    Tag tag = new Tag(object.getString("key"), object.getString("value"), cloudServer.getInstanceId());
                    tag.setResourceType("instance");
                    tag.setVendorId(vendor.getId());
                    tag.setRegionId(server.getRegionId());
                    tagRepository.save(tag);
                }
            }

            cloudServer.setRecycle(false);
            cloudServerRepository.update(cloudServer);

            // int disk = 0;
            List<Volume> volumeModels = server.getDiskDevices();
            if (!ListTool.isEmpty(volumeModels)) {
                for (Volume volume : volumeModels) {
                    BocloudResult volumeDetailResult = volumeDriver.detail(volume.getVolumeUuid(), vendor.getUuid(),
                            server.getRegionId());
                    if (volumeDetailResult.isSuccess()) {
                        String volumeContent = JSONObject.toJSONString(volumeDetailResult.getData());
                        Volume newVolume = JSONObject.parseObject(volumeContent, volume.getClass());
                        BeanUtils.copyProperties(newVolume, volume);
                    }
                    volume.setCreatorId(userId);
                    volume.setVendorId(vendor.getId());
                    volume.setVendorType(vendor.getType());
                    volume.setInstanceId(server.getInstanceId());
                    volume.setTenantId(cloudServer.getTenantId() == null ? 0L : cloudServer.getTenantId());
                    Long projectId = cloudServer.getProjectId();
                    if (null != projectId && projectId > 0) {
                        volume.setProjectId(projectId);
                    }
                    volume.setRegionId(server.getRegionId());
                    volume.setName(volume.getName());
                    volume.setServerId(cloudServer.getId());
                    // disk = volume.getSize() == null ? disk : disk +
                    // volume.getSize();
                    volumeRepository.save(volume);
                }
            }
            List<Volume> listByServerId = volumeRepository.listByServerId(cloudServer.getId());
            if (null != listByServerId && listByServerId.size() > 0) {
                cloudServer.setDiskDevices(listByServerId);
            }
            // 更改网络接口绑定关系
            List<NetworkInterfaceModel> nics = server.getNetworkInterfaces();
            for (NetworkInterfaceModel interfaceModel : nics) {
                NetworkInterface networkInterface = networkInterfaceRepository
                        .query(interfaceModel.getNetworkInterfaceUuid(), vendor.getId());
                if (ObjectUtils.isEmpty(networkInterface)) {
                    networkInterface = new NetworkInterface();
                    // 查询不到代表用户使用subnetId + groups创建的云主机
                    BocloudResult nicsResult = interfaceDriver.detail(vendor.getUuid(), server.getRegionId(),
                            interfaceModel.getNetworkInterfaceUuid());
                    if (nicsResult.isSuccess()) {
                        NetworkInterfaceModel networkInterfaceModel = JSONObject.parseObject(
                                JSONObject.toJSONString(nicsResult.getData()), NetworkInterfaceModel.class);
                        BeanUtils.copyProperties(networkInterfaceModel, networkInterface);
                        Subnet subnet = subnetRepository.query(networkInterfaceModel.getSubnetUuid(), vendor.getId());
                        networkInterface.setSubnetId(
                                Optional.ofNullable(subnet).map(subnetKey -> subnetKey.getId()).orElse(null));
                        networkInterface.setVpcId(
                                Optional.ofNullable(subnet).map(subnetKey -> subnetKey.getNetworkId()).orElse(null));
                        networkInterface.setSubnetName(
                                Optional.ofNullable(subnet).map(subnetKey -> subnetKey.getName()).orElse(null));
                        networkInterface.setServerId(cloudServer.getId());
                        networkInterface.setCreatorId(userId);
                        networkInterface.setVendorId(vendor.getId());
                        networkInterface.setRegionId(cloudServer.getRegionId());
                        networkInterface.setZone(cloudServer.getZone());
                        networkInterface.setSgroupUuid(Optional.ofNullable(networkInterfaceModel.getSecurityGroups())
                                .map(securityGroups -> JSON.toJSONString(securityGroups)).orElse(null));
                        networkInterface.setMacAddress(interfaceModel.getMacAddress());
                        networkInterfaceRepository.save(networkInterface);
                    }

                } else {
                    networkInterface.setDeviceIndex(interfaceModel.getDeviceIndex());
                    networkInterface.setServerId(cloudServer.getId());
                    networkInterface.setAttachmentUuid(interfaceModel.getAttachmentUuid());
                    networkInterface.setAttachedTime(interfaceModel.getAttachedTime());
                    networkInterface.setPrimaryPrivateIPAddress(interfaceModel.getPrimaryPrivateIPAddress());
                    networkInterface.setPublicIPAddress(interfaceModel.getPublicIPAddress());
                    networkInterface.setStatus(interfaceModel.getStatus());
                    networkInterface.setMacAddress(interfaceModel.getMacAddress());
                    networkInterfaceRepository.update(networkInterface);
                }
            }

            // 更改安全组绑定关系
            if (null != cloudServer.getSecurityGroups()) {
                List<String> secuList = JSONObject.parseArray(JSON.toJSONString(cloudServer.getSecurityGroups()),
                        String.class);
                for (String securityGroup : secuList) {
                    SecurityGroup group = securityGroupRepository.getByGroupId(securityGroup);
                    ServerSgroup serverSgroup = new ServerSgroup(cloudServer.getId(), group.getId());
                    serverSgroupRepository.save(serverSgroup);
                }
            }
            // 保存 秘钥
            updateServerKeyPair(cloudServer, vendor.getId());
            // 保存资源
            List<String> ips = Lists.newArrayList();
            if (!StringUtils.isEmpty(server.getPrivateIps())) {
                List<String> objects = JSONObject.parseArray(server.getPrivateIps(), String.class);
                for (String ip : objects) {
                    ips.add(ip);
                }
            }
            Resource resource = resourceRepository.query(ResourceCategory.Computer.name(),
                    ResourceCatalog.LOGICAL.name(), cloudServer.getId());
            if (null == resource) {
                resource = new Resource(ResourceCategory.Computer.name(), ResourceCatalog.LOGICAL.name(),
                        cloudServer.getId(), JSONObject.toJSONString(ips), cloudServer.getOsCategory(), vendor.getId());
                resource.setRegionId(cloudServer.getRegionId());
                resource.preSave(cloudServer.getName(), cloudServer.getStatus(), userId, context.getTenant(),
                        context.getProject());
                resource.setInventory(JSONObject.toJSONString(cloudServer));
                resourceRepository.save(resource);
            } else {
                resource.setInventory(JSONObject.toJSONString(cloudServer));
                resource.setStatus(cloudServer.getStatus());
                resource.setMenderId(userId);
                resource.setCreatorId(userId);
                resource.setGmtModify(new Date());
                resource.setGmtCreate(new Date());
                resource.setPrivateIps(JSONObject.toJSONString(ips));
                resource.setRegionId(cloudServer.getRegionId());
                resourceRepository.update(resource);
            }
        } else {
            event = new ResourceEvent(cloudServer.getId(), handler, "失败", userId, new Date(), "VM", vendor.getId(),
                    context.getCatalog());
            cloudServerRepository.remove(cloudServer.getId(), context.getTarget());
        }
        resourceEventRepository.save(event);
        return result.isSuccess() ? new BocloudResult<>(true, cloudServer, "success")
                : new BocloudResult<>(false, cloudServer, result.getMessage());
    }

    /**
     * 返回随机字符串，同时包含数字、大小写字母
     *
     * @param len 字符串长度，不能小于3
     * @return String 随机字符串
     */
    public static String randomStr(int len) {
        if (len < 3) {
            throw new IllegalArgumentException("字符串长度不能小于3");
        }
        // 数组，用于存放随机字符
        char[] chArr = new char[len];
        // 为了保证必须包含数字、大小写字母
        chArr[0] = (char) ('0' + PwdRandom.uniform(0, 10));
        chArr[1] = (char) ('A' + PwdRandom.uniform(0, 26));
        chArr[2] = (char) ('a' + PwdRandom.uniform(0, 26));

        char[] codes = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
                'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
                'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
                'z'};
        // charArr[3..len-1]随机生成codes中的字符
        for (int i = 3; i < len; i++) {
            chArr[i] = codes[PwdRandom.uniform(0, codes.length)];
        }

        // 将数组chArr随机排序
        for (int i = 0; i < len; i++) {
            int r = i + PwdRandom.uniform(len - i);
            char temp = chArr[i];
            chArr[i] = chArr[r];
            chArr[r] = temp;
        }

        return new String(chArr);
    }

    private void updateServerKeyPair(CloudServer server, Long vendorId) throws Exception {
        String keypairName = server.getKeypairName();
        if (null != keypairName && !"".equals(keypairName) && !"null".equalsIgnoreCase(keypairName)) {
            List<String> keyPairList = JSON.parseArray(keypairName, String.class);
            ServerKeypair serverKeypair = null;
            for (String keyName : keyPairList) {
                if (null != keyName && !"".equals(keyName) && !"null".equalsIgnoreCase(keyName)) {
                    serverKeypair = new ServerKeypair();
                    Keypair keypair = keypairRepository.getByName(keyName, vendorId);
                    serverKeypair.setKeypairId(keypair.getId());
                    serverKeypair.setServerId(server.getId());
                    serverKeypairRepository.save(serverKeypair);
                }
            }
        }
    }

}
