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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.groups.ResourceGroupDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.ResourceGroupModel;
import com.bocloud.cmp.params.azure.UserAssignParams;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.entity.resource.azure.slb.Slb;
import com.bocloud.ims.interfaces.resource.ResourceGroupService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.repository.resource.azure.AvailabilitySetRepository;
import com.bocloud.ims.repository.resource.azure.account.BatchAccountRepository;
import com.bocloud.ims.repository.resource.azure.account.StorageAccountRepository;
import com.bocloud.ims.repository.resource.azure.db.CloudDatabaseServerRepository;
import com.bocloud.ims.repository.resource.azure.network.PublicIPAddressRepository;
import com.bocloud.ims.repository.resource.azure.network.RouterTableRepository;
import com.bocloud.ims.repository.resource.azure.redis.CloudRedisRepository;
import com.bocloud.ims.repository.resource.azure.slb.SlbRepository;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Company: 博云 www.bocloud.com
 * @ClassName: AzureResourceGroupsServiceImpl
 * @Description:
 * @author: zhenpingLi/李振平
 * @date: 2019/8/8 13:44
 */
@Service("resourceGroupService")
public class ResourceGroupServiceImpl implements ResourceGroupService {

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

    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private ResourceGroupRepository resourceGroupRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ResourceGroupDriver resourceGroupDriver;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private NetworkInterfaceRepository networkInterfaceRepository;
    @Autowired
    private VpcRepository vpcRepository;
    @Autowired
    private SubnetRepository subnetRepository;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private VolumeRepository volumeRepository;
    @Autowired
    private SecurityGroupRepository securityGroupRepository;
    @Autowired
    private SecurityGroupRuleRepository securityGroupRuleRepository;
    @Autowired
    private BatchAccountRepository batchAccountRepository;
    @Autowired
    private StorageAccountRepository storageAccountRepository;
    @Autowired
    private SnapshotRepository snapshotRepository;
    @Autowired
    private ImageRepository imageRepository;
    @Autowired
    private KeypairRepository keypairRepository;
    @Autowired
    private SlbRepository slbRepository;
    @Autowired
    private AvailabilitySetRepository availabilitySetRepository;
    @Autowired
    private PublicIPAddressRepository publicIPAddressRepository;
    @Autowired
    private CloudRedisRepository cloudRedisRepository;
    @Autowired
    private CloudDatabaseServerRepository cloudDatabaseServerRepository;
    @Autowired
    private RouterTableRepository routerTableRepository;
    @Autowired
    private ResourceTaskNotice resourceTaskNotice;

