package com.bocloud.ims.service.transporter.vendor;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.common.LocationDriver;
import com.bocloud.cmp.model.SangforModel;
import com.bocloud.cmp.model.sangfor.SangForServerGroupModel;
import com.bocloud.cmp.model.sangfor.SangforFirewallRuleModel;
import com.bocloud.cmp.model.sangfor.SangforHostModel;
import com.bocloud.cmp.model.sangfor.SangforIfacesModel;
import com.bocloud.cmp.model.sangfor.SangforServerModel;
import com.bocloud.cmp.model.sangfor.SangforVirtualNetworkDeviceModel;
import com.bocloud.cmp.model.sangfor.SangforVolumeModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.Result;
import com.bocloud.common.utils.ListTool;
import com.bocloud.database.core.intf.impl.BasicGenericDao;
import com.bocloud.entity.bean.GenericEntity;
import com.bocloud.ims.entity.common.ServiceCategory;
import com.bocloud.ims.entity.common.ServiceSku;
import com.bocloud.ims.entity.params.SkuParams;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.internal.CosInternalService;
import com.bocloud.ims.service.transporter.ResourceTransporter;
import com.bocloud.ims.service.utils.BeanUtils;
import com.bocloud.ims.service.utils.SangforConvertUtils;
import com.bocloud.ims.service.utils.SangforSyncHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Component
public class SangforTransporter implements ResourceTransporter {

    @Autowired
    private LocationDriver locationDriver;
    @Autowired
    ServerRepository serverRepository;
    @Autowired
    CloudServerRepository cloudServerRepository;
    @Autowired
    BasicGenericDao basicGenericDao;
    @Autowired
    DataStoreRepository dataStoreRepository;
    @Autowired
    NetworkRepository networkRepository;
    @Autowired
    SangforServerLogRepository serverLogRepository;
    @Autowired
    SnapshotRepository snapshotRepository;
    @Autowired
    SangforIfaceRepository ifaceRepository;
    @Autowired
    VolumeRepository volumeRepository;
    @Autowired
    FirewallRuleRepository firewallRuleRepository;
    @Autowired
    CosInternalService cosInternalService;
    @Autowired
    SangforServerGroupRepository serverGroupRepository;
    @Autowired
    SangforBridgeInfoRepository bridgeInfoRepository;
    @Autowired
    SangforVlanGroupRepositpry vlanGroupRepositpry;

    @Override
    public Result transport(CloudVendor vendor, Long operator) {
        try {
            BocloudResult syncResult = locationDriver.sync(vendor.getUuid());
            if (syncResult.isFailed()){
                log.info("{}云平台同步失败",vendor.getName());
                return syncResult;
            }
            Long vendorId = vendor.getId();
            if (Objects.nonNull(syncResult.getData())){
                SangforModel model = null;
                String modelJson = JSONObject.toJSONString(syncResult.getData());
//                log.info("抽取到数据:\n{}", modelJson);
                model = JSONObject.parseObject(modelJson, SangforModel.class);
//                model = this.getTestData();

                log.info("主机数据同步开始...");
                syncHost(model.getHostModels(), vendorId);
                log.info("主机数据同步结束...");

                log.info("物理网络数据同步开始...");
                syncIface(model.getIfacesModels(), vendorId);
                log.info("物理网络数据同步结束...");

                log.info("存储数据同步开始...");
                syncDataStore(model.getVolumeModels(), vendorId);
                log.info("存储数据同步结束...");

                log.info("虚拟机分组数据同步开始...");
                syncServerGroup(model.getServerGroupModels(),vendorId);
                log.info("虚拟机分组数据同步结束...");

                log.info("虚拟机数据同步开始...");
                syncServer(model.getServerModels(), vendor);
                log.info("虚拟机数据同步结束...");

                log.info("网络数据同步开始...");
                syncNetwork(model.getVirtualNetworkDeviceModels(), vendorId);
                log.info("网络数据同步结束...");

                log.info("网络数据同步开始...");
                syncFirewallRule(model.getFirewallModels(), vendorId);
                log.info("网络数据同步结束...");


            }
            return new BocloudResult<>(true,"云平台【 " + vendor.getName() + " 】数据同步完成");
        } catch (Exception e) {
            log.error("云平台同步失败", e);
            return new BocloudResult(false, "云平台【 " + vendor.getName() + " 】数据同步失败:" + e.getMessage());
        }
    }

