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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.azure.slb.FrontEndDriver;
import com.bocloud.cmp.model.azure.slb.FrontEndModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.enums.azure.AzureIpRelationType;
import com.bocloud.ims.common.enums.azure.AzureTaskStatus;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Subnet;
import com.bocloud.ims.entity.resource.Vpc;
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.BackEnd;
import com.bocloud.ims.entity.resource.azure.slb.FrontEnd;
import com.bocloud.ims.entity.resource.azure.slb.Slb;
import com.bocloud.ims.interfaces.resource.azure.slb.FrontEndService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.SubnetRepository;
import com.bocloud.ims.repository.resource.VpcRepository;
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.repository.resource.azure.slb.SlbRepository;
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 org.springframework.util.ObjectUtils;

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

@Service("frontEndService")
public class FrontEndServiceImpl implements FrontEndService {

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

    private static Logger logger = LoggerFactory.getLogger(FrontEndServiceImpl.class);
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private FrontEndRepository frontEndRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private FrontEndDriver frontEndDriver;
    @Autowired
    private PublicIPAddressRepository publicIPAddressRepository;
    @Autowired
    private SlbRepository slbRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private ResourcePublicIPAddressRelationRepository resourcePublicIPAddressRelationRepository;
    @Autowired
    private VpcRepository vpcRepository;
    @Autowired
    private SubnetRepository subnetRepository;

