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


import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.network.PublicIPAddressDriver;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.azure.PublicIPAddressModel;
import com.bocloud.cmp.params.azure.UserAssignParams;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.JSONTools;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.enums.azure.AzureTaskStatus;
import com.bocloud.ims.entity.common.Project;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.ResourceGroup;
import com.bocloud.ims.entity.resource.azure.network.NicIpConfiguration;
import com.bocloud.ims.entity.resource.azure.network.PublicIPAddress;
import com.bocloud.ims.entity.resource.azure.network.ResourcePublicIPAddressRelation;
import com.bocloud.ims.entity.resource.azure.slb.FrontEnd;
import com.bocloud.ims.interfaces.resource.azure.network.PublicIPAddressService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ResourceGroupRepository;
import com.bocloud.ims.repository.resource.TenantCloudBillRepository;
import com.bocloud.ims.repository.resource.azure.network.NicIpConfigurationRepository;
import com.bocloud.ims.repository.resource.azure.network.PublicIPAddressRepository;
import com.bocloud.ims.repository.resource.azure.network.ResourcePublicIPAddressRelationRepository;
import com.bocloud.ims.repository.resource.azure.slb.FrontEndRepository;
import com.bocloud.ims.service.internal.SmsProjectService;
import com.bocloud.ims.service.internal.SmsTenantService;
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.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
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 org.springframework.util.ObjectUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service("publicIPAddressService")
public class PublicIPAddressServiceImpl implements PublicIPAddressService {

    private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;

    private static Logger logger = LoggerFactory.getLogger(PublicIPAddressServiceImpl.class);
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private PublicIPAddressRepository publicIPAddressRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ResourceGroupRepository resourceGroupRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private PublicIPAddressDriver publicIPAddressDriver;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private TenantCloudBillRepository cloudBillRepository;
    @Autowired
    private SmsTenantService smsTenantService;
    @Autowired
    private ResourcePublicIPAddressRelationRepository resourcePublicIPAddressRelationRepository;
    @Autowired
    private FrontEndRepository frontEndRepository;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private NicIpConfigurationRepository nicIpConfigurationRepository;