    private void syncServerGroup(List<SangForServerGroupModel> serverGroupModels, Long vendorId) throws Exception {
        if (serverGroupModels == null) return;
        List<SangforServerGroup> oldenFirewallList = serverGroupRepository.listByVid(vendorId);
        List<SangforServerGroup> newlyList = SangforConvertUtils.serverGroupConver(serverGroupModels);
        newlyList.forEach( e-> e.setVendorId(vendorId));

        SangforSyncHelper.build(newlyList,oldenFirewallList,SangforServerGroup::getUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
    }

    private void syncFirewallRule(List<SangforFirewallRuleModel> firewallModels, Long vendorId) throws Exception {
        if (firewallModels == null) return;
        List<FirewallRule> oldenFirewallList = firewallRuleRepository.listByVid(vendorId);
        List<FirewallRule> newlyList = SangforConvertUtils.firewallConver(firewallModels);
        newlyList.forEach( e-> e.setVendorId(vendorId));

        SangforSyncHelper.build(newlyList,oldenFirewallList,FirewallRule::getRuleUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();

    }

    private void syncIface(List<SangforIfacesModel> ifacesModels, Long vendorId) {
        if (ifacesModels == null) return;
        // 同步物理网络
        List<SangforIface> oldenifaceList = ifaceRepository.listByVid(vendorId);
        List<SangforIface> ifaces = SangforConvertUtils.ifaceConver(ifacesModels);
        ifaces.forEach(e -> {
            e.setVendorId( vendorId);
        });
        SangforSyncHelper.build(ifaces,oldenifaceList,SangforIface::getUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
    }

    private void syncNetwork(List<SangforVirtualNetworkDeviceModel> virtualNetworkDeviceModels, Long vendorId) {
        if (virtualNetworkDeviceModels == null) return;
        // 网络设备同步
        List<Network> oldenList = networkRepository.listByVid(vendorId);
        List<Network> newlyList = SangforConvertUtils.networkConver(virtualNetworkDeviceModels);
        newlyList.forEach( e->{
            e.setVendorId(vendorId);
        });
        SangforSyncHelper.build(newlyList,oldenList,Network::getGuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();

        Map<String, Long> networkMap = networkRepository.listByVid(vendorId).stream().collect(Collectors.toMap(Network::getGuid, Network::getId));
        // 物理出口详情同步（物理网口）
        List<SangforBridgeInfo> bridgeInfoOldList = bridgeInfoRepository.listByVid(vendorId);
        List<SangforBridgeInfo> bridgeInfoNewlyList =  SangforConvertUtils.bridgeInfoConver(virtualNetworkDeviceModels);
        bridgeInfoNewlyList.forEach(e ->{
            e.setVendorId(vendorId);
            String networkUuid = e.getNetworkUuid();
            e.setNetworkId(networkUuid == null ? null : networkMap.get(networkUuid));
        });
        SangforSyncHelper.build(bridgeInfoNewlyList,bridgeInfoOldList,SangforBridgeInfo::getAssembleUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
        // 物理出口详情同步（端口组）
        List<SangforVlanGroup> vlanGroupOldList = vlanGroupRepositpry.listByVid(vendorId);
        List<SangforVlanGroup> vlanGroupNewlyList =  SangforConvertUtils.vlanGroupConver(virtualNetworkDeviceModels);
        vlanGroupNewlyList.forEach(e ->{
            e.setVendorId(vendorId);
            String networkUuid = e.getNetworkUuid();
            e.setNetworkId(networkUuid == null ? null : networkMap.get(networkUuid));
        });
        SangforSyncHelper.build(vlanGroupNewlyList,vlanGroupOldList,SangforVlanGroup::getAssembleUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
    }

    private void syncDataStore(SangforVolumeModel volumeModels, Long vendorId) {
        if (volumeModels == null ) return;
        // 同步虚拟存储
        log.info("同步虚拟存储...");
        List<DataStore> dataStores = dataStoreRepository.listByVendor(vendorId);
        List<DataStore> oldenVSList = dataStores.stream().filter(e -> "VS".equals(e.getType())).collect(Collectors.toList());
        List<DataStore> newlyList = SangforConvertUtils.datastoreVSConver(volumeModels.getVirtual_storage());
        newlyList.forEach( e-> e.setVendorId(vendorId));
        SangforSyncHelper.build(newlyList,oldenVSList,DataStore::getGuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
        // 同步其他存储
        log.info("同步其他存储...");
        List<DataStore> oldenISCSIList = dataStores.stream().filter(e -> "OTHER".equals(e.getType())).collect(Collectors.toList());
        newlyList = SangforConvertUtils.datastoreISCSIConver(volumeModels.getIscsi());
        newlyList.forEach( e-> e.setVendorId(vendorId));
        SangforSyncHelper.build(newlyList,oldenISCSIList,DataStore::getGuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
        // 同步系统盘
        log.info("同步系统盘...");
        List<DataStore> oldenSDList = dataStores.stream().filter(e -> "SD".equals(e.getType())).collect(Collectors.toList());
        List<DataStore> newlySDList = SangforConvertUtils.datastoreSDConver(volumeModels.getSysDisk());
        newlySDList.forEach( e-> e.setVendorId(vendorId));
        SangforSyncHelper.build(newlySDList,oldenSDList,DataStore::getGuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
    }

    private void syncServer(List<SangforServerModel> serverModels, CloudVendor vendor) {
        Long vendorId = vendor.getId();
        if (serverModels == null) {
            return;
        }
        List<CloudServer> oldenList = cloudServerRepository.listByVid(vendorId);
        List<CloudServer> newlyList = SangforConvertUtils.serverConver(serverModels);
        // 云主机绑定服务类型和规格
        createServiceSku(newlyList, null, vendor);
        Map<String, Server> hostMap = serverRepository.listByVendor(vendorId).stream().collect(Collectors.toMap(Server::getUuid, Function.identity()));
        Map<String, Boolean> cloudServerRecycleMap = oldenList.stream()
                .filter(vm -> StringUtils.isNotEmpty(vm.getInstanceId()))
                .peek(vm -> {
                    if (vm.getRecycle() == null) {
                        vm.setRecycle(Boolean.FALSE);
                    }
                })
                .collect(Collectors.toMap(CloudServer::getInstanceId, CloudServer::getRecycle));

        newlyList.forEach( e->{
            e.setVendorId(vendorId);
            Server server = hostMap.get(e.getHost());
            e.setHostId(server == null ? null:server.getId());
            Boolean recycle = cloudServerRecycleMap.getOrDefault(e.getInstanceId(), false);
            e.setRecycle(recycle);
        });
        SangforSyncHelper.build(newlyList,oldenList,CloudServer::getInstanceId)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();

        Map<String, Long>  serverIdMap = cloudServerRepository.listByVid(vendorId).stream().collect(Collectors.toMap(CloudServer::getInstanceId, CloudServer::getId));
        Map<String, Long> dataStoreIdMap = dataStoreRepository.listByVendor(vendorId).stream().filter(e -> "VS".equals(e.getType()) || "OTHER".equals(e.getType())).collect(Collectors.toMap(DataStore::getGuid, DataStore::getId));
        // 虚拟机卷同步
        List<Volume> oldenVolumeList = volumeRepository.listByVendorId(vendorId);
        List<Volume> newlyVolumeList = SangforConvertUtils.volumeConver(serverModels);
        // 虚拟卷绑定服务类型和规格
        createServiceSku(null, newlyVolumeList, vendor);
        newlyVolumeList.forEach(e->{
            e.setVendorId(vendorId);
            e.setServerId(serverIdMap.get(e.getServerInstanceId()));
            e.setDataStoreId(dataStoreIdMap.get(e.getDataStoreUuid()));
            e.setBootVolume(true);
        });
        SangforSyncHelper.build(newlyVolumeList,oldenVolumeList,Volume::getVolumeUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();


//        // 虚拟机操作日志
//        log.info("虚拟机操作日志同步开始...");
//        Map<String, CloudServer>  serverMap = cloudServerRepository.listByVid(vendorId).stream().collect(Collectors.toMap(CloudServer::getInstanceId, Function.identity()));;
//        List<SangforServerLog> oldenLogList = serverLogRepository.listByVid(vendorId);
//        List<SangforServerLog> newlyLogList = null;
//        try {
//            newlyLogList= SangforConvertUtils.logConver(serverModels);
//        } catch (ParseException e) {
//            log.error("虚拟机日志同步失败",e);
//        }
//        newlyLogList.forEach( e -> {
//            e.setVendorId(vendorId);
//            CloudServer cloudServer = serverMap.get(e.getVm());
//            e.setServerId(cloudServer == null ? null : cloudServer.getId());
//        });
//        SangforSyncHelper.build(newlyLogList,oldenLogList,SangforServerLog::getGuuid)
//                .whenInsert(this::whenInsert)
//                .whenUpdate(this::whenUpdate)
//                .whenRemove(this::whenRemove)
//                .sync();
//        log.info("虚拟机操作日志同步结束...");

        // 虚拟机快照
        log.info("虚拟机快照同步开始...");
        List<Snapshot> oldenSnapshotList = snapshotRepository.listByVid(vendorId);
        List<Snapshot> newlySnapshotList = null;
        try {
            newlySnapshotList= SangforConvertUtils.snapshotConver(serverModels);
        } catch (ParseException e) {
            log.error("虚拟机快照同步失败",e);
        }
        newlySnapshotList.forEach( e ->{
            e.setVmId(serverIdMap.get(e.getVmInstanceId()));
            e.setVendorId(vendorId);
        });
        SangforSyncHelper.build(newlySnapshotList,oldenSnapshotList,Snapshot::getSnapshotUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
        log.info("虚拟机快照同步结束...");

    }

    private void syncHost(List<SangforHostModel> hostModels, Long vendorId) {
        if (hostModels == null) return;
        List<Server> oldenList = serverRepository.listByVendor(vendorId);
        List<Server> newlyList = SangforConvertUtils.hostConver(hostModels);
        newlyList.forEach( e -> {
            e.setVendorId(vendorId);
        });
        SangforSyncHelper.build(newlyList,oldenList,Server::getUuid)
                .whenInsert(this::whenInsert)
                .whenUpdate(this::whenUpdate)
                .whenRemove(this::whenRemove)
                .sync();
    }



    private <T> void whenUpdate(List<T> entities) {
        if (ListTool.isEmpty(entities))return;
        entities.forEach( e -> {
            if (!(e instanceof GenericEntity)){
                return;
            }
            GenericEntity genericEntity  = (GenericEntity)e;
            if (genericEntity.getGmtModify() == null){
                genericEntity.setGmtModify(new Date());
            }
//            if (genericEntity.getMenderId() == null){
//                genericEntity.setMenderId(1L);
//            }
        });
        basicGenericDao.batchUpdate(entities,entities.size());
    }

    private <T> void whenInsert(List <T> entities) {
        if (ListTool.isEmpty(entities))return;
        entities.forEach( e -> {
            if (!(e instanceof GenericEntity)){
                return;
            }
            GenericEntity genericEntity  = (GenericEntity)e;
            if (genericEntity.getGmtCreate() == null){
                genericEntity.setGmtCreate(new Date());
            }
            if (genericEntity.getGmtModify() == null){
                genericEntity.setGmtModify(new Date());
            }
            if (genericEntity.getCreatorId() == null){
                genericEntity.setCreatorId(1L);
            }
            if (genericEntity.getMenderId() == null){
                genericEntity.setMenderId(1L);
            }
            if (genericEntity.getDeleted() == null){
                genericEntity.setDeleted(false);
            }
        });
        basicGenericDao.batchSave(entities,entities.size());
    }

    private <T> void whenRemove(List<T> entities) {
        if (ListTool.isEmpty(entities)) return;
        entities.forEach( e -> {
            if (!(e instanceof GenericEntity)){
                return;
            }
            GenericEntity genericEntity  = (GenericEntity)e ;
            if (genericEntity.getMenderId() == null){
                genericEntity.setMenderId(1L);
            }
            if (genericEntity.getGmtModify() == null){
                genericEntity.setGmtModify(new Date());
            }
            genericEntity.setDeleted(true);
        });
        basicGenericDao.batchUpdate(entities, entities.size());
    }

    public static SangforModel getTestData(){
        SangforModel model = new SangforModel();
        SangforModel hostModels = JSONObject.parseObject(DataUtil.hostModels, SangforModel.class);
        SangforModel serverModels = JSONObject.parseObject(DataUtil.serverModels, SangforModel.class);
        SangforModel volumeModels = JSONObject.parseObject(DataUtil.volumeModels, SangforModel.class);
        SangforModel virtualNetworkDeviceModels = JSONObject.parseObject(DataUtil.virtualNetworkDeviceModels, SangforModel.class);
        BeanUtils.copyPropertiesIgnoreNull(hostModels,model);
        BeanUtils.copyPropertiesIgnoreNull(serverModels,model);
        BeanUtils.copyPropertiesIgnoreNull(volumeModels,model);
        BeanUtils.copyPropertiesIgnoreNull(virtualNetworkDeviceModels,model);
        return model;
    }

    /**
     * 自动创建平台规格
     * @param cloudServers
     * @param volumes
     * @param vendor
     */
    public void createServiceSku(List<CloudServer> cloudServers, List<Volume> volumes, CloudVendor vendor) {
        try {
            String serviceCode;
            String spec = null;
            String code = null;
            ServiceSku sku;
            if (!CollectionUtils.isEmpty(cloudServers)) {
                serviceCode = vendor.getType().toLowerCase() + ".standard.server";
                List<ServiceCategory> serviceCategories = cosInternalService.listServiceCategories(serviceCode);
                Assert.notNull(serviceCategories, "不存在对应的服务类型");
                Assert.isTrue(serviceCategories.size() == 1, "服务类型只允许存在一种");
                ServiceCategory serviceCategory = serviceCategories.get(0);
                Long categoryId = serviceCategory.getId();

                SkuParams skuParams = new SkuParams();
                for (CloudServer cloudServer : cloudServers) {
                    int currentCpu = cloudServer.getCpu();
                    int currentMem = cloudServer.getMemory().intValue();
                    spec = "[{\"unit\":\"核\",\"specName\":\"cpu\",\"displayName\":\"CPU\",\"specValue\":\"" + currentCpu + "\"},{\"unit\":\"GB\",\"specName\":\"memory\",\"displayName\":\"内存\",\"specValue\":\"" + currentMem + "\"}]";
                    code = "standard." + currentCpu + "C" + currentMem + "G";

                    skuParams.setCatalogId(1L);
                    skuParams.setServiceCode(serviceCode);
                    skuParams.setCategoryId(categoryId);
                    skuParams.setSpec(spec);
                    skuParams.setCode(code);
                    sku = cosInternalService.createServiceSku(skuParams);
                    Assert.notNull(sku, "对应规格不存在, 云主机名称为:" + cloudServer.getName());
                    cloudServer.setCategoryId(categoryId);
                    cloudServer.setSkuId(sku.getId());
                }
            }
            if (!CollectionUtils.isEmpty(volumes)) {
                serviceCode = vendor.getType().toLowerCase() + ".standard.volume";
                List<ServiceCategory> serviceCategories = cosInternalService.listServiceCategories(serviceCode);
                Assert.notNull(serviceCategories, "不存在对应的服务类型");
                Assert.isTrue(serviceCategories.size() == 1, "服务类型只允许存在一种");
                ServiceCategory serviceCategory = serviceCategories.get(0);
                Long categoryId = serviceCategory.getId();

                SkuParams skuParams = new SkuParams();
                for (Volume volume : volumes) {
                    int currentDisk = volume.getSize();
                    spec = "[{\"unit\":\"GB\",\"specName\":\"disk\",\"displayName\":\"磁盘\",\"specValue\":\"" + currentDisk + "\"}]";
                    code = "standard." + currentDisk + "G";

                    skuParams.setCatalogId(2L);
                    skuParams.setServiceCode(serviceCode);
                    skuParams.setCategoryId(categoryId);
                    skuParams.setSpec(spec);
                    skuParams.setCode(code);
                    sku = cosInternalService.createServiceSku(skuParams);
                    Assert.notNull(sku, "对应规格不存在, 磁盘容量为:" + volume.getSize());
                    volume.setCategoryId(categoryId);
                    volume.setSkuId(sku.getId());
                }

            }
        } catch (Exception e) {
            log.error("深信服同步过程中自动创建资源规格失败",e);
        }
    }

    public static void main(String[] args) {
        SangforModel testData = getTestData();
        System.out.println("--");
    }
}
