package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.loadbalance.LbDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.LbListenerPolicyModel;
import com.bocloud.cmp.model.LbListenerRuleModel;
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.interfaces.resource.LbListenerPolicyService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.BeanUtils;
import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import software.amazon.awssdk.services.rekognition.model.Asset;

import java.util.*;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class LbListenerPolicyServiceImpl implements LbListenerPolicyService {
    private static Logger logger = LoggerFactory.getLogger(LbListenerRuleServiceImpl.class);

    @Autowired
    LbListenerPolicyRepository lbListenerPolicyRepository;
    @Autowired
    LbListenerRuleRepository lbListenerRuleRepository;
    @Autowired
    LbListenerRepository lbListenerRepository;
    @Autowired
    LbPoolRepository lbPoolRepository;
    @Autowired
    CloudVendorRepository cloudVendorRepository;
    @Autowired
    LbDriver lbDriver;
    @Autowired
    ThreadPoolTaskExecutor taskExecutor;


    @Override
    public BocloudResult<GridBean<LbListenerPolicy>> 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);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
            sorter.put("gmtCreate", Common.ONE);
            Long listenerId = null;
            Long vendorId = null;
            String regionId = null;
            for (Param param : params) {
                Map<String, Object> map = param.getParam();
                if (map.containsKey("listenerId")){
                    listenerId =  Long.valueOf(map.get("listenerId").toString());
                }
                if (map.containsKey("vendorId")){
                    vendorId =  Long.valueOf(map.get("vendorId").toString());
                }
                if (map.containsKey("regionId")){
                    regionId =  map.get("regionId").toString();
                }
            }
            int total = lbListenerPolicyRepository.count(params);
            if (simple) {
                if (0 == context.getTenant()) {
                    params.add(new Param(MapTools.simpleMap(Common.TENANTID, 0), Sign.EQ));
                }
                List<SimpleBean> beans = lbListenerPolicyRepository.list(params, sorter);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                List<LbListenerPolicy> list = lbListenerPolicyRepository.list(params);
                Map<String, LbListener> listenerMap = lbListenerRepository.listByVid(vendorId,regionId).stream().collect(Collectors.toMap(LbListener::getListenerUuid,Function.identity()));
                Map<String, String> poolMap = lbPoolRepository.listByVid(vendorId, regionId).stream().collect(Collectors.toMap(LbPool::getPoolUuid, LbPool::getName));
                Map<Long, List<LbListenerRule>> ruleMap = lbListenerRuleRepository.listByListenerId(listenerId).stream().collect(Collectors.groupingBy(LbListenerRule::getPolicyId));
                list.forEach( e-> {
                    e.setRules(ruleMap.get(e.getId()));
                    String redirectListenerUuid = e.getRedirectListenerUuid();
                    if (Objects.nonNull(redirectListenerUuid) && Objects.nonNull(listenerMap.get(redirectListenerUuid))){
                        e.setRedirectListenerName(listenerMap.get(redirectListenerUuid).getName());
                    }
                    String redirectPoolUuid = e.getRedirectPoolUuid();
                    if (Objects.nonNull(redirectPoolUuid)){
                        e.setRedirectPoolName(poolMap.get(redirectPoolUuid));
                    }
                });

                // 添加默认的转发策略
                LbListener listener = lbListenerRepository.query(listenerId);
                LbListenerPolicy defaultPolicy = new LbListenerPolicy();
                LbListenerRule defaultPolicyRule = new LbListenerRule();
                defaultPolicy.setAction("REDIRECT_TO_POOL");
                defaultPolicy.setRules(Collections.singletonList(defaultPolicyRule));
                defaultPolicy.setRedirectPoolUuid(listener.getDefaultPoolUuid());
                LbListener listener1 = listenerMap.get(listener.getDefaultPoolUuid());
                if(Objects.nonNull(listener1)){
                    defaultPolicy.setRedirectPoolName(listener1.getName());
                }
                defaultPolicy.setListenerId(listener.getId());
                defaultPolicy.setListenerUuid(listener.getListenerUuid());
                defaultPolicy.setProvisioningStatus("ACTIVE");
                defaultPolicy.setVendorId(vendorId);
                String protocol = listener.getProtocol();
                Integer port = listener.getPort();
                if (!StringUtils.isEmpty(protocol) && Objects.nonNull(port))
                defaultPolicyRule.setRuleValue(protocol + ":" + port);
                defaultPolicyRule.setProvisioningStatus("ACTIVE");
                list.add(defaultPolicy);
                gridBean = GridHelper.getBean(1, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list lb listener failure:", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult create(LbListenerPolicy lbListenerPolicy, RequestContext context) {
        try {
            Long vendorId = lbListenerPolicy.getVendorId();
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(vendor, "云平台不存在");
            Long listenerId = lbListenerPolicy.getListenerId();
            LbListener lbListener = lbListenerRepository.query(listenerId);
            Assert.notNull(lbListener, "监听器不存在不存在");

            switch (CloudProvider.valueOf(vendor.getType())) {
                case HUAWEI:
                case HCSO:
                    String action = lbListenerPolicy.getAction();
                    Assert.notNull(action,"动作不能为空");
                    String listenerUuid = lbListenerPolicy.getListenerUuid();
                    Assert.notNull(listenerUuid,"监听器不能为空");

                    LbListenerPolicyModel policyModel = new LbListenerPolicyModel();
                    BeanUtils.copyPropertiesIgnoreNull(lbListenerPolicy, policyModel);
                    List<LbListenerRule> rules = lbListenerPolicy.getRules();
                    ArrayList<LbListenerRuleModel> ruleModelsuleModels = new ArrayList<>();
                    for (LbListenerRule rule : rules) {
                        LbListenerRuleModel ruleModel = new LbListenerRuleModel();
                        BeanUtils.copyPropertiesIgnoreNull(rule, ruleModel);
                        ruleModelsuleModels.add(ruleModel);
                    }
                    policyModel.setRules(ruleModelsuleModels);
                    BocloudResult bocloudResult = lbDriver.createListenerPolicy(vendor.getUuid(), lbListener.getRegionId(), policyModel);
                    if(bocloudResult.isSuccess()){
                        String redirectListenerUuid = policyModel.getRedirectListenerUuid();
                        String redirectPoolUuid = policyModel.getRedirectPoolUuid();
                        LbListener redirectListener = null;
                        LbPool lbPool = null;

                        if (StringUtils.isNotEmpty(redirectListenerUuid)){
                            redirectListener = lbListenerRepository.queryByUuid(redirectListenerUuid,vendorId);
                        }
                        if (StringUtils.isNotEmpty(redirectPoolUuid)){
                            lbPool = lbPoolRepository.queryByUuid(redirectPoolUuid, vendorId);
                            lbPool.setListenerId(lbListener.getId());
                            lbPool.setListenerUuid(lbListener.getListenerUuid());
                            lbPoolRepository.update(lbPool);
                        }
                        policyModel = (LbListenerPolicyModel) bocloudResult.getData();
                        LbListenerPolicy lbListenerTarget = new LbListenerPolicy();
                        org.springframework.beans.BeanUtils.copyProperties(policyModel, lbListenerTarget);
                        lbListenerTarget.setListenerId(listenerId);
                        lbListenerTarget.setRedirectListenerId(redirectListener == null ? null : redirectListener.getId());
                        lbListenerTarget.setRedirectPoolId(lbPool == null ? null : lbPool.getId());
                        lbListenerTarget.setCreatorId(context.getTarget());
                        lbListenerTarget.setVendorId(vendorId);
                        lbListenerTarget.setGmtCreate(new Date());
                        lbListenerTarget.setRegionId(lbListener.getRegionId());
                        lbListenerPolicyRepository.save(lbListenerTarget);
                        bocloudResult = lbDriver.listListenerRule(vendor.getUuid(), policyModel.getPolicyUuid(), lbListener.getRegionId());
                        if (bocloudResult.isSuccess()) {
                            List<LbListenerRuleModel> ruleModelList = JSONArray.parseArray(JSONArray.toJSONString(bocloudResult.getData()), LbListenerRuleModel.class);
                            for (LbListenerRuleModel model : ruleModelList) {
                                LbListenerRule lbRuleTarget = new LbListenerRule();
                                org.springframework.beans.BeanUtils.copyProperties(model, lbRuleTarget);
                                lbRuleTarget.setPolicyId(lbListenerTarget.getId());
                                lbRuleTarget.setListenerId(lbListenerTarget.getListenerId());
                                lbRuleTarget.setCreatorId(context.getTarget());
                                lbRuleTarget.setVendorId(vendorId);
                                lbRuleTarget.setGmtCreate(new Date());
                                lbRuleTarget.setRegionId(lbListener.getRegionId());
                                lbListenerRuleRepository.save(lbRuleTarget);
                            }
                        }
                        return new BocloudResult(true, "创建成功");
                    }
                    return new BocloudResult(true, "创建失败");
                default:
                    return new BocloudResult(false, "暂不支持");
            }
        } catch (Exception e){
            logger.info("创建监听器策略失败", e);
            return new BocloudResult(false, "创建失败");
        }
    }

    @Override
    public BocloudResult modify(LbListenerPolicy policy, RequestContext context) {
        try {
            logger.info("修改监听器策略policy:{}", JSONObject.toJSONString(policy));
            LbListenerPolicy policyEx = lbListenerPolicyRepository.query(policy.getId());
            Assert.notNull(policyEx,"转发策略不存在");
            Long vendorId = policy.getVendorId();
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(vendor, "云平台不存在");
            Long listenerId = policy.getListenerId();
            LbListener lbListener = lbListenerRepository.query(listenerId);
            Assert.notNull(lbListener, "监听器不存在不存在");
            switch (CloudProvider.valueOf(vendor.getType())){
                case HUAWEI:
                case HCSO:
                    // 修改策略policy
                    LbListenerPolicyModel policyModel = new LbListenerPolicyModel();
                    BeanUtils.copyPropertiesIgnoreNull(policy, policyModel);
                    policyModel.setPolicyUuid(policyEx.getPolicyUuid());
                    logger.info("修改监听器策略policyModel:{}",JSONObject.toJSONString(policyModel));
                    BocloudResult bocloudResult = lbDriver.updateListenerPolicy(vendor.getUuid(),  lbListener.getRegionId(), policyModel);
                    if (bocloudResult.isSuccess()){
                        // 持久化policy
                        String redirectListenerUuid = policyModel.getRedirectListenerUuid();
                        String redirectPoolUuid = policyModel.getRedirectPoolUuid();
                        LbListener redirectListener = null;
                        LbPool lbPool = null;
                        if (StringUtils.isNotEmpty(redirectListenerUuid)){
                            redirectListener = lbListenerRepository.queryByUuid(redirectListenerUuid,vendorId);
                        }
                        if (StringUtils.isNotEmpty(redirectPoolUuid)){
                            lbPool = lbPoolRepository.queryByUuid(redirectPoolUuid, vendorId);
                        }

                        if ("REDIRECT_TO_POOL".equals(policy.getAction())){
                            String oldPoolUuid = policyEx.getRedirectPoolUuid();
                            String newPoolUuid = policyModel.getRedirectPoolUuid();
                            if (oldPoolUuid == null){//重定向至没有绑定任何listener的pool,直接绑定
                                LbPool newPool = lbPoolRepository.queryByUuid(newPoolUuid, vendorId);
                                newPool.setListenerId(policyEx.getListenerId());
                                newPool.setListenerUuid(policyEx.getListenerUuid());
                                lbPoolRepository.update(newPool);
                            } else if(StringUtils.equals(oldPoolUuid, newPoolUuid)){
                                // 什么都不做
                            } else {
                                List<LbListenerPolicy> oldPolicies = lbListenerPolicyRepository.listByListenerId(lbListener.getId());
                                List<String> collect = oldPolicies.stream()
                                        .filter(e -> Objects.nonNull(e.getRedirectPoolUuid()) && !policy.getId().equals(e.getId()))
                                        .map(LbListenerPolicy::getRedirectPoolUuid).distinct().collect(Collectors.toList());
                                if (!collect.contains(oldPoolUuid)){
                                    LbPool oldPool = lbPoolRepository.queryByUuid(oldPoolUuid, vendorId);
                                    oldPool.setListenerId(null);
                                    oldPool.setListenerUuid(null);
                                    lbPoolRepository.update(oldPool);
                                }
                                LbPool newPool = lbPoolRepository.queryByUuid(oldPoolUuid, vendorId);
                                newPool.setListenerId(lbListener.getId());
                                newPool.setListenerUuid(lbListener.getListenerUuid());
                                lbPoolRepository.update(newPool);
                            }
                        }
                        BeanUtils.copyPropertiesIgnoreNull(policyModel, policyEx);
                        policyEx.setListenerId(listenerId);
                        policyEx.setRedirectListenerId(redirectListener == null ? null : redirectListener.getId());
                        policyEx.setRedirectPoolId(lbPool == null ? null : lbPool.getId());
                        policyEx.setMenderId(context.getTarget());
                        policyEx.setVendorId(vendorId);
                        policyEx.setGmtModify(new Date());
                        policyEx.setRegionId(lbListener.getRegionId());
                        lbListenerPolicyRepository.update(policyEx);




                        // 修改、新增、删除规则rule
                        List<LbListenerRule> rules = policy.getRules();
                        ArrayList<LbListenerRuleModel> addRuleModels = new ArrayList<>();
                        ArrayList<LbListenerRuleModel> updateRuleModels = new ArrayList<>();
                        List<Long> updateRuleIds = new ArrayList<>();
                        List<LbListenerRule> removeRules = lbListenerRuleRepository.listByPolicyId(policy.getId());
                        for (LbListenerRule rule : rules) {
                            LbListenerRuleModel ruleModel = new LbListenerRuleModel();
                            BeanUtils.copyPropertiesIgnoreNull(rule, ruleModel);
                            if (Objects.isNull(rule.getId())){
                                logger.info("新增监听器规则rule:{}",JSONObject.toJSONString(ruleModel));
                                addRuleModels.add(ruleModel);
                            } else {
                                updateRuleIds.add(rule.getId());
                                LbListenerRule query = lbListenerRuleRepository.query(rule.getId());
                                ruleModel.setPolicyUuid(query.getPolicyUuid());
                                ruleModel.setRuleUuid(query.getRuleUuid());
                                logger.info("修改监听器规则rule:{}",JSONObject.toJSONString(ruleModel));
                                updateRuleModels.add(ruleModel);
                            }
                        }

                        removeRules.removeIf(e->{
                            return updateRuleIds.contains(e.getId());
                        });
                        ArrayList<Future<BocloudResult>> addFutures = new ArrayList<>();
                        ArrayList<Future<BocloudResult>> updateFutures = new ArrayList<>();
                        ArrayList<Future<BocloudResult>> removeFutures = new ArrayList<>();
                        addRuleModels.forEach(e -> {
                            Future<BocloudResult> resultFuture = taskExecutor.submit(() -> lbDriver.createListenerRule(vendor.getUuid(),  lbListener.getRegionId(), e));
                            addFutures.add(resultFuture);
                        });
                        updateRuleModels.forEach(e-> {
                            Future<BocloudResult> resultFuture = taskExecutor.submit(() -> lbDriver.updateListenerRule(vendor.getUuid(),  lbListener.getRegionId(), e));
                            updateFutures.add(resultFuture);
                        });

                        // 持久化规则rule
                        for (Future<BocloudResult> future : addFutures) {
                            BocloudResult result = future.get();
                            if (result.isSuccess()){
                                LbListenerRuleModel model = (LbListenerRuleModel) result.getData();
                                LbListenerRule lbRuleTarget = new LbListenerRule();
                                BeanUtils.copyPropertiesIgnoreNull(model, lbRuleTarget);
                                lbRuleTarget.setPolicyId(policy.getId());
                                lbRuleTarget.setListenerId(listenerId);
                                lbRuleTarget.setCreatorId(context.getTarget());
                                lbRuleTarget.setVendorId(vendorId);
                                lbRuleTarget.setGmtCreate(new Date());
                                lbRuleTarget.setRegionId(lbListener.getRegionId());
                                lbListenerRuleRepository.save(lbRuleTarget);
                            } else {
                                logger.error(result.toString());
                            }
                        }
                        for (Future<BocloudResult> future : updateFutures) {
                            BocloudResult result = future.get();
                            if (result.isSuccess()){
                                LbListenerRuleModel model = (LbListenerRuleModel) result.getData();
                                LbListenerRule lbRuleTarget = lbListenerRuleRepository.queryByUuid(model.getRuleUuid());
                                BeanUtils.copyPropertiesIgnoreNull(model, lbRuleTarget);
                                lbRuleTarget.setPolicyId(policy.getId());
                                lbRuleTarget.setListenerId(listenerId);
                                lbRuleTarget.setMenderId(context.getTarget());
                                lbRuleTarget.setVendorId(vendorId);
                                lbRuleTarget.setGmtModify(new Date());
                                lbRuleTarget.setRegionId(lbListener.getRegionId());
                                lbListenerRuleRepository.update(lbRuleTarget);
                            } else {
                                logger.error(result.toString());
                            }
                        }
                        for (LbListenerRule rule : removeRules) {
                            lbDriver.removeListenerRule(vendor.getUuid(),  lbListener.getRegionId(),rule.getPolicyUuid(), rule.getRuleUuid());
                            lbListenerRuleRepository.remove(rule.getId(),context.getTarget());
                        }
                        return new BocloudResult(true, "修改成功");
                    }
                    return new BocloudResult(false, "修改失败");
                default:
                    return new BocloudResult(false, "暂不支持");
            }
        } catch ( Exception e){
            logger.info("修改监听器策略失败", e);
            return new BocloudResult(false, "创建失败");
        }


    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        try {
            LbListenerPolicy policy = lbListenerPolicyRepository.query(id);
            Assert.notNull(policy,"转发策略不存在");
            Long vendorId = policy.getVendorId();
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            Assert.notNull(vendor, "云平台不存在");
            Long listenerId = policy.getListenerId();
            LbListener lbListener = lbListenerRepository.query(listenerId);
            Assert.notNull(lbListener, "监听器不存在不存在");

            switch (CloudProvider.valueOf(vendor.getType())){
                case HUAWEI:
                case HCSO:
                    BocloudResult bocloudResult = lbDriver.removeListenerPolicy(vendor.getUuid(),  lbListener.getRegionId(), policy.getPolicyUuid());
                    if (bocloudResult.isSuccess()){
                        lbListenerPolicyRepository.remove(id,context.getTarget());

                        List<LbListenerRule> lbListenerRules = lbListenerRuleRepository.listByPolicyId(id);
                        for (LbListenerRule rule : lbListenerRules) {
                            lbListenerRuleRepository.remove(rule.getId(),context.getTarget());
                        }

                        if ("REDIRECT_TO_POOL".equals(policy.getAction())){
                            String redirectPoolUuid = policy.getRedirectPoolUuid();
                            if (redirectPoolUuid == null){//重定向至没有绑定任何listener的pool,直接绑定
                                // 什么都不做
                            } else {
                                List<LbListenerPolicy> oldPolicies = lbListenerPolicyRepository.listByListenerId(lbListener.getId());
                                List<String> collect = oldPolicies.stream()
                                        .filter(e -> Objects.nonNull(e.getRedirectPoolUuid()) && !policy.getId().equals(e.getId()))
                                        .map(LbListenerPolicy::getRedirectPoolUuid).distinct().collect(Collectors.toList());
                                if (collect.isEmpty()){ // 没有被当前监听器下的其他策略依赖
                                    LbPool lbPool = lbPoolRepository.queryByUuid(redirectPoolUuid, vendorId);
                                    lbPool.setListenerId(null);
                                    lbPool.setListenerUuid(null);
                                    lbPoolRepository.update(lbPool);
                                }
                            }
                        }
                        return  new BocloudResult<>(true,"删除成功");
                    }
                    return  new BocloudResult<>(true,"删除失败");
                default:
                    return new BocloudResult(false, "暂不支持");
            }
        } catch (Exception e){
            logger.info("删除监听器策略失败", e);
            return new BocloudResult(false, "删除失败");
        }
    }
}
