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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.azure.ddos.DdosProtectionPlanDriver;
import com.bocloud.cmp.model.DeleteParam;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.azure.ddos.DdosProtectionPlanModel;
import com.bocloud.common.enums.BoCloudExchange;
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.NetworkInterface;
import com.bocloud.ims.entity.resource.ResourceGroup;
import com.bocloud.ims.entity.resource.Vpc;
import com.bocloud.ims.entity.resource.azure.blob.Container;
import com.bocloud.ims.entity.resource.azure.ddos.DdosProtectionPlan;
import com.bocloud.ims.interfaces.resource.azure.ddos.DdosProtectionPlanService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.NetworkInterfaceRepository;
import com.bocloud.ims.repository.resource.ResourceGroupRepository;
import com.bocloud.ims.repository.resource.VpcRepository;
import com.bocloud.ims.repository.resource.azure.ddos.DdosProtectionPlanRepository;
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 lombok.extern.slf4j.Slf4j;
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 java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author wangyu
 * @version 5.0.0-SNAPSHOT
 * @since 2021/3/5
 */
@Slf4j
@Service
public class DdosProtectionPlanServiceImpl implements DdosProtectionPlanService {

    @Autowired
    private DdosProtectionPlanRepository repository;
    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private ResourceGroupRepository groupRepository;
    @Autowired
    private DdosProtectionPlanDriver driver;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private VpcRepository vpcRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private NetworkInterfaceRepository interfaceRepository;
    @Autowired
    private AmqpTemplate amqpTemplate;
    private static final String NOTICE_QUEUE = "event.notice.queue";


