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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.azure.ddos.DdosCustomPolicieDriver;
import com.bocloud.cmp.model.azure.ddos.DdosCustomPolicieModel;
import com.bocloud.cmp.params.azure.AzureDdosCustomPolicieOperationParams;
import com.bocloud.common.model.*;
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.common.Project;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.azure.blob.Container;
import com.bocloud.ims.entity.resource.azure.ddos.DdosCustomPolicie;
import com.bocloud.ims.entity.resource.azure.ddos.DdosProtectionPlan;
import com.bocloud.ims.interfaces.resource.azure.ddos.AzureDdosCustomPolicieService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.azure.ddos.DdosCustomPolicieRepository;
import com.bocloud.ims.service.internal.SmsProjectService;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.api.client.util.Maps;
import lombok.extern.slf4j.Slf4j;
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.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author wangyu
 * @version 5.0.0-SNAPSHOT
 * @since 2021/3/8
 */
@Slf4j
@Service
public class AzureDdosCustomPolicieServiceImpl implements AzureDdosCustomPolicieService {
    @Autowired
    private DdosCustomPolicieRepository repository;
    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private DdosCustomPolicieDriver driver;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;


    @Override
    public BocloudResult<GridBean<DdosCustomPolicie>> list(Pager pager, RequestContext context) {
        try {
            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);

            List<DdosCustomPolicie> list;
            int total;
            GridBean gridBean;
            Long userId = null;
            List<Long> projectIds = new ArrayList<>();
            Long tenantId = context.getTenant();
            Long projectId = context.getProject();
            if (null != tenantId && 0L != tenantId) {
                List<Project> projects = smsProjectService.projectsByCondition(context.getTarget(),
                        context.getCatalog());
                if (null != projectId && 0L != projectId) {
                    projectIds.add(projectId);
                } else if (!ListTool.isEmpty(projects)) {
                    projects.forEach(project -> projectIds.add(project.getId()));
                } else {
                    projectIds.add(-1L);
                }
                if (RequestContext.Catalog.User == context.getCatalog()) {
                    userId = context.getTarget();
                }
            }
            total = repository.count(params, context.getTenant(), projectIds, userId);
            list = repository.list(page, rows, params, sorter, context.getTenant(), projectIds, userId);
            gridBean = GridHelper.getBean(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<Void> create(DdosCustomPolicie policie, RequestContext context) {
        try {
            CloudVendor vendor = vendorRepository.query(policie.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            if (null != repository.getByName(vendor.getId(), policie.getResourceGroupId(), policie.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            taskExecutor.submit(() -> {
                DdosCustomPolicieModel model = new DdosCustomPolicieModel();
                BeanUtils.copyProperties(policie, model);
                BocloudResult result = driver.create(model, vendor.getUuid());
                try {
                    if (result.isSuccess()) {
                        DdosCustomPolicie newPolicy = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), DdosCustomPolicie.class);
                        newPolicy.setCreatorId(context.getTarget());
                        newPolicy.setGmtCreate(new Date());
                        newPolicy.setVendorId(vendor.getId());
                        newPolicy.setTenantId(context.getTenant());
                        newPolicy.setProjectId(context.getProject());
                        if (RequestContext.Catalog.User == context.getCatalog()) {
                            newPolicy.setUserId(context.getTarget());
                        }
                        repository.save(newPolicy);
                    }
                } catch (Exception e) {
                    log.error("Get error message:{}", e);
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), result.getMessage(),
                        vendor.getType().toLowerCase() + ".ddospolicy", MapTools.simpleMap("vendorId", vendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            });
            return new BocloudResult<>(true, "创建任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "创建失败");
        }
    }

    @Override
    public BocloudResult<Void> modify(DdosCustomPolicie policie, RequestContext context) {
        String path = DdosProtectionPlan.class.getSimpleName() + "_" + policie.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosCustomPolicie database = repository.query(policie.getId());
            Assert.notNull(database, "数据不存在");
            CloudVendor vendor = vendorRepository.query(database.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            DdosCustomPolicieModel model = new DdosCustomPolicieModel();
            BeanUtils.copyProperties(policie, model);
            BocloudResult result = driver.modify(model, vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), DdosCustomPolicieModel.class);
            BeanUtils.copyProperties(model, database);
            database.setMenderId(context.getTarget());
            repository.update(database);
            return new BocloudResult<>(true, "修改成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "修改失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<DdosCustomPolicie> detail(Long id) {
        try {
            DdosCustomPolicie policie = repository.query(id);
            Assert.notNull(policie, "数据不存在");
            return new BocloudResult<>(true, policie, "查询成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "查询失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> remove(Long id, RequestContext context) {
        String path = DdosProtectionPlan.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosCustomPolicie policie = repository.query(id);
            Assert.notNull(policie, "数据不存在");
            CloudVendor vendor = vendorRepository.query(policie.getVendorId());
            DdosCustomPolicieModel model = new DdosCustomPolicieModel();
            model.setName(policie.getName());
            model.setResourceGroupName(policie.getResourceGroupName());
            BocloudResult result = driver.delete(model, vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            Boolean flag = repository.remove(id, context.getTarget());
            return flag ? new BocloudResult(true, "删除成功") : new BocloudResult(false, "删除失败");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "删除失败");
        }
    }

    @Override
    public BocloudResult<Void> batchRemove(AzureDdosCustomPolicieOperationParams params, RequestContext context) {
        try {
            Assert.notNull(params, "参数格式错误");
            List<Long> idArray = params.getIds();
            Assert.notEmpty(idArray, "没有需要删除的数据！");
            CloudVendor vendor = vendorRepository.query(params.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            List<BocloudResult> succList = new ArrayList<>();
            List<BocloudResult> failList = new ArrayList<>();
            List<Future<BocloudResult>> futures = new ArrayList<>();
            new Thread(() -> {
                try {
                    for (Long id : idArray) {
                        futures.add(taskExecutor.submit(() -> this.remove(id, context)));
                    }
                    for (int i = 0; i < futures.size(); i++) {
                        BocloudResult result = futures.get(i).get(10, TimeUnit.MINUTES);
                        if (result.isSuccess()) {
                            succList.add(result);
                        } else {
                            failList.add(result);
                        }
                    }
                } catch (Exception e) {
                    log.error("Get error message：{}", e.getMessage());
                }
                String message = "DDOS策略删除成功【" + succList.size() + "】个，失败【" + failList.size() + "】个";
                resourceEventPublisher.send(new OperateResult(succList.size() > 0 ? true : false, message,
                        vendor.getType().toLowerCase() + ".ddospolicy", MapTools.simpleMap("vendorId", vendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            }).start();
            return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("Get error message：{}", e.getMessage());
            return new BocloudResult<>(false, "批量删除失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> assign(AzureDdosCustomPolicieOperationParams params, RequestContext context) {
        Long id = params.getId();
        Long userId = params.getUserId();
        Long projectId = params.getProjectId();
        Long tenantId = params.getTenantId();
        String path = Container.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosCustomPolicie policie = repository.query(id);
            Assert.notNull(policie, "数据不存在");
            policie.setTenantId(tenantId);
            policie.setProjectId(projectId);
            policie.setUserId(userId);
            repository.update(policie);
            return new BocloudResult(true, policie, "操作成功");
        } catch (Exception e) {
            log.error("assign container  fail:", e);
            return new BocloudResult<>(false, "操作失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Void> batchAssign(AzureDdosCustomPolicieOperationParams params, RequestContext context) {
        List<Long> ids = params.getIds();
        for (Long id : ids) {
            Long userId = params.getUserId();
            Long projectId = params.getProjectId();
            Long tenantId = params.getTenantId();
            String path = Container.class.getSimpleName() + "_" + id;
            try (BocloudACLock lock = lockFactory.getACLock(path)) {
                Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
                DdosCustomPolicie policie = repository.query(id);
                Assert.notNull(policie, "数据不存在");
                policie.setTenantId(tenantId);
                policie.setProjectId(projectId);
                policie.setUserId(userId);
                repository.update(policie);
            } catch (Exception e) {
                log.error("assign container  fail:", e);
                return new BocloudResult<>(false, "操作失败:" + e.getMessage());
            }
        }
        return new BocloudResult<>(true, "操作成功");
    }
}