    @Override
    public BocloudResult<GridBean<ResourceGroup>> list(Pager pager) {
        try {
            int page = pager.getPage();
            int rows = pager.getRows();
            List<Param> params = pager.getParams();
            Map<String, String> sorter = pager.getSorter();
            boolean simple = pager.getSimple();

            GridBean gridBean = null;
            if (null == params) {
                params = new ArrayList<>();
            }
            if (null == sorter) {
                sorter = Maps.newHashMap();
            }
            sorter.put("gmtCreate", Common.ONE);
            List<ResourceGroup> list = resourceGroupRepository.list(page, rows, params, sorter);
            for (ResourceGroup resourceGroup : list) {
                CloudVendor cloudVendor = cloudVendorRepository.query(resourceGroup.getVendorId());
                if (null != cloudVendor) {
                    resourceGroup.setVendorName(cloudVendor.getName());
                }
            }
            int total = resourceGroupRepository.count(params);
            gridBean = GridHelper.getBean(page, rows, total, list);
            return new BocloudResult(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list azureResourceGroup failure:", e);
            return new BocloudResult(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<String> create(ResourceGroup resourceGroup, RequestContext context) {
        try {
            Assert.notNull(resourceGroup, "参数格式异常");
            Long vendorId = resourceGroup.getVendorId();
            String regionId = resourceGroup.getRegionId();
            String name = resourceGroup.getName();
            String displayName = resourceGroup.getDisplayName();

            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(vendor, "平台不存在");
            if (null != resourceGroupRepository.getByName(vendorId, name)) {
                return new BocloudResult<>(false, "资源组标识已存在");
            }

            ResourceGroupModel model = new ResourceGroupModel();
            model.setName(name);
            model.setRegionId(regionId);
            model.setDisplayName(displayName);
            BocloudResult result = resourceGroupDriver.create(vendor.getUuid(), regionId, model);
            if (result.isFailed()) {
                return result;
            }
            ResourceGroupModel resourceGroupModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
                    ResourceGroupModel.class);
            ResourceGroup group = new ResourceGroup();
            BeanUtils.copyProperties(resourceGroupModel, group);
            group.setVendorId(vendorId);
            group.setCreatorId(context.getTarget());
            resourceGroupRepository.save(group);
            return new BocloudResult<>(true, "创建资源组成功");
        } catch (Exception e) {
            logger.error("create azureResourceGroup error:", e);
            return new BocloudResult<>(false, "创建资源组失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<String> remove(Long id, RequestContext context) {
        String path = ResourceGroup.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            ResourceGroup entity = resourceGroupRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            CloudVendor cloudVendor = cloudVendorRepository.query(entity.getVendorId());
            Assert.notNull(cloudVendor, "平台验证失败");
            CloudProvider provider = CloudProvider.valueOf(cloudVendor.getType());
            taskExecutor.execute(() -> {
                String operate = cloudVendor.getType().toLowerCase() + ".resourceGroup";
                String message = "删除资源组失败";
                BocloudResult result = null;
                try {
                    switch (provider) {
                        case ALIYUN:
                            if ("default".equalsIgnoreCase(entity.getName())) {
                                resourceEventPublisher.send(new OperateResult(false, "默认资源组不允许删除", operate,
                                        MapTools.simpleMap("vendorId", cloudVendor.getId()), operateCategory, context));
                                return;
                            }
                            Boolean hasResource = checkExistResource(cloudVendor.getId(), entity.getRegionId(),
                                    entity.getResourceGroupUuid());
                            if (!hasResource) {
                                result = resourceGroupDriver.remove(cloudVendor.getUuid(), entity.getRegionId(),
                                        entity.getResourceGroupUuid());
                            }
                            break;
                        case AZURE:
                            result = resourceGroupDriver.remove(cloudVendor.getUuid(), entity.getRegionId(),
                                    entity.getName());
                        default:
                            break;
                    }
                    if (result.isSuccess()) {
                        message = "删除资源组成功";
                        switch (provider) {
                            case ALIYUN:
                                // 一般7天后才会删除
                                ResourceGroupModel model = (ResourceGroupModel) result.getData();
                                BeanUtils.copyProperties(model, entity);
                                resourceGroupRepository.update(entity);
                                break;
                            case AZURE:
                                logger.info("执行删除资源组......");
                                resourceGroupRepository.remove(id, context.getTarget());
                                logger.info("成功！");

                                logger.info("执行删除镜像......");
                                imageRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除网络接口......");
                                networkInterfaceRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除虚拟网络......");
                                List<Vpc> vpcs = vpcRepository.listByGroupId(cloudVendor.getId(), entity.getId());
                                for (Vpc vpc : vpcs) {
                                    subnetRepository.removeByNetworkId(vpc.getId(), context.getTarget());
                                    vpcRepository.delete(vpc.getId(), context.getTarget());
                                }
                                logger.info("成功！");

                                logger.info("执行删除云主机......");
                                List<CloudServer> cloudServers = cloudServerRepository.listByGroupId(cloudVendor.getId(),entity.getId());
                                cloudServerRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                //通知主机资源
                                resourceTaskNotice.sendLogicalMessage(cloudServers, ResourceTaskNotice.Action.DEL);
                                logger.info("成功！");

                                logger.info("执行删除硬盘......");
                                volumeRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除快照......");
                                snapshotRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除安全组......");
                                List<SecurityGroup> securityGroups = securityGroupRepository.listByGroupId(cloudVendor.getId(), entity.getId());
                                for (SecurityGroup securityGroup : securityGroups) {
                                    securityGroupRepository.remove(securityGroup.getId(), context.getTarget());
                                    securityGroupRuleRepository.removeBySgId(securityGroup.getId(), context.getTarget());
                                }
                                logger.info("成功！");

                                logger.info("执行删除Batch账户......");
                                batchAccountRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除StorageAccount账户......");
                                storageAccountRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除密钥......");
                                keypairRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除负载均衡器......");
                                List<Slb> slbs = slbRepository.listByGroupId(cloudVendor.getId(), entity.getId());
                                for (Slb slb : slbs) {
                                    slbRepository.remove(slb.getId(), context.getTarget());
                                }
                                logger.info("成功！");

                                logger.info("执行删除公共IP地址......");
                                publicIPAddressRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除可用性集......");
                                availabilitySetRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除Redis......");
                                cloudRedisRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除Mysql......");
                                cloudDatabaseServerRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");

                                logger.info("执行删除路由表......");
                                routerTableRepository.removeByGroupId(cloudVendor.getId(), context.getTarget(), entity.getId());
                                logger.info("成功！");
                                break;
                            default:
                                break;

                        }
                        resourceEventPublisher.send(new OperateResult(true, message, operate,
                                MapTools.simpleMap("vendorId", cloudVendor.getId()), operateCategory, context));
                    } else {
                        logger.error("删除资源组失败：" + result.getMessage());
                        message = result.getMessage();
                        resourceEventPublisher.send(new OperateResult(false, message, operate,
                                MapTools.simpleMap("vendorId", cloudVendor.getId()), operateCategory, context));
                    }
                } catch (Exception e) {
                    logger.error("remove azureResourceGroup fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", cloudVendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "资源组删除任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("remove azureResourceGroup fail:", e);
            return new BocloudResult<>(false, "删除资源组失败:" + e.getMessage());
        }
    }

    /***
     * 检查资源组下是否存在资源，当资源组内存在资源时，资源组无法被删除。
     *
     * @param vendorId
     * @param regionId
     * @param resourceGroupUuid
     * @return
     */
    private Boolean checkExistResource(Long vendorId, String regionId, String resourceGroupUuid) {
        try {
            List<CloudServer> serverList = cloudServerRepository.listByResourceGroupUuid(vendorId, regionId,
                    resourceGroupUuid);
            if (serverList.size() > 0) {
                return true;
            }
            List<Vpc> vpcList = vpcRepository.listByResourceGroupUuid(vendorId, regionId, resourceGroupUuid);
            return vpcList.size() > 0;
        } catch (Exception e) {
            logger.error("checkExistResource fail");
            return true;
        }
    }

    @Override
    public BocloudResult<ResourceGroup> detail(Long id) {
        try {
            ResourceGroup resourceGroup = resourceGroupRepository.query(id);
            CloudVendor cloudVendor = cloudVendorRepository.query(resourceGroup.getVendorId());
            if (null != cloudVendor) {
                resourceGroup.setVendorName(cloudVendor.getName());
            }
            return new BocloudResult<>(true, resourceGroup, "查询详情成功");
        } catch (Exception e) {
            logger.error("query azureResourceGroup detail fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<ResourceGroup> assign(UserAssignParams params, RequestContext context) {
        // 资源ID
        Long id = params.getId();
        // 租户用户ID
        Long userId = params.getUserId();
        // 租户用户所在项目
        Long projectId = params.getProjectId();
        Long tenantId = params.getTenantId();
        try {
            ResourceGroup entity = resourceGroupRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            entity.setTenantId(tenantId);
            entity.setProjectId(projectId);
            entity.setUserId(userId);
            resourceGroupRepository.update(entity);
            return new BocloudResult<>(true, entity, "操作成功");
        } catch (Exception e) {
            logger.error("assign ResourceGroup  fail:", e);
            return new BocloudResult<>(false, "操作失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> assignBatch(UserAssignParams params, RequestContext context) {
        Long userId = params.getUserId();
        // 租户用户所在项目
        Long projectId = params.getProjectId();
        Long tenantId = params.getTenantId();
        List<Long> ids = params.getIds();
        for (Long id : ids) {
            UserAssignParams newParams = new UserAssignParams();
            newParams.setId(id);
            newParams.setUserId(userId);
            newParams.setTenantId(tenantId);
            newParams.setProjectId(projectId);
            this.assign(newParams, context);
        }
        return new BocloudResult<>(true, "操作成功");
    }

}
