package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.backup.BackupDriver;
import com.bocloud.cmp.model.BackupModel;
import com.bocloud.cmp.model.VolumeModel;
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.resource.Backup;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Volume;
import com.bocloud.ims.interfaces.resource.BackupService;
import com.bocloud.ims.repository.resource.BackupRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.SnapshotRepository;
import com.bocloud.ims.repository.resource.VolumeRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 备份
 *
 * @author admin
 */
@Service("backupService")
public class BackupServiceImpl implements BackupService {

    private static Logger logger = LoggerFactory.getLogger(BackupServiceImpl.class);

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private VolumeRepository volumeRepository;
    @Autowired
    private BackupDriver backupDriver;
    @Autowired
    private BackupRepository backupRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private SnapshotRepository snapshotRepository;
    @Autowired
    private LockFactory lockFactory;

    @Override
    public BocloudResult<GridBean<Backup>> list(Pager pager, RequestContext context) {
        List<Backup> list = Lists.newArrayList();
        int total = 0;
        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);
            sorter.put("gmtCreate", Common.ONE);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);
            sorter.put("gmtCreate", Common.ONE);
            total = backupRepository.count(params);
            if (simple) {
                List<SimpleBean> beans = backupRepository.list(params, sorter);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = backupRepository.list(page, rows, params, sorter);
                gridBean = GridHelper.getBean(page, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list backup failure:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }

    }

    @Override
    public BocloudResult create(Backup backup, RequestContext context) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public BocloudResult modify(Backup backup, RequestContext context) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        String path = Backup.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Backup backup = backupRepository.query(id);
            if (null == backup) {
                return new BocloudResult(false, "备份数据不存在!");
            }
            CloudVendor cloudVendor = cloudVendorRepository.query(backup.getVendorId());
            if (null == cloudVendor) {
                return new BocloudResult(false, "平台验证失败!");
            }
            String status = backup.getStatus();
            backup.setStatus("DELETING");
            backupRepository.update(backup);
            changeBackupStatus(backup, backup.getVendorId(), "DELETING");
            BackupModel model = new BackupModel();
            model.setBackupUuid(backup.getBackupUuid());
            taskExecutor.execute(() -> {
                BocloudResult result = new BocloudResult<>(false, "备份恢复失败！");
                String operate = cloudVendor.getType().toLowerCase() + ".backup";
                String content = "备份删除失败！";
                try {
                    result = backupDriver.remove(model, cloudVendor.getUuid(), backup.getRegionId());
                    if (result.isSuccess()) {
                        content = "备份删除成功";
                        /**
                         * 删除磁盘级别备份
                         */
                        backupRepository.delete(id);
                        if ("OS::Nova::Server".equalsIgnoreCase(backup.getResourceType())) {
                            backupRepository.deleteByCheckPointId(null, backup.getCheckpointUuid(), cloudVendor.getId(),
                                    backup.getRegionId(), context.getTarget(), backup.getBackupUuid());
                        } else {
                            if (StringUtils.isEmpty(backup.getSnapshotUuid())) {
                                snapshotRepository.removeBySnapUuid(backup.getSnapshotUuid(), cloudVendor.getId(),
                                        context.getTarget());
                            }
                        }
                    } else {
                        content = result.getMessage();
                        backup.setStatus(status);
                        backupRepository.update(backup);
                        changeBackupStatus(backup, backup.getVendorId(), status);
                    }
                } catch (Exception e) {
                    backup.setStatus(status);
                    backupRepository.update(backup);
                    changeBackupStatus(backup, backup.getVendorId(), status);
                    content = e.getMessage();
                }
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
                        MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            });

            return new BocloudResult<>(true, "删除任务已下发，正在执行....");
        } catch (Exception e) {
            logger.error("remove 备份 fail:", e);
            return new BocloudResult<>(false, "备份删除失败:" + e.getMessage());
        }

    }

    public void changeBackupStatus(Backup backup, Long vendorId, String status) {
        if ("OS::Nova::Server".equalsIgnoreCase(backup.getResourceType())) {
            List<Backup> listByCheckPointId = backupRepository.listByCheckPointId(null, backup.getCheckpointUuid(),
                    vendorId, backup.getRegionId(), backup.getBackupUuid());
            if (!ListTool.isEmpty(listByCheckPointId)) {
                for (Backup allBackups : listByCheckPointId) {
                    allBackups.setStatus(status);
                    backupRepository.update(allBackups);

                }
            }
        } else {
            backup.setStatus(status);
            backupRepository.update(backup);
        }
    }

    @Override
    public BocloudResult<Backup> detail(Long id) {
        try {
            Backup backup = backupRepository.query(id);
            Assert.notNull(backup, "备份数据不存在!");
            return new BocloudResult<>(true, backup, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message：", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult restore(Backup backupParam, RequestContext context) {
        try {
            Backup backup = backupRepository.query(backupParam.getId());
            if (null == backup) {
                return new BocloudResult(false, "备份数据不存在!");
            }
            if ("RESTORING".equalsIgnoreCase(backup.getStatus())) {
                return new BocloudResult<>(false, "当前备份处于恢复中，无需重复提交！");
            }
            CloudVendor cloudVendor = cloudVendorRepository.query(backup.getVendorId());
            if (null == cloudVendor) {
                return new BocloudResult(false, "云平台不存在！");
            }
            if ("OS::Cinder::Volume".equalsIgnoreCase(backup.getResourceType())) {
                Volume volume = volumeRepository.getByVolumeUuid(backup.getResourceUuid(), backup.getVendorId());
                if (null == volume) {
                    return new BocloudResult(false, "您所要操作的磁盘不存在，无法进行数据恢复!");
                }
                if ("in_use".equalsIgnoreCase(volume.getStatus())) {
                    return new BocloudResult<>(false, "备份对应的磁盘挂载在云服务器上，不能进行数据恢复，请先卸载磁盘!");
                }
            }
            BackupModel model = new BackupModel();
            if ("OS::Nova::Server".equalsIgnoreCase(backup.getResourceType())) {
                List<VolumeModel> volumeModels = new ArrayList<VolumeModel>();
                List<JSONObject> parseArray = backupParam.getSubBackups();
                if (ListTool.isEmpty(parseArray)) {
                    return new BocloudResult(false, "参数格式错误！");
                }
                for (JSONObject jsonObject : parseArray) {
                    VolumeModel volumeModel = new VolumeModel();
                    volumeModel.setBackupUuid(jsonObject.getString("backupUuid"));
                    volumeModel.setVolumeUuid(jsonObject.getString("volumeUuid"));
                    volumeModels.add(volumeModel);
                }
                model.setVolumeModels(volumeModels);
            }
            model.setBackupUuid(backup.getBackupUuid());
            model.setResourceType(backup.getResourceType());
            model.setResourceUuid(backup.getResourceUuid());
            String oldStatus = backup.getStatus();
            backup.setStatus("RESTORING");
            backupRepository.update(backup);
            taskExecutor.execute(() -> {
                BocloudResult result = new BocloudResult<>(false, "备份恢复失败！");
                String operate = cloudVendor.getType().toLowerCase() + ".backup";
                String content = "备份恢复失败！";
                try {
                    result = backupDriver.restore(model, cloudVendor.getUuid(), backup.getRegionId());
                    if (result.isSuccess()) {
                        content = "备份恢复成功";
                    } else {
                        content = result.getMessage();
                    }
                } catch (Exception e) {
                    content = e.getMessage();
                }
                backup.setStatus(oldStatus);
                backupRepository.update(backup);
                resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
                        MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
                        OperateResult.OperateCategory.ResourceEvent, context));
            });
            return new BocloudResult<>(true, "恢复备份任务已下发，正在执行....");
        } catch (Exception e) {
            return new BocloudResult<>(false, "恢复备份失败:" + e.getMessage());
        }
    }

}
