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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.blob.ContainerDriver;
import com.bocloud.cmp.model.DeleteParam;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.azure.account.StorageAccountModel;
import com.bocloud.cmp.model.azure.blob.ContainerModel;
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.azure.account.StorageAccount;
import com.bocloud.ims.entity.resource.azure.blob.Blob;
import com.bocloud.ims.entity.resource.azure.blob.Container;
import com.bocloud.ims.interfaces.resource.azure.blob.ContainerService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.azure.account.StorageAccountRepository;
import com.bocloud.ims.repository.resource.azure.blob.BlobRepository;
import com.bocloud.ims.repository.resource.azure.blob.ContainerRepository;
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.apache.commons.lang3.StringUtils;
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.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * azure存储容器业务实现
 *
 * @author wangyu
 * @version 5.0.0-SNAPSHOT
 * @since 2021/2/2
 */
@Slf4j
@Service
public class ContainerServiceImpl implements ContainerService {

    @Autowired
    private CloudVendorRepository vendorRepository;
    @Autowired
    private ContainerRepository repository;
    @Autowired
    private StorageAccountRepository accountRepository;
    @Autowired
    private ContainerDriver containerDriver;
    @Autowired
    private BlobRepository blobRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private AmqpTemplate amqpTemplate;
    private static final String NOTICE_QUEUE = "event.notice.queue";

