package com.bocloud.ims.service.resource;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
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.ObjectUtils;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.network.SecurityGroupDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.SecurityGroupBean;
import com.bocloud.cmp.model.SecurityGroupModel;
import com.bocloud.cmp.model.SecurityGroupRuleModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.GridBean;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.model.RequestContext.Catalog;
import com.bocloud.common.model.Sign;
import com.bocloud.common.model.SimpleBean;
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.CloudVendor;
import com.bocloud.ims.entity.resource.NetworkInterface;
import com.bocloud.ims.entity.resource.OSTenant;
import com.bocloud.ims.entity.resource.ResourceGroup;
import com.bocloud.ims.entity.resource.SecurityGroup;
import com.bocloud.ims.entity.resource.SecurityGroupRule;
import com.bocloud.ims.entity.resource.ServerSgroup;
import com.bocloud.ims.entity.resource.TenantProvider;
import com.bocloud.ims.entity.resource.Vpc;
import com.bocloud.ims.entity.resource.manageone.ManageOneAccount;
import com.bocloud.ims.interfaces.manageone.ManageOneAccountService;
import com.bocloud.ims.interfaces.resource.ResourcePoolService;
import com.bocloud.ims.interfaces.resource.SecurityGroupService;
import com.bocloud.ims.interfaces.resource.VendorProjectService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.DictionaryRepository;
import com.bocloud.ims.repository.resource.NetworkInterfaceRepository;
import com.bocloud.ims.repository.resource.OSTenantRepository;
import com.bocloud.ims.repository.resource.RegionRepository;
import com.bocloud.ims.repository.resource.ResourceGroupRepository;
import com.bocloud.ims.repository.resource.SecurityGroupRepository;
import com.bocloud.ims.repository.resource.SecurityGroupRuleRepository;
import com.bocloud.ims.repository.resource.ServerSgroupRepository;
import com.bocloud.ims.repository.resource.VpcRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 安全组Service接口实现
 *
 * @author weiwei
 */
@Service("securityGroupService")
public class SecurityGroupServiceImpl implements SecurityGroupService {

    private static Logger logger = LoggerFactory.getLogger(SecurityGroupServiceImpl.class);
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private SecurityGroupRepository securityGroupRepository;
    @Autowired
    private SecurityGroupRuleRepository securityGroupRuleRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private SecurityGroupDriver securityGroupDriver;
    @Autowired
    private ServerSgroupRepository serverSgroupRepository;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private RegionRepository regionRepository;
    @Autowired
    private VpcRepository vpcRepository;
    @Autowired
    private VendorProjectService vendorProjectService;
    @Autowired
    private OSTenantRepository osTenantRepository;
    @Autowired
    private ManageOneAccountService accountService;
    @Autowired
    private NetworkInterfaceRepository networkInterfaceRepository;
    @Autowired
    private ResourceGroupRepository resourceGroupRepository;
    @Autowired
    private ResourcePoolService resourcePoolService;
    @Autowired
    private DictionaryRepository dictionaryRepository;