    @Override
    public BocloudResult<GridBean<DdosProtectionPlan>> list(Pager pager, RequestContext context) {
        try {
            List<DdosProtectionPlan> list;
            int total;
            GridBean gridBean;
            pager.getSorter().put("gmtCreate", Common.ONE);
            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(pager.getParams(), context.getTenant(), projectIds, userId);
            if (pager.getSimple()) {
                List<SimpleBean> beans = repository.list(pager.getParams(), pager.getSorter(), context.getTenant(), projectIds, userId);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = repository.list(pager.getPage(), pager.getRows(), pager.getParams(), pager.getSorter(), context.getTenant(), projectIds, userId);
                for (DdosProtectionPlan plan : list) {
                    List<Vpc> vpcs = vpcRepository.listByPlanUuid(plan.getVendorId(), plan.getRegionId(), plan.getDdosUuid());
                    plan.setVpcs(vpcs);
                }
                gridBean = GridHelper.getBean(pager.getPage(), pager.getRows(), total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<DdosProtectionPlan> create(DdosProtectionPlan plan, RequestContext context) {
        try {
            CloudVendor vendor = vendorRepository.query(plan.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            if (null != repository.getByName(vendor.getId(), plan.getResourceGroupId(), plan.getName())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            ResourceGroup group = groupRepository.query(plan.getResourceGroupId());
            Assert.notNull(group, "资源组不存在");
            String name = plan.getName();
            taskExecutor.submit(() -> {
                DdosProtectionPlanModel model = new DdosProtectionPlanModel();
                model.setResourceGroupName(group.getName());
                model.setRegionId(plan.getRegionId());
                model.setName(plan.getName());
                BocloudResult result = driver.create(model, vendor.getUuid());
                try {
                    if (result.isSuccess()) {
                        DdosProtectionPlan newPlan = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), DdosProtectionPlan.class);
                        newPlan.setCreatorId(context.getTarget());
                        newPlan.setGmtCreate(new Date());
                        newPlan.setVendorId(vendor.getId());
                        newPlan.setTenantId(context.getTenant());
                        newPlan.setProjectId(context.getProject());
                        if (RequestContext.Catalog.User == context.getCatalog()) {
                            newPlan.setUserId(context.getTarget());
                        }
                        newPlan.setResourceGroupId(group.getId());
                        repository.save(newPlan);
                    }
                } catch (Exception e) {
                    log.error("Get error message:{}", e);
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), result.getMessage(),
                        vendor.getType().toLowerCase() + ".ddos", MapTools.simpleMap("vendorId", vendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
                String message = "尊敬的客户，您好！您的Ddos防护计划【" + name + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建成功，请登录控制台查看及管理。";
                if (result.isFailed()) {
                    message = "尊敬的客户，您好！您的Ddos防护计划【" + name + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建失败，失败信息：" + result.getMessage();
                }
                amqpTemplate.convertAndSend(BoCloudExchange.Default.DIRECT.getExchange(), NOTICE_QUEUE, new NotifyMessage(context, "Ddos防护计划创建通知", message, true, true));
            });
            return new BocloudResult<>(true, "创建任务已下发，正在执行...");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "创建失败");
        }
    }

    @Override
    public BocloudResult<DdosProtectionPlan> modify(DdosProtectionPlan plan, RequestContext context) {
        String path = DdosProtectionPlan.class.getSimpleName() + "_" + plan.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosProtectionPlan database = repository.query(plan.getId());
            Assert.notNull(database, "数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!database.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            if (null != database.getLocked() && database.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            CloudVendor vendor = vendorRepository.query(database.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            DdosProtectionPlanModel model = new DdosProtectionPlanModel();
            model.setDdosUuid(database.getDdosUuid());
            model.setTags(plan.getTags());
            BocloudResult result = driver.modify(model, vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), DdosProtectionPlanModel.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<DdosProtectionPlan> detail(Long id, RequestContext context) {
        try {
            DdosProtectionPlan plan = repository.query(id);
            Assert.notNull(plan, "数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!plan.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            List<Vpc> vpcs = vpcRepository.listByPlanUuid(plan.getVendorId(), plan.getRegionId(), plan.getDdosUuid());
            plan.setVpcs(vpcs);
            List<NetworkInterface> interfaces = new ArrayList<>();
            if (!ListTool.isEmpty(vpcs)) {
                for (Vpc vpc : vpcs) {
                    List<NetworkInterface> networkInterfaces = interfaceRepository.listByVpcId(vpc.getId());
                    if (!ListTool.isEmpty(networkInterfaces)) {
                        interfaces.addAll(networkInterfaces);
                    }
                }
            }
            plan.setNetworkCards(interfaces);
            return new BocloudResult<>(true, plan, "查询成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "查询失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        String path = DdosProtectionPlan.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosProtectionPlan plan = repository.query(id);
            Assert.notNull(plan, "数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!plan.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            if (null != plan.getLocked() && plan.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            CloudVendor vendor = vendorRepository.query(plan.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            repository.updateStatus(id, "DELETEING", context.getTarget());
            BocloudResult result = driver.delete(plan.getDdosUuid(), vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            Boolean flag = repository.remove(id, context.getTarget());
            if (flag) {
                String message = "尊敬的客户，您好！您的Ddos防护计划【" + plan.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "删除成功";
                amqpTemplate.convertAndSend(BoCloudExchange.Default.DIRECT.getExchange(), NOTICE_QUEUE, new NotifyMessage(context, "Ddos防护计划删除通知", message, true, true));
            } else {
                repository.updateStatus(id, plan.getStatus(), context.getTarget());
            }
            return flag ? new BocloudResult<>(true, "删除成功") : new BocloudResult<>(false, "删除失败");
        } catch (Exception e) {
            repository.updateStatus(id, "Succeeded", context.getTarget());
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "删除失败：" + e.getMessage());
        }
    }

    @Override
    public BocloudResult batchRemove(DeleteParam params, RequestContext context) {
        try {
            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 (InterruptedException | ExecutionException | TimeoutException 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() + ".ddos", 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 assign(JSONObject object, RequestContext context) {
        Long id = object.getLong("id");
        Long userId = object.getLong("userId");
        Long projectId = object.getLong("projectId");
        Long tenantId = object.getLong("tenantId");
        String path = Container.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            DdosProtectionPlan plan = repository.query(id);
            Assert.notNull(plan, "数据不存在");
            if (null != plan.getLocked() && plan.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            plan.setTenantId(tenantId);
            plan.setProjectId(projectId);
            plan.setUserId(userId);
            repository.update(plan);
            return new BocloudResult<>(true, plan, "操作成功");
        } catch (Exception e) {
            log.error("assign container  fail:", e);
            return new BocloudResult<>(false, "操作失败:" + e.getMessage());
        }
    }

}