    @Override
    public BocloudResult<GridBean<PublicIPAddress>> 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());
        sorter.put("gmtCreate", Common.ONE);
        Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
        Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
        Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
        try {
            List<PublicIPAddress> list;
            int total;
            GridBean<PublicIPAddress> gridBean;
            Long tenantId = context.getTenant();
            Long projectId = context.getProject();

            // 为租户下的用户
            if (null != tenantId && 0L != tenantId) {
                List<Project> projects = smsProjectService.projectsByCondition(context.getTarget(),
                        context.getCatalog());
                List<Long> projectIds = new ArrayList<>();
                // 此时查询的是全部用户全部项目的信息
                if (null != projectId && 0L != projectId) {
                    projectIds.add(projectId);
                } else if (!ListTool.isEmpty(projects)) {
                    projects.forEach(project -> projectIds.add(project.getId()));
                } else {
                    projectIds.add(-1L);
                }

                total = this.publicIPAddressRepository.count(params, null, projectIds, null);
                list = this.publicIPAddressRepository.list(page, rows, params, sorter, null, projectIds, null);
            } else {
                list = publicIPAddressRepository.list(page, rows, params, sorter);
                total = publicIPAddressRepository.count(params);
            }
            //数据二次处理，找到eip使用者
            for (PublicIPAddress publicIPAddress : list) {
                ResourcePublicIPAddressRelation resourcePublicIPAddressRelation = resourcePublicIPAddressRelationRepository.queryByIpId(publicIPAddress.getId());
                if (ObjectUtils.isEmpty(resourcePublicIPAddressRelation)) {
                    continue;
                }
                switch (resourcePublicIPAddressRelation.getResourceType()) {
                    case "NICIPCONFIGURATION":
                        NicIpConfiguration nicIpConfiguration = nicIpConfigurationRepository.query(resourcePublicIPAddressRelation.getResourceId());
                        if (ObjectUtils.isEmpty(nicIpConfiguration)) {
                            logger.info("nicIpConfiguration不存在:{}", resourcePublicIPAddressRelation);
                            continue;
                        }
                        publicIPAddress.setResourceUserType(resourcePublicIPAddressRelation.getResourceType());
                        publicIPAddress.setResourceUserName(nicIpConfiguration.getNetworkInterfaceName());
                        publicIPAddress.setResourceUserId(nicIpConfiguration.getId());
                        break;
                    case "FRONTEND":
                        FrontEnd frontEnd = frontEndRepository.query(resourcePublicIPAddressRelation.getResourceId());
                        if (ObjectUtils.isEmpty(frontEnd)) {
                            continue;
                        }
                        publicIPAddress.setResourceUserType(resourcePublicIPAddressRelation.getResourceType());
                        publicIPAddress.setResourceUserName(frontEnd.getName());
                        publicIPAddress.setResourceUserId(frontEnd.getId());
                        break;
                    default:
                        logger.info("类型不正确:{}", resourcePublicIPAddressRelation.getResourceType());
                        break;
                }
            }
            gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list publicIp address failure:", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> create(PublicIPAddress entityParams, RequestContext context) {
        logger.info("eip创建接受参数：{}", entityParams);
        try {
            CloudVendor vendor = cloudVendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            if (null != publicIPAddressRepository.getByName(entityParams.getVendorId(), entityParams.getResourceGroupId(), entityParams.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            ResourceGroup resourceGroup = resourceGroupRepository.query(entityParams.getResourceGroupId());
            Assert.notNull(resourceGroup, "资源组不能为空");

            PublicIPAddressModel model = new PublicIPAddressModel();
            BeanUtils.copyProperties(entityParams, model);
            model.setResourceGroupName(resourceGroup.getName());

            //增加状态
            entityParams.setCreatorId(context.getTarget());
            entityParams.setResourceGroupId(resourceGroup.getId());
            entityParams.setTenantId(context.getTenant());
            entityParams.setProjectId(context.getProject());
            if (context.getCatalog() == RequestContext.Catalog.User) {
                entityParams.setUserId(context.getTarget());
            }
            entityParams.setStatus(AzureTaskStatus.BUILDING.name());
            publicIPAddressRepository.save(entityParams);

            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".publicIPAddress";
                String message;
                BocloudResult result = publicIPAddressDriver.create(vendor.getUuid(), entityParams.getRegionId(),
                        model);
                if (result.isSuccess()) {
                    message = "尊敬的客户，您好！您的弹性IP【" + entityParams.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建成功，请登录控制台查看及管理。";
                    PublicIPAddressModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
                            PublicIPAddressModel.class);
                    BeanUtils.copyProperties(newModel, entityParams);
                    entityParams.setStatus(AzureTaskStatus.AVAILABLE.name());
                    publicIPAddressRepository.update(entityParams);

                } else {
                    logger.error("创建弹性IP失败：" + result.getMessage());
                    message = "尊敬的客户，您好！您的弹性IP【" + entityParams.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建失败，失败信息：" + result.getMessage();
                    publicIPAddressRepository.remove(entityParams.getId(), context.getTarget());
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                //站内信推送
                NotifyMessage notifyMessage = new NotifyMessage(context, "弹性IP:", message, null,
                        true, false);
                amqpTemplate.convertAndSend("event.notice.queue", notifyMessage);
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("create publicIPAddress error:", e);
            return new BocloudResult<>(false, "创建弹性IP失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> createBatch(PublicIPAddress entityParams, Integer num, RequestContext context) {
        try {
            for (int i = 0; i < num; i++) {
                //数据重新打包并执行创建
                PublicIPAddress newEntityParams = new PublicIPAddress();
                BeanUtils.copyProperties(entityParams, newEntityParams);
                String newName = entityParams.getName() + "-" + RandomStringUtils.random(5);
                newEntityParams.setName(newName);
                this.create(newEntityParams, context);
            }
            return new BocloudResult<>(true, "批量创建开始执行！");
        } catch (Exception e) {
            logger.error("create publicIPAddress error:", e);
            return new BocloudResult<>(false, "批量创建弹性IP失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> modify(PublicIPAddress modifyParams, RequestContext context) {
        try {
            PublicIPAddress entity = publicIPAddressRepository.query(modifyParams.getId());
            Assert.notNull(entity, "资源不存在");
            CloudVendor vendor = cloudVendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            final String status = entity.getStatus();

            PublicIPAddressModel model = new PublicIPAddressModel();
            BeanUtils.copyProperties(modifyParams, model);
            model.setUuid(entity.getUuid());

            entity.setStatus(AzureTaskStatus.UPDATING.name());
            publicIPAddressRepository.update(entity);
            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".publicIPAddress";
                String message;
                BocloudResult result = publicIPAddressDriver.modify(vendor.getUuid(), entity.getRegionId(),
                        model);
                if (result.isSuccess()) {
                    message = "尊敬的客户，您好！您的弹性IP【" + entity.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "更新成功，请登录控制台查看及管理。";
                    PublicIPAddressModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
                            PublicIPAddressModel.class);
                    entity.setStatus(status);
                    entity.setIdleTimeoutInMinutes(newModel.getIdleTimeoutInMinutes());
                    entity.setFqdn(newModel.getFqdn());
                    publicIPAddressRepository.update(entity);
                } else {
                    logger.error("更新弹性IP失败：" + result.getMessage());
                    message = "尊敬的客户，您好！您的弹性IP【" + entity.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "更新失败，失败信息：" + result.getMessage();
                    entity.setStatus(status);
                    publicIPAddressRepository.update(entity);
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                        MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                //站内信推送
                NotifyMessage notifyMessage = new NotifyMessage(context, "弹性IP:", message, null,
                        true, false);
                amqpTemplate.convertAndSend("event.notice.queue", notifyMessage);
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("modify publicIPAddress error:", e);
            return new BocloudResult<>(false, "修改弹性IP失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {

        String path = PublicIPAddress.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            PublicIPAddress entity = publicIPAddressRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            CloudVendor vendor = cloudVendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台验证失败");
            entity.setStatus(AzureTaskStatus.DELETING.name());
            publicIPAddressRepository.update(entity);
            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".publicIPAddress";
                String message = "删除弹性IP失败";
                BocloudResult result = publicIPAddressDriver.remove(vendor.getUuid(), entity.getRegionId(),
                        entity.getUuid());
                try {
                    if (result.isSuccess()) {
                        message = "删除弹性IP成功";
                        publicIPAddressRepository.remove(id, context.getTarget());
                    } else {
                        logger.error("删除弹性IP失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                    //站内信推送
                    NotifyMessage notifyMessage = new NotifyMessage(context, "提示:", message, null,
                            true, false);
                    amqpTemplate.convertAndSend("event.notice.queue", notifyMessage);
                } catch (Exception e) {
                    logger.error("remove publicIPAddress fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate,
                            MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("remove publicIPAddress fail:", e);
            return new BocloudResult<>(false, "删除弹性IP失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<PublicIPAddress> detail(Long id, RequestContext context) {
        try {
            PublicIPAddress entity = publicIPAddressRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            ResourcePublicIPAddressRelation resourcePublicIPAddressRelation = resourcePublicIPAddressRelationRepository.queryByIpId(entity.getId());
            if (!ObjectUtils.isEmpty(resourcePublicIPAddressRelation)) {
                switch (resourcePublicIPAddressRelation.getResourceType()) {
                    case "NICIPCONFIGURATION":
                        NicIpConfiguration nicIpConfiguration = nicIpConfigurationRepository.query(resourcePublicIPAddressRelation.getResourceId());
                        if (!ObjectUtils.isEmpty(nicIpConfiguration)) {
                            entity.setResourceUserType(resourcePublicIPAddressRelation.getResourceType());
                            entity.setResourceUserName(nicIpConfiguration.getNetworkInterfaceName());
                            entity.setResourceUserId(nicIpConfiguration.getId());
                        }
                        break;
                    case "FRONTEND":
                        FrontEnd frontEnd = frontEndRepository.query(resourcePublicIPAddressRelation.getResourceId());
                        if (!ObjectUtils.isEmpty(frontEnd)) {
                            entity.setResourceUserType(resourcePublicIPAddressRelation.getResourceType());
                            entity.setResourceUserName(frontEnd.getName());
                            entity.setResourceUserId(frontEnd.getId());
                        }
                        break;
                    default:
                        logger.info("类型不正确:{}", resourcePublicIPAddressRelation.getResourceType());
                        break;
                }
            }
            return new BocloudResult<>(true, entity, "查询详情成功");
        } catch (Exception e) {
            logger.error("query publicIp address detail fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<List<PublicIPAddress>> listUnuse(Long vendorId, String regionId, RequestContext context) {
        try {
            List<PublicIPAddress> publicIPAddresses;
            Long tenantId = context.getTenant();
            Long projectId = context.getProject();
            // 为租户下的用户
            if (null != tenantId && 0L != tenantId) {
                List<Project> projects = smsProjectService.projectsByCondition(context.getTarget(),
                        context.getCatalog());
                List<Long> projectIds = new ArrayList<>();
                // 此时查询的是全部用户全部项目的信息
                if (null != projectId && 0L != projectId) {
                    projectIds.add(projectId);
                } else if (!ListTool.isEmpty(projects)) {
                    projects.forEach(project -> projectIds.add(project.getId()));
                } else {
                    projectIds.add(-1L);
                }
                publicIPAddresses = publicIPAddressRepository.listUnused(vendorId, regionId, null, projectIds);
            } else {
                publicIPAddresses = publicIPAddressRepository.listUnused(vendorId, regionId);
            }
            return new BocloudResult<>(true, publicIPAddresses, "查询成功");
        } catch (Exception e) {
            logger.error("listUnused publicIp address fail:", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> removeCourse(Long id, RequestContext context) {
        String path = PublicIPAddress.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            PublicIPAddress entity = publicIPAddressRepository.query(id);
            Assert.notNull(entity, "资源不存在");
            CloudVendor vendor = cloudVendorRepository.query(entity.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            BocloudResult result = publicIPAddressDriver.remove(vendor.getUuid(), entity.getRegionId(),
                    entity.getUuid());
            if (result.isFailed()) {
                logger.info("删除弹性IP失败！");
                return result;
            }
            logger.info("删除弹性IP成功！");
            publicIPAddressRepository.remove(id, context.getTarget());
            resourcePublicIPAddressRelationRepository.removeByIpId(id);
            return new BocloudResult<>(true, "删除弹性IP成功");
        } catch (Exception e) {
            logger.error("remove publicIPAddress fail:", e);
            return new BocloudResult<>(false, "删除弹性IP失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> assign(UserAssignParams params, RequestContext context) {
        // 资源ID
        Long id = params.getId();
        // 租户用户ID
        Long userId = params.getUserId();
        // 租户用户所在项目
        Long projectId = params.getProjectId();
        Long tenantId = params.getTenantId();
        try {
            PublicIPAddress entity = publicIPAddressRepository.query(id);
            Assert.notNull(entity, "数据不存在");
            entity.setTenantId(tenantId);
            entity.setProjectId(projectId);
            entity.setUserId(userId);
            publicIPAddressRepository.update(entity);
            return new BocloudResult(true, entity, "操作成功");
        } catch (Exception e) {
            logger.error("assign publicIPAddress  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, "操作成功");
    }

    @Override
    public BocloudResult vmApplyCreate(String params, RequestContext context) {
        JSONObject object = JSONTools.isJSONObj(params);
        PublicIPAddress entityParams = JSONObject.parseObject(object.getString("publicIPAddress"),
                PublicIPAddress.class);
        try {
            CloudVendor vendor = cloudVendorRepository.query(entityParams.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            if (null != publicIPAddressRepository.getByName(entityParams.getVendorId(), entityParams.getResourceGroupId(), entityParams.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            ResourceGroup resourceGroup = resourceGroupRepository.query(entityParams.getResourceGroupId());
            Assert.notNull(resourceGroup, "资源组不存在");
            PublicIPAddressModel model = new PublicIPAddressModel();
            BeanUtils.copyProperties(entityParams, model);
            model.setResourceGroupName(resourceGroup.getName());
            BocloudResult result = publicIPAddressDriver.create(vendor.getUuid(), entityParams.getRegionId(),
                    model);
            if (result.isSuccess()) {
                PublicIPAddressModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
                        PublicIPAddressModel.class);
                BeanUtils.copyProperties(newModel, entityParams);
                entityParams.setCreatorId(context.getTarget());
                entityParams.setResourceGroupId(resourceGroup.getId());
                entityParams.setTenantId(context.getTenant());
                entityParams.setProjectId(context.getProject());
                if (context.getCatalog() == RequestContext.Catalog.User) {
                    entityParams.setUserId(context.getTarget());
                }
                publicIPAddressRepository.save(entityParams);
            } else {
                logger.error("创建公共IP地址失败：" + result.getMessage());
            }

            return new BocloudResult<>(true, entityParams, "创建成功！");
        } catch (Exception e) {
            logger.error("create publicIPAddress error:", e);
            return new BocloudResult<>(false, "创建弹性IP失败:" + e.getMessage());
        }
    }


}