    @Override
    public BocloudResult<GridBean<SecurityGroup>> list(Pager pager, RequestContext context) {
        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);
            sorter.put("gmtCreate", Common.ONE);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
            int total = 0;
            Catalog catalog = context.getCatalog();
            // 自服务查询
            if (Catalog.Tenant == catalog || Catalog.User == catalog) {
                String vendorType = "";
                Long vendorId = null;
                for (Param param : params) {
                    Map<String, Object> paramMap = param.getParam();
                    if (paramMap.containsKey("vendorType")) {
                        vendorType = String.valueOf(paramMap.get("vendorType"));
                        paramMap.remove("vendorType");
                    }
                    if (paramMap.containsKey("vendorId")) {
                        vendorId = Long.parseLong(paramMap.get("vendorId").toString());
                    }
                }
                Long tenantId = context.getTenant();
                params.add(new Param(MapTools.simpleMap("tenant_id", context.getTenant()), Sign.EQ));
                // 查询当前租户绑定的云平台id
                List<Long> tenantVendorIds = new ArrayList<>();
                tenantVendorIds.add(-1L);
                if (null != vendorId) {
                    tenantVendorIds.add(vendorId);
                } else {
                    tenantVendorIds.addAll(resourcePoolService.listTenantVendorIds(tenantId, vendorType));
                }
                List<String> tenantUuids = new ArrayList<>();
                CloudVendor cloudVendor = null;
                if (null != vendorId) {
                    cloudVendor = cloudVendorRepository.query(vendorId);
                }
                if ("OPENSTACK".equalsIgnoreCase(vendorType)
                    || (null != cloudVendor && "OPENSTACK".equalsIgnoreCase(cloudVendor.getType()))) {
                    // 如果是openstack 需要根据租户uuid查询资源
                    for (Long cloudVendorId : tenantVendorIds) {
                        tenantUuids.add(getOpTenantUuid(cloudVendorId, tenantId));
                    }
                }
                if (simple) {
                    total = securityGroupRepository.count(params, tenantVendorIds, tenantUuids, tenantId);
                    List<SimpleBean> beans =
                        securityGroupRepository.list(params, sorter, tenantVendorIds, tenantUuids, tenantId);
                    gridBean = new GridBean(1, 1, total, beans);
                } else {
                    total = securityGroupRepository.count(params, tenantVendorIds, tenantUuids, tenantId);
                    List<SecurityGroup> list = this.securityGroupRepository.list(page, rows, params, sorter,
                        tenantVendorIds, tenantUuids, tenantId);
                    gridBean = GridHelper.getBean(page, rows, total, list);
                }
            } else {
                if (simple) {
                    if (null == context.getTenant() || 0 == context.getTenant()) {
                        params.add(new Param(MapTools.simpleMap(Common.TENANTID, 0L), Sign.EQ));
                    }
                    List<SimpleBean> beans = securityGroupRepository.list(params, sorter);
                    total = securityGroupRepository.count(params);
                    gridBean = new GridBean(1, 1, total, beans);
                } else {
                    List<SecurityGroup> list = this.securityGroupRepository.list(page, rows, params, sorter);
                    total = securityGroupRepository.count(params);
                    gridBean = GridHelper.getBean(page, rows, total, list);
                }
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("Query SecurityGroup list fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
    }

    private Object generateUuids(List<String> uuids) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < uuids.size(); i++) {
            builder.append("'").append(uuids.get(i)).append("'").append(",");
        }
        builder.append(" '-1' ");
        return builder.toString();
    }

    public String getOpTenantUuid(Long vendorId, Long tenantId) {
        String tenantUuid = "";
        try {
            if (null == vendorId) {
                return tenantUuid;
            }
            CloudVendor cloudVendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(cloudVendor, "云平台信息不存在");
            if (CloudProvider.valueOf(cloudVendor.getType()) == CloudProvider.OPENSTACK) {
                TenantProvider tenantProvider = vendorProjectService.getAncestorProject(cloudVendor.getId(), tenantId);
                if (null != tenantProvider) {
                    tenantUuid = tenantProvider.getTenantUuid();
                }
            }
        } catch (Exception e) {
            logger.error("查询OpenStack租户uuid异常：" + e.getMessage());
        }
        return tenantUuid;
    }

    @Override
    public BocloudResult<SecurityGroup> create(SecurityGroup securityGroup, RequestContext context) {
        try {
            Assert.notNull(securityGroup, "参数格式错误");
            String name = securityGroup.getName();
            String remark = securityGroup.getRemark();
            String vpcUuid = securityGroup.getVpcUuid();
            SecurityGroup group = new SecurityGroup(name, remark);
            group.setCreatorId(context.getTarget());
            group.setMenderId(context.getTarget());
            group.setVendorId(securityGroup.getVendorId());
            group.setOsTenantName(securityGroup.getOsTenantName());
            String region = securityGroup.getRegionId();
            group.setRegionId(region);
            Long vendorId = group.getVendorId();
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(vendor, "云平台不存在");
            boolean exists = securityGroupRepository.isExists(group.getName(), vendorId);
            if (exists) {
                return new BocloudResult<SecurityGroup>(false, "安全组名称已存在");
            }
            // 如果是op 先验证当前租户是否当定op项目
            if ((Catalog.Tenant == context.getCatalog() || Catalog.User == context.getCatalog())
                && CloudProvider.OPENSTACK.name().equals(vendor.getType())) {
                TenantProvider tenantProvider =
                    vendorProjectService.getAncestorProject(vendor.getId(), context.getTenant());
                Assert.notNull(tenantProvider, "租户云平台认证信息不存在");
            }
            SecurityGroupModel model = new SecurityGroupModel(group.getName(), group.getRemark());
            if (!StringUtils.isEmpty(vpcUuid)) {
                Vpc vpc = vpcRepository.query(vpcUuid, vendorId);
                if (null != vpc) {
                    group.setVpcId(vpc.getId());
                }
                // 阿里云需要指定vpcUuid
                model.setVpcUuid(vpcUuid);
            }
            if (CloudProvider.AZURE.name().equals(vendor.getType())) {
                Long resourceGroupId = securityGroup.getResourceGroupId();
                ResourceGroup resourceGroup = resourceGroupRepository.query(resourceGroupId);
                if (ObjectUtils.isEmpty(resourceGroup)) {
                    logger.error("get resourceGroup failure");
                    return new BocloudResult<>(false, "资源组不能为空");
                }
                group.setResourceGroupId(resourceGroup.getId());
                model.setResourceGroupName(resourceGroup.getName());
                model.setRegionId(securityGroup.getRegionId());
            }
            if (CloudProvider.AWS.name().equals(vendor.getType())) {
                String awsGroupName = securityGroup.getAwsGroupName();
                model.setAwsGroupName(awsGroupName);
            }
            if (CloudProvider.ALIYUN.name().equals(vendor.getType())) {
                model.setCategory(securityGroup.getCategory());
                model.setSecurityGroupType(securityGroup.getSecurityGroupType());
                String resourceGroupUuid = securityGroup.getResourceGroupUuid();
                if (StringUtils.isNotBlank(resourceGroupUuid)) {
                    model.setResourceGroupUuid(resourceGroupUuid);
                } else {
                    ResourceGroup resourceGroup = resourceGroupRepository.getByName(group.getVendorId(), "default");
                    if (null != resourceGroup) {
                        model.setResourceGroupUuid(resourceGroup.getResourceGroupUuid());
                    }
                }
            }
            BocloudResult result = null;
            if (CloudProvider.MANAGEONE.name().equals(vendor.getType()) && null != context.getTenant()
                && 0L != context.getTenant()) {
                ManageOneAccount account = accountService.queryAccount(vendorId, context.getTenant());
                Assert.notNull(account, "认证失败！");
                result = securityGroupDriver.create(vendor.getUuid(), account.getUsername(), account.getPassword(),
                    account.getProjectId(), account.getDomainName(), model);
            } else {
                TenantProvider provider = vendorProjectService.getAncestorProject(vendor.getId(), context.getTenant());
                if (null == provider) {
                    result = securityGroupDriver.create(vendor.getUuid(), group.getOsTenantName(), group.getRegionId(),
                        model);
                } else {
                    result = securityGroupDriver.create(vendor.getUuid(), provider.getProjectName(),
                        group.getRegionId(), model);
                }
            }
            if (null == result || !result.isSuccess()) {
                return result;
            }
            model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), SecurityGroupModel.class);
            BeanUtils.copyProperties(model, group);
            group.preSave(context.getTarget(), context.getTarget(), vendorId, context.getTenant(),
                context.getProject());
            group.setTenantId(context.getTenant());
            group.setVpcUuid(vpcUuid);
            group.setRemark(remark);
            if ((Catalog.Tenant == context.getCatalog() || Catalog.User == context.getCatalog())
                    && CloudProvider.OPENSTACK.name().equals(vendor.getType())) {
            	group.setRegionId(region);
            	group.setZoneId(securityGroup.getZoneId());
            }
            securityGroupRepository.save(group);
            // 如果存在安全组规则 则更新
            List<SecurityGroupRuleModel> ruleModels = model.getModels();
            if (!ListTool.isEmpty(ruleModels)) {
                for (SecurityGroupRuleModel ruleModel : ruleModels) {
                    SecurityGroupRule rule = new SecurityGroupRule();
                    BeanUtils.copyProperties(ruleModel, rule);
                    rule.preSave(context.getTarget(), context.getTarget(), vendorId, group.getId(), context.getTenant(),
                        context.getProject());
                    rule.setDefaultRule(ruleModel.getDefaultRule());
                    securityGroupRuleRepository.save(rule);
                }
            }
            return new BocloudResult<SecurityGroup>(true, group, "创建成功!");
        } catch (Exception e) {
            logger.error("create failure:", e);
            return new BocloudResult<SecurityGroup>(false, "安全组创建失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<String> remove(Long id, RequestContext context) {
        String path = SecurityGroup.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            SecurityGroup group = securityGroupRepository.query(id);
            if (null == group) {
                logger.warn("SecurityGroup data not exist");
                return new BocloudResult<>(false, "数据不存在");
            }
            List<ServerSgroup> list = serverSgroupRepository.listByGroupId(id);
            if (!list.isEmpty()) {
                return new BocloudResult<>(false, "安全组无法删除：有虚拟机配置该安全组");
            }
            CloudVendor vendor = cloudVendorRepository.query(group.getVendorId());
            if (null == vendor) {
                logger.error("get cloudVendor and reg failure!");
                return new BocloudResult<>(false, "平台不存在失败");
            }
            BocloudResult result =
                securityGroupDriver.remove(group.getGroupUuid(), vendor.getUuid(), group.getRegionId());
            if (result.isFailed()) {
                return new BocloudResult<>(false, result.getMessage());
            }
            securityGroupRuleRepository.removeBySgId(id, context.getTarget());
            securityGroupRepository.remove(id, context.getTarget());
            return new BocloudResult<>(true, "安全组删除成功");
        } catch (Exception e) {
            logger.error("remove failure:", e);
            return new BocloudResult<>(false, "安全组删除失败");
        }
    }

    @Override
    public BocloudResult<SecurityGroup> detail(Long id) {
        try {
            SecurityGroup group = securityGroupRepository.query(id);
            CloudVendor vendor = cloudVendorRepository.query(group.getVendorId());
            if (null != vendor) {
                group.setVendorName(vendor.getName());
                group.setVendorType(vendor.getType());
                // azure
                CloudProvider type = CloudProvider.valueOf(vendor.getType());
                if (CloudProvider.AZURE == type) {
                    List<NetworkInterface> interfaces =
                        networkInterfaceRepository.listBySgId(vendor.getId(), group.getId());
                    if (!CollectionUtils.isEmpty(interfaces)) {
                        Optional.ofNullable(interfaces)
                            .ifPresent(networkInterfaces -> group.setNetworkInterfaceNames(StringUtils
                                .join(networkInterfaces.stream().map(networkInterface -> networkInterface.getName())
                                    .collect(Collectors.toList()), ",")));
                    }
                }
                if (CloudProvider.OPENSTACK == type) {
                	String regionName = dictionaryRepository.getNameByValue(group.getRegionId());
                	group.setRegionName(regionName);
                	String zoneName = dictionaryRepository.getNameByValue(group.getZoneId());
                	group.setZoneName(zoneName);
                }
            }
            if (!StringUtils.isEmpty(group.getTenantUuid())) {
                OSTenant osTenant = osTenantRepository.getByTenantUuid(group.getTenantUuid(), group.getVendorId());
                if (null != osTenant) {
                    group.setOsTenantName(osTenant.getName());
                }
            }
            return new BocloudResult<SecurityGroup>(true, group, "查询成功！");
        } catch (Exception e) {
            logger.error("get detail failure:", e);
            return new BocloudResult<SecurityGroup>(false, "查询失败!");
        }
    }

    @Override
    public BocloudResult<SecurityGroup> modify(SecurityGroupBean bean, Long userId) {
        String path = SecurityGroup.class.getSimpleName() + "_" + bean.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            SecurityGroup group = securityGroupRepository.query(bean.getId());
            if (null == group) {
                logger.warn("SecurityGroup data not exist");
                return new BocloudResult<SecurityGroup>(false, "数据不存在");
            }
            CloudVendor vendor = cloudVendorRepository.query(group.getVendorId());
            if (null == vendor) {
                logger.error("get cloudVendor and reg failure!");
                return new BocloudResult<SecurityGroup>(false, "平台不存在");
            }
            SecurityGroupModel model = new SecurityGroupModel(bean.getName(), group.getGroupUuid(), bean.getRemark());
            BocloudResult result = securityGroupDriver.modify(vendor.getUuid(), group.getRegionId(), model);
            if (result.isFailed()) {
                return new BocloudResult<SecurityGroup>(false, result.getMessage());
            }
            BeanUtils.copyProperties(bean, group);
            group.setMenderId(userId);
            securityGroupRepository.update(group);
            return new BocloudResult<SecurityGroup>(true, group, "安全组【" + group.getName() + "】修改成功");
        } catch (Exception e) {
            logger.error("Modify security group failure:", e);
            return new BocloudResult<SecurityGroup>(false, "安全组【" + bean.getName() + "】修改失败");
        }
    }

    @Override
    public BocloudResult<String> batchRemove(List<Long> idArray, RequestContext context) {
        try {
            if (ListTool.isEmpty(idArray)) {
                return new BocloudResult<>(false, "没有需要删除的安全组！");
            }
            SecurityGroup securityGroup = securityGroupRepository.query(idArray.get(0));
            if (null == securityGroup) {
                return new BocloudResult<>(false, "安全组数据异常！");
            }
            CloudVendor vendor = cloudVendorRepository.query(securityGroup.getVendorId());
            if (null == vendor) {
                return new BocloudResult<>(false, "平台信息异常！");
            }
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String operate = vendor.getType().toLowerCase() + ".vpc";
                    try {
                        List<BocloudResult> succList = Lists.newArrayList();
                        List<BocloudResult> failList = Lists.newArrayList();
                        for (Long id : idArray) {
                            BocloudResult bocloudResult = remove(id, context);
                            if (bocloudResult.isSuccess()) {
                                succList.add(bocloudResult);
                            } else {
                                failList.add(bocloudResult);
                            }
                        }
                        // 删除成功的数量
                        int success = succList.size();
                        // 删除失败的数量
                        int fail = failList.size();
                        String content = null;
                        if (fail == idArray.size()) {
                            content = "安全组批量删除失败，详情如下：\n";
                            for (BocloudResult bocloudResult : failList) {
                                content += bocloudResult.getMessage() + "\n";
                            }
                        } else if (success == idArray.size()) {
                            content = "安全组批量删除成功【" + success + "】个！";
                        } else {
                            content = "安全组批量删除成功【" + success + "】个,失败【" + fail + "】个，失败详情如下：\n";
                            for (int i = 0; i < failList.size(); i++) {
                                content += "【" + (i + 1) + "】" + failList.get(i).getMessage() + "\n";
                            }
                        }
                        resourceEventPublisher.send(new OperateResult(fail == idArray.size() ? false : true, content,
                            operate, MapTools.simpleMap("vendorId", vendor.getId()),
                            OperateResult.OperateCategory.ResourceEvent, context));
                    } catch (Exception e) {
                        logger.error("安全组批量删除异常：" + e.getMessage());
                        resourceEventPublisher.send(new OperateResult(false, "安全组批量删除异常：" + e.getMessage(), operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), OperateResult.OperateCategory.ResourceEvent,
                            context));
                    }
                }
            }).start();
            return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("batch remove security group fail:", e);
            return new BocloudResult<>(false, "批量删除任务下发失败");
        }
    }

}