    @Override
    public BocloudResult<GridBean<FrontEnd>> 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<FrontEnd> list = frontEndRepository.list(page, rows, params, sorter);
            int total = frontEndRepository.count(params);
            GridBean<FrontEnd> gridBean = new GridBean<>(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list frontend failure:", e);
            return new BocloudResult<>(false, "查询失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> create(FrontEnd entityParams, RequestContext context) {
        try {
            Slb slb = slbRepository.query(entityParams.getSlbId());
            Assert.notNull(slb, "负载均衡不存在");
            CloudVendor vendor = cloudVendorRepository.query(slb.getVendorId());
            Assert.notNull(vendor, "平台不存在");
            if (null != frontEndRepository.getByName(entityParams.getVendorId(), slb.getId(), entityParams.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            FrontEndModel model = new FrontEndModel();
            BeanUtils.copyProperties(entityParams, model);
            model.setSlbUuid(slb.getUuid());
            if (entityParams.getFrontEndType().equalsIgnoreCase("INTERNAL")) {
                Vpc vpc = vpcRepository.query(entityParams.getVpcId());
                Assert.notNull(vpc, "vpc验证失败");
                Subnet subnet = subnetRepository.query(entityParams.getSubnetId());
                Assert.notNull(subnet, "子网验证失败");
                model.setVpcUuid(vpc.getVpcId());
                model.setSubnetName(subnet.getName());
                model.setSubnetUuid(subnet.getSubnetUuid());
            } else if (entityParams.getFrontEndType().equalsIgnoreCase("EXTERNAL")) {
                PublicIPAddress ipAddress = publicIPAddressRepository.query(entityParams.getPublicIpId());
                if (null == ipAddress) {
                    logger.error("get publicIPAddress failure");
                    return new BocloudResult(false, "公共IP不存在");
                }
                model.setPublicIpUuid(ipAddress.getUuid());
            }
            //增加状态
            slb.setStatus(AzureTaskStatus.IN_USE.name());
            slbRepository.update(slb);
            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".frontEnd";
                String message = "创建前端IP配置失败";
                BocloudResult result = frontEndDriver.create(vendor.getUuid(), slb.getRegionId(), model);
                try {
                    if (result.isSuccess()) {
                        message = "创建前端IP配置成功";
                        FrontEndModel newModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), FrontEndModel.class);
                        BeanUtils.copyProperties(newModel, entityParams);
                        entityParams.setCreatorId(context.getTarget());
                        entityParams.setVendorId(slb.getVendorId());

                        if (newModel.getFrontEndType().equalsIgnoreCase("INTERNAL")) {
                            Subnet subnet = subnetRepository.query(newModel.getSubnetUuid(), entityParams.getVendorId());
                            if (null != subnet) {
                                entityParams.setSubnetId(subnet.getId());
                            }
                            frontEndRepository.save(entityParams);
                        } else if (newModel.getFrontEndType().equalsIgnoreCase("EXTERNAL")) {
                            PublicIPAddress publicIPAddress = publicIPAddressRepository.getByUuid(entityParams.getVendorId(), newModel.getPublicIpUuid());
                            Optional.ofNullable(publicIPAddress).ifPresent(ipAddress -> entityParams.setPublicIpId(ipAddress.getId()));
                            frontEndRepository.save(entityParams);
                            if (!ObjectUtils.isEmpty(publicIPAddress)) {
                                ResourcePublicIPAddressRelation relation = new ResourcePublicIPAddressRelation();
                                relation.setIpId(publicIPAddress.getId());
                                relation.setResourceId(entityParams.getId());
                                relation.setResourceType(AzureIpRelationType.FRONTEND.name());
                                resourcePublicIPAddressRelationRepository.save(relation);
                                //重置状态
                                publicIPAddress.setStatus(AzureTaskStatus.IN_USE.name());
                                publicIPAddressRepository.update(publicIPAddress);
                            }
                        }
                    } else {
                        logger.error("创建前端IP配置失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap("vendorId",
                            vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    logger.error("create frontend fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap(
                            "vendorId", vendor.getId()), operateCategory, context));
                } finally {
                    //重置状态
                    slb.setStatus(AzureTaskStatus.AVAILABLE.name());
                    slbRepository.update(slb);
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("create frontend error:", e);
            return new BocloudResult<>(false, "创建前端IP配置失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {
        String path = BackEnd.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            FrontEnd entity = frontEndRepository.query(id);
            Assert.notNull(entity, "资源不存在");
            Slb slb = slbRepository.query(entity.getSlbId());
            Assert.notNull(slb, "负载均衡不存在");
            CloudVendor vendor = cloudVendorRepository.query(slb.getVendorId());
            Assert.notNull(vendor, "平台不存在");

            FrontEndModel model = new FrontEndModel();
            model.setName(entity.getName());
            model.setSlbUuid(slb.getUuid());
            //增加状态
            slb.setStatus(AzureTaskStatus.IN_USE.name());
            slbRepository.update(slb);
            taskExecutor.execute(() -> {
                String operate = vendor.getType().toLowerCase() + ".frontEnd";
                String message = "删除前端IP配置失败";

                BocloudResult result = frontEndDriver.remove(vendor.getUuid(),
                        slb.getRegionId(), model);
                try {
                    if (result.isSuccess()) {
                        message = "删除前端IP配置成功";
                        frontEndRepository.remove(id, context.getTarget());
                        List<ResourcePublicIPAddressRelation> addressRelations = resourcePublicIPAddressRelationRepository.listByResourceId(id, AzureIpRelationType.FRONTEND.name());
                        for (ResourcePublicIPAddressRelation relation : addressRelations) {
                            PublicIPAddress publicIPAddress = publicIPAddressRepository.query(relation.getIpId());
                            if (!ObjectUtils.isEmpty(publicIPAddress)) {
                                //重置状态
                                publicIPAddress.setStatus(AzureTaskStatus.AVAILABLE.name());
                                publicIPAddressRepository.update(publicIPAddress);
                            }
                        }
                        resourcePublicIPAddressRelationRepository.removeByResourceId(id, AzureIpRelationType.FRONTEND.name());
                    } else {
                        logger.error("删除前端IP配置失败：" + result.getMessage());
                        message = result.getMessage();
                    }
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap("vendorId",
                            vendor.getId()), operateCategory, context));
                } catch (Exception e) {
                    logger.error("remove frontend fail:", e);
                    resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, MapTools.simpleMap(
                            "vendorId", vendor.getId()), operateCategory, context));
                } finally {
                    //重置状态
                    slb.setStatus(AzureTaskStatus.AVAILABLE.name());
                    slbRepository.update(slb);
                }
            });
            return new BocloudResult<>(true, "任务已下发，正在执行...");
        } catch (Exception e) {
            logger.error("remove frontend fail:", e);
            return new BocloudResult<>(false, "删除前端IP配置失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<FrontEnd> detail(Long id, RequestContext context) {
        try {
            FrontEnd frontEnd = frontEndRepository.query(id);
            Assert.notNull(frontEnd, "资源不存在");
            return new BocloudResult<>(true, frontEnd, "查询详情成功");
        } catch (Exception e) {
            logger.error("query frontend detail fail:", e);
            return new BocloudResult<>(false, "查询详情失败:" + e.getMessage());
        }
    }
}