    @Override
    public BocloudResult list(Pager pager, RequestContext context) {
        try {
            List<Container> 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(), userId, projectIds);
            if (pager.getSimple()) {
                List<SimpleBean> beans = repository.list(pager.getParams(), pager.getSorter(), userId, projectIds);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = repository.list(pager.getPage(), pager.getRows(), pager.getParams(), pager.getSorter(), userId, projectIds);
                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<Container> create(Container container, RequestContext context) {
        try {
            CloudVendor vendor = vendorRepository.query(container.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            if (null != repository.getByName(container.getName(), container.getStorageAccountId(), vendor.getId())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            ContainerModel model = new ContainerModel();
            BeanUtils.copyProperties(container, model);
            StorageAccount account = accountRepository.query(container.getStorageAccountId());
            Assert.notNull(account, "未找到存储账号");
            StorageAccountModel accountModel = JSONObject.parseObject(JSONObject.toJSONString(account), StorageAccountModel.class);
            container.setCreatorId(context.getTarget());
            container.setGmtCreate(new Date());
            container.setVendorId(vendor.getId());
            container.setStorageAccountId(account.getId());
            container.setTenantId(context.getTenant());
            container.setProjectId(context.getProject());
            if (RequestContext.Catalog.User == context.getCatalog()) {
                container.setUserId(context.getTarget());
            }
            container.setLeaseState("BUILDING");
            repository.save(container);
            final ContainerModel finalModel = model;
            final Long containerId = container.getId();
            final String name = container.getName();
            taskExecutor.submit(() -> {
                BocloudResult result = containerDriver.create(finalModel, accountModel, vendor.getUuid());
                try {
                    if (result.isSuccess()) {
                        ContainerModel containerModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ContainerModel.class);
                        Container newContainer = repository.query(containerId);
                        if (null == newContainer) {
                            newContainer = new Container();
                        }
                        BeanUtils.copyProperties(containerModel, newContainer);
                        newContainer.setGmtModify(new Date());
                        newContainer.setMenderId(context.getTarget());
                        repository.update(newContainer);
                    }
                } catch (Exception e) {
                    if (null != container && null != container.getId()) {
                        repository.remove(container.getId(), context.getTarget());
                    }
                    log.error("Get error message:{}", e);
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), result.getMessage(),
                        vendor.getType().toLowerCase() + ".container", MapTools.simpleMap("vendorId", vendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
                String message = "尊敬的客户，您好！您的容器【" + name + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "创建成功，请登录控制台查看及管理。";
                if (result.isFailed()) {
                    message = "尊敬的客户，您好！您的容器【" + 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, "容器创建通知", message, true, true));
            });
            return new BocloudResult<>(true, "创建任务已下发，正在执行...");
        } catch (Exception e) {
            if (null != container && null != container.getId()) {
                repository.remove(container.getId(), context.getTarget());
            }
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "创建失败");
        }
    }

    @Override
    public BocloudResult<Container> modify(Container container, RequestContext context) {
        return null;
    }

    @Override
    public BocloudResult<Container> detail(Long id, RequestContext context) {
        try {
            Container container = repository.query(id);
            Assert.notNull(container, "容器数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!container.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            //查询存储账号
            StorageAccount account = accountRepository.query(container.getStorageAccountId());
            container.setStorageAccount(account);
            return new BocloudResult<>(true, container, "查询成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        String path = Container.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Container container = repository.query(id);
            Assert.notNull(container, "容器数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!container.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            if (null != container.getLocked() && container.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            CloudVendor vendor = vendorRepository.query(container.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            StorageAccount account = accountRepository.query(container.getStorageAccountId());
            Assert.notNull(account, "未找到存储账号");
            repository.updateStatus(id, "DELETEING", context.getTarget());
            StorageAccountModel accountModel = JSONObject.parseObject(JSONObject.toJSONString(account), StorageAccountModel.class);
            BocloudResult result = containerDriver.delete(container.getName(), accountModel, vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            Boolean flag = repository.remove(id, context.getTarget());
            if (flag) {
                blobRepository.removeByContainerId(container.getId(), context.getTarget());
                String message = "尊敬的客户，您好！您的容器【" + container.getName() + "】于" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "删除成功";
                amqpTemplate.convertAndSend(BoCloudExchange.Default.DIRECT.getExchange(), NOTICE_QUEUE, new NotifyMessage(context, "容器删除通知", message, true, true));
            } else {
                repository.updateStatus(id, container.getLeaseState(), context.getTarget());
            }
            return flag ? new BocloudResult<>(true, "删除成功") : new BocloudResult<>(false, "删除失败");
        } catch (Exception e) {
            repository.updateStatus(id, "AVAILABLE", 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 = "容器删除成功【" + succList.size() + "】个，失败【" + failList.size() + "】个";
                resourceEventPublisher.send(new OperateResult(succList.size() > 0 ? true : false, message,
                        vendor.getType().toLowerCase() + ".container", 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 updateMetadata(Long id, Map<String, String> metadata, RequestContext context) {
        String path = Container.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Container container = repository.query(id);
            Assert.notNull(container, "容器数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!container.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            if (null != container.getLocked() && container.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            CloudVendor vendor = vendorRepository.query(container.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            StorageAccount account = accountRepository.query(container.getStorageAccountId());
            Assert.notNull(account, "未找到存储账号");
            StorageAccountModel accountModel = JSONObject.parseObject(JSONObject.toJSONString(account), StorageAccountModel.class);
            taskExecutor.execute(() -> {
                String oldMetadata = container.getMetadata();
                Map<String, String> oldMetadataMap = new HashMap<>();
                if (!StringUtils.isEmpty(oldMetadata)) {
                    oldMetadataMap = JSONObject.parseObject(oldMetadata, Map.class);
                }
                oldMetadataMap.putAll(metadata);
                BocloudResult result = containerDriver.updateMetadata(container.getName(), oldMetadataMap, accountModel, vendor.getUuid());
                if (result.isSuccess()) {
                    ContainerModel containerModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ContainerModel.class);
                    BeanUtils.copyProperties(containerModel, container);
                    container.setGmtModify(new Date());
                    container.setMenderId(context.getTarget());
                    repository.update(container);
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), result.getMessage(),
                        vendor.getType().toLowerCase() + ".container", 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 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), "请求超时");
            Container container = repository.query(id);
            Assert.notNull(container, "数据不存在");
            if (null != container.getLocked() && container.getLocked()) {
                return new BocloudResult<>(false, "资源处于锁定状态，请先解锁");
            }
            container.setTenantId(tenantId);
            container.setProjectId(projectId);
            container.setUserId(userId);
            repository.update(container);
            List<Blob> blobs = blobRepository.listByCondition(container.getVendorId(), container.getId(), null);
            for (Blob blob : blobs) {
                blob.setTenantId(tenantId);
                blob.setProjectId(projectId);
                blob.setUserId(userId);
                blobRepository.update(blob);
            }
            return new BocloudResult<>(true, container, "操作成功");
        } catch (Exception e) {
            log.error("assign container  fail:", e);
            return new BocloudResult<>(false, "操作失败:" + e.getMessage());
        }
    }

    @Override
    public BocloudResult action(Long id, String action, JSONObject params, RequestContext context) {
        String path = Container.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Container container = repository.query(id);
            Assert.notNull(container, "数据不存在");
            RequestContext.Catalog catalog = context.getCatalog();
            if ((RequestContext.Catalog.Tenant == catalog || RequestContext.Catalog.User == catalog) && (!container.getTenantId().equals(context.getTenant()))) {
                return new BocloudResult<>(false, "此操作被禁止，当前资源归属于其他租户");
            }
            CloudVendor vendor = vendorRepository.query(container.getVendorId());
            Assert.notNull(vendor, "云平台不存在！");
            StorageAccount account = accountRepository.query(container.getStorageAccountId());
            Assert.notNull(account, "未找到存储账号");
            StorageAccountModel accountModel = JSONObject.parseObject(JSONObject.toJSONString(account), StorageAccountModel.class);
            this.updatePublicAccessType(container, action, params.getString("publicAccessType"), accountModel, vendor, context);
            return new BocloudResult<>(true, "操作成功");
        } catch (Exception e) {
            log.error("Get error message：{}", e);
            return new BocloudResult<>(false, "操作失败：" + e.getMessage());
        }
    }


    /**
     * 更新公共访问级别
     *
     * @param container
     * @param action
     * @param publicAccessType
     * @param account
     * @param vendor
     * @param context
     * @return
     */
    private BocloudResult updatePublicAccessType(Container container, String action, String publicAccessType, StorageAccountModel account, CloudVendor vendor, RequestContext context) {
        if ("updatePublicAccessType".equalsIgnoreCase(action)) {
            BocloudResult result = containerDriver.updatePublicAccess(container.getName(), publicAccessType, account, vendor.getUuid());
            Assert.isTrue(result.isSuccess(), result.getMessage());
            container.setPublicAccess(publicAccessType);
            container.setMenderId(context.getTarget());
            container.setGmtModify(new Date());
            repository.update(container);
        }
        return new BocloudResult<>(true, "更新成功");
    }
}
