package com.bocloud.ims.service.resource;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.storage.StorageDriver;
import com.bocloud.cmp.driver.storage.VolumeDriver;
import com.bocloud.cmp.driver.storage.VolumeTemplateDriver;
import com.bocloud.cmp.model.StorageModel;
import com.bocloud.cmp.model.VolumeModel;
import com.bocloud.cmp.model.VolumeTemplateModel;
import com.bocloud.common.encrypt.AESEncrptor;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.GridBean;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.model.SimpleBean;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.database.utils.QueryBuilder;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Disk;
import com.bocloud.ims.entity.resource.ImageVolume;
import com.bocloud.ims.entity.resource.ServerVolume;
import com.bocloud.ims.entity.resource.Storage;
import com.bocloud.ims.entity.resource.Volume;
import com.bocloud.ims.entity.resource.powervc.PowervcRedisKeyBuilder;
import com.bocloud.ims.interfaces.resource.StorageService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.DiskRepository;
import com.bocloud.ims.repository.resource.ImageVolumeRepository;
import com.bocloud.ims.repository.resource.ServerVolumeRepository;
import com.bocloud.ims.repository.resource.StorageRepository;
import com.bocloud.ims.repository.resource.VolumePoolRepository;
import com.bocloud.ims.repository.resource.VolumeRepository;
import com.bocloud.ims.repository.resource.VolumeTemplateRepository;
import com.bocloud.ims.service.transporter.vendor.PowervcTransporter;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.google.common.collect.Maps;

/**
 * 存储器服务实现
 *
 * @author lkx007
 */
@Service("storageService")
public class StorageServiceImpl implements StorageService {
    private static Logger logger = LoggerFactory.getLogger(StorageServiceImpl.class);
    private static String OPERATION = "powervc.storage.operate";
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private StorageRepository storageRepository;
    @Autowired
    private VolumeRepository volumeRepository;
    @Autowired
    private VolumePoolRepository volumePoolRepository;
    @Autowired
    private VolumeTemplateRepository volumeTemplateRepository;
    @Autowired
    private StorageDriver storageDriver;
    @Autowired
    private VolumeDriver volumeDriver;
    @Autowired
    private VolumeTemplateDriver volumeTemplateDriver;
    @Autowired
    private PowervcTransporter powervcTransportor;
    @Autowired
    private ImageVolumeRepository imageVolumeRepository;
    @Autowired
    private ServerVolumeRepository serverVolumeRepository;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DiskRepository diskRepository;

    @Override
    public BocloudResult<GridBean<Storage>> list(Pager pager) throws Exception {
        List<Storage> list = null;
        List<SimpleBean> beans = null;
        int total = 0;
        GridBean gridBean = null;
        try {

            int page = pager.getPage();
            int rows = pager.getRows();
            List<Param> params = pager.getParams();
            Map<String, String> sorter = pager.getSorter();
            boolean simple = pager.getSimple();

            if (null == params) {
                params = new ArrayList<>();
            }
            if (null == sorter) {
                sorter = Maps.newHashMap();
            }
            sorter.put("gmtCreate", Common.ONE);
            total = storageRepository.count(params);
            if (simple) {
                beans = this.storageRepository.list(params, sorter);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = storageRepository.list(page, rows, params, sorter);
            }
            for (Storage storage : list) {
                List<Disk> disks = diskRepository.listByStorage(storage.getId());
                storage.setDiskAllCount(disks.size());
                storage.setDiskCapacity(Math.round(disks.stream().mapToLong(Disk::getCapacity).sum() / 1024D / 1024D / 1024D * 100) / 100D);
                storage.setDiskUseCount((int) disks.stream().filter(disk -> disk.getMdiskId() != null).count());
            }
            gridBean = GridHelper.getBean(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询存储器成功");
        } catch (Exception e) {
            logger.error("List storage fail:", e);
            return new BocloudResult<>(false, "查询存储器失败");
        }
    }

    @Override
    public BocloudResult<String> create(Storage storage, RequestContext context) {
        try {
            CloudVendor vendor = cloudVendorRepository.query(storage.getVendorId());
            if (vendor == null) {
                logger.error("vendor id = [{}]已经被删除", storage.getVendorId());
                return new BocloudResult<>(false, "指定vendorId" + storage.getVendorId() + "不存在");
            }
            if (!StringUtils.isNotEmpty(storage.getType()) ||
                    !StringUtils.isNotEmpty(storage.getIp()) ||
                    !StringUtils.isNotEmpty(storage.getName()) ||
                    !StringUtils.isNotEmpty(storage.getUsername()) ||
                    !StringUtils.isNotEmpty(storage.getPassword())) {
                logger.error("param error:{}", JSONObject.toJSONString(storage));
                return new BocloudResult<>(false, "连接存储器失败：参数格式错误");
            }
            // check storage exists
            if (storageRepository.getByName(storage.getVendorId(), storage.getName()) != null) {
                logger.error("名称为{}的存储器已经存在", storage.getName());
                return new BocloudResult<>(false, "名称为" + storage.getIp() + "的存储器已经存在");
            }
            if (storageRepository.getIp(storage.getVendorId(), storage.getIp()) != null) {
                logger.error("ip为{}的存储器已经存在", storage.getIp());
                return new BocloudResult<>(false, "ip为" + storage.getIp() + "的存储器已经存在");
            }
            taskExecutor.submit(() -> {
                try {
                    StorageModel storageModel = new StorageModel();
                    convert(storage, storageModel);
                    storageModel.setVolumePool(storage.getVolumePool().getName());
                    BocloudResult result = storageDriver.create(vendor.getUuid(), storageModel);
                    if (result.isFailed()) {
                        logger.error("添加存储器失败:{}", result);
                        resourceEventPublisher.send(new OperateResult(false,
                                "添加存储器失败：" + result.getMessage(),
                                OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                        return;
                    }
                    // 从创建结果中获取host信息
                    StorageModel resultModel = (StorageModel) result.getData();
                    storage.setHost(resultModel.getHost());

                    // list所有的存储器
                    BocloudResult listResult = storageDriver.list(vendor.getUuid());
                    if (listResult.isFailed()) {
                        logger.error("添加存储器成功，获取存储器详情失败:{}", listResult);
                        resourceEventPublisher.send(new OperateResult(false,
                                "添加存储器成功，获取存储器详情失败：" + listResult.getMessage(),
                                OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                        return;
                    }
                    // 通过host信息去匹配存储器信息并保存
                    List<StorageModel> storages = (List<StorageModel>) listResult.getData();
                    for (StorageModel model : storages) {
                        if (resultModel.getHost().equals(model.getHost())) {
                            storage.setUuid(model.getUuid());
                            storage.setCreatorId(context.getTarget());
                            storage.setMenderId(context.getTarget());
                            storage.setGmtCreate(new Date());
                            storageRepository.save(storage);
                            powervcTransportor.insertOrUpdate(vendor, model, context.getTarget());

                            // 获取创建的存储器模板
                            String defaultTemplateUuid = model.getDefaultTemplateUuid();
                            BocloudResult templateResult = volumeTemplateDriver.detail(defaultTemplateUuid, vendor.getUuid());
                            if (templateResult.isFailed()) {
                                logger.error("添加存储器成功，获取存储器默认模板失败:{}", listResult);
                                resourceEventPublisher.send(new OperateResult(false,
                                        "添加存储器成功，获取存储器默认模板失败：" + listResult.getMessage(),
                                        OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                                return;
                            }
                            VolumeTemplateModel volumeTemplate = (VolumeTemplateModel) templateResult.getData();
                            powervcTransportor.insertOrUpdate(vendor, volumeTemplate, context.getTarget());
                            break;
                        }
                    }
                    // 发送界面消息，通知添加存储器成功
                    resourceEventPublisher.send(new OperateResult(true,
                            "添加存储器" + storage.getName() + "成功",
                            OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                } catch (Exception e) {
                    logger.error("creat storage exception:", e);
                    resourceEventPublisher.send(new OperateResult(false, "添加存储器异常",
                            OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                    return;
                }
            });
            return new BocloudResult<>(true, "添加存储器" + storage.getName() + "任务已经下发，正在执行...");
        } catch (Exception e) {
            logger.error("create storage fail:", e);
            return new BocloudResult<>(false, "添加存储器失败");
        }
    }

    @Override
    public BocloudResult<String> modify(Storage storage, RequestContext context) {
        try {
            if (!StringUtils.isNotEmpty(storage.getIp()) ||
                    !StringUtils.isNotEmpty(storage.getName()) ||
                    !StringUtils.isNotEmpty(storage.getUsername()) ||
                    !StringUtils.isNotEmpty(storage.getPassword())) {
                logger.error("param error:{}", JSONObject.toJSONString(storage));
                return new BocloudResult<>(false, "连接存储器失败：参数格式错误");
            }
            Storage storageOld = storageRepository.get(storage.getId());
            if (storageOld == null || storageOld.getDeleted()) {
                logger.error("存储器 id = [{}]已经被删除", storage.getId());
                return new BocloudResult<>(false, "存储器" + storage.getId() + "不存在");
            }
            CloudVendor vendor = cloudVendorRepository.query(storageOld.getVendorId());
            if (vendor == null) {
                logger.error("vendor id = [{}]已经被删除", storageOld.getVendorId());
                return new BocloudResult<>(false, "指定vendorId" + storageOld.getVendorId() + "不存在");
            }
            if (!storage.getName().equals(storageOld.getName())) {
                if (storageRepository.getByName(storageOld.getVendorId(), storage.getName()) != null) {
                    logger.error("名称为{}的存储器已经存在", storage.getName());
                    return new BocloudResult<>(false, "名称为" + storage.getIp() + "的存储器已经存在");
                }
            }
            if (!storage.getIp().equals(storageOld.getIp())) {
                if (storageRepository.getIp(storageOld.getVendorId(), storage.getIp()) != null) {
                    logger.error("ip为{}的存储器已经存在", storage.getIp());
                    return new BocloudResult<>(false, "ip为" + storage.getIp() + "的存储器已经存在");
                }
            }
            taskExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        StorageModel storageModel = new StorageModel();
                        convert(storage, storageModel);
                        storageModel.setHost(storageOld.getHost());
                        BocloudResult result = storageDriver.modify(vendor.getUuid(), storageModel);
                        if (result.isFailed()) {
                            logger.error("修改存储器失败:{}", result);
                            resourceEventPublisher.send(new OperateResult(false,
                                    "修改存储器失败：" + result.getMessage(),
                                    OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                            return;
                        }
                        // 从创建结果中获取host信息
                        StorageModel resultModel = (StorageModel) result.getData();
                        // list所有的存储器
                        BocloudResult listResult = storageDriver.list(vendor.getUuid());
                        if (listResult.isFailed()) {
                            logger.error("添加存储器成功，获取存储器详情失败:{}", listResult);
                            resourceEventPublisher.send(new OperateResult(false,
                                    "添加存储器成功，获取存储器详情失败：" + listResult.getMessage(),
                                    OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                            return;
                        }
                        // 通过host信息去匹配存储器信息并保存
                        List<StorageModel> storages = (List<StorageModel>) listResult.getData();
                        for (StorageModel model : storages) {
                            if (resultModel.getHost().equals(model.getHost())) {
                                powervcTransportor.insertOrUpdate(vendor, model, context.getTarget());
                                break;
                            }
                        }
                        // 发送界面消息，通知添加存储器成功
                        resourceEventPublisher.send(new OperateResult(true,
                                "修改存储器" + storage.getName() + "成功",
                                OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                    } catch (Exception e) {
                        logger.error("update storage exception:", e);
                        resourceEventPublisher.send(new OperateResult(false, "修改存储器异常",
                                OPERATION, null, OperateResult.OperateCategory.ResourceEvent, context));
                        return;
                    }
                }
            });
            return new BocloudResult<>(true, "修改存储器" + storage.getName() + "任务已经下发，正在执行...");
        } catch (Exception e) {
            logger.error("update storage fail:", e);
            return new BocloudResult<>(false, "修改存储器失败");
        }
    }

    @Override
    public BocloudResult connect(Storage storage, RequestContext context) {
        try {
            if (!StringUtils.isNotEmpty(storage.getType()) ||
                    !StringUtils.isNotEmpty(storage.getIp()) ||
                    !StringUtils.isNotEmpty(storage.getName()) ||
                    !StringUtils.isNotEmpty(storage.getUsername()) ||
                    !StringUtils.isNotEmpty(storage.getPassword())) {
                logger.error("param error:{}", JSONObject.toJSONString(storage));
                return new BocloudResult<>(false, "连接存储器失败：参数格式错误");
            }
            CloudVendor vendor = cloudVendorRepository.query(storage.getVendorId());
            if (vendor == null) {
                logger.error("vendor id = [{}]已经被删除", storage.getVendorId());
                return new BocloudResult<>(false, "指定vendorId" + storage.getVendorId() + "不存在");
            }
            StorageModel storageModel = new StorageModel();
            convert(storage, storageModel);
            return storageDriver.connect(vendor.getUuid(), storageModel);
        } catch (Exception e) {
            logger.error("create storage fail:", e);
            return new BocloudResult<>(false, "添加存储器失败");
        }
    }

    @Override
    public BocloudResult<String> remove(Long id, RequestContext context) {
        try {
            Storage dbStorage = storageRepository.get(id);
            if (dbStorage == null) {
                logger.error("id = [{}]的存储器已经被删除", id);
                return new BocloudResult<>(false, "删除存储器失败：存储器已经被删除");
            }
            CloudVendor vendor = cloudVendorRepository.query(dbStorage.getVendorId());
            if (vendor == null) {
                logger.error("vendor id = [{}]已经被删除", dbStorage.getVendorId());
                return new BocloudResult<>(false, "指定vendorId" + dbStorage.getVendorId() + "不存在");
            }
            List<ImageVolume> imageVolumes = imageVolumeRepository.listByStorage(id);
            if (imageVolumes != null && imageVolumes.size() > 0) {
                Set<String> imageNames = new HashSet<>();
                for (ImageVolume imageVolume : imageVolumes) {
                    imageNames.add(imageVolume.getImageName());
                }
                return new BocloudResult<>(false, "删除失败，该存储器存在卷已经被映像使用，请先删除如下映像：" + imageNames.toString());
            }
            List<ServerVolume> serverVolumes = serverVolumeRepository.listByStorageId(id);
            if (serverVolumes != null && serverVolumes.size() > 0) {
                Set<String> serverNames = new HashSet<>();
                for (ServerVolume serverVolume : serverVolumes) {
                    serverNames.add(serverVolume.getServerName());
                }
                return new BocloudResult<>(false, "删除失败，该存储器存在卷已经被附加到云主机，请先删除或取消管理如下云主机：" + serverNames.toString());
            }
            List<Volume> vendorVolumes = volumeRepository.listByVendorId(vendor.getId());
            for (Volume volume : vendorVolumes) {
                if ("error".equals(volume.getStatus()) &&
                        (volume.getStorageId() == null || volume.getStorageId().equals(dbStorage.getId()))) {
                    return new BocloudResult<>(false, "删除失败，请先删除错误状态的存储卷");
                }
            }
            BocloudResult result = storageDriver.remove(vendor.getUuid(), dbStorage.getHost());
            if (result.isFailed()) {
                logger.error("移除存储器失败:{}", result);
                return new BocloudResult<>(false, "移除存储器失败：" + result.getMessage());
            }
            List<Volume> storageVolumes = volumeRepository.listByStorageId(id);
            for (Volume volume : storageVolumes) {
                volumeRepository.remove(volume.getId(), context.getTarget());
            }
            volumePoolRepository.removeByStorage(id);
            volumeTemplateRepository.removeByStorage(id);
            storageRepository.remove(id);
            return new BocloudResult<>(true, "移除存储器" + dbStorage.getName() + "成功");
        } catch (Exception e) {
            logger.error("remove storage fail:", e);
            return new BocloudResult<>(false, "移除存储器失败");
        }
    }

    private void convert(Storage storage, StorageModel storageModel) {
        storageModel.setUuid(storage.getUuid());
        storageModel.setName(storage.getName());
        storageModel.setStatus(storage.getStatus());
        storageModel.setIp(storage.getIp());
        storageModel.setHost(storage.getHost());
        storageModel.setUsername(storage.getUsername());
        AESEncrptor aesEncrptor = new AESEncrptor();
        try {
            storageModel.setPassword(aesEncrptor.decrypt(storage.getPassword(), null));
        } catch (Exception e) {
            storageModel.setPassword(storage.getPassword());
        }
        storageModel.setType(storage.getType());
        storageModel.setTotalCapacity(storage.getTotalCapacity());
        storageModel.setFreeCapaticy(storage.getFreeCapaticy());
        storageModel.setCompressionSuppoerd(storage.getCompressionSuppoerd());
    }

    @Override
    public BocloudResult<GridBean<VolumeModel>> availableVolume(int page, int rows, List<Param> params, Map<String, String> sorter, boolean simple) {
        try {
            Map<String, Object> paramMap = QueryBuilder.getParam(params);
            if (!paramMap.containsKey("storageId")) {
                return new BocloudResult<>(false, "查询存储器上的所有的卷失败: 需要查询参数storageId");
            }
            int storageId = Integer.valueOf(paramMap.get("storageId").toString());
            Storage dbStorage = storageRepository.get(storageId);
            if (dbStorage == null) {
                logger.error("id = [{}]的存储器已经被删除", storageId);
                return new BocloudResult<>(false, "查询存储器上的所有的卷失败: 存储器已经被删除");
            }
            CloudVendor vendor = cloudVendorRepository.query(dbStorage.getVendorId());
            if (vendor == null) {
                logger.error("vendor id = [{}]已经被删除", dbStorage.getVendorId());
                return new BocloudResult<>(false, "查询存储器上的所有的卷失败: 指定vendorId" + dbStorage.getVendorId() + "不存在");
            }
            // 从redis或driver查询数据
            List<VolumeModel> volumes;
            String redisKey = PowervcRedisKeyBuilder.buildForVolume(vendor.getId(), dbStorage.getId());
            ValueOperations<String, String> redisOps = redisTemplate.opsForValue();
            String redisData = redisOps.get(redisKey);
            if (!StringUtils.isEmpty(redisData)) {
                volumes = JSON.parseArray(redisData, VolumeModel.class);
            } else {
                BocloudResult bocloudResult = volumeDriver.allVolumes(vendor.getUuid(), dbStorage.getHost());
                if (bocloudResult.isFailed()) {
                    logger.error("查询存储器上的所有的卷失败: {}", bocloudResult);
                    return bocloudResult;
                }
                volumes = (List<VolumeModel>) bocloudResult.getData();
                // 过滤掉已经被管理进来的
                volumes.removeIf(VolumeModel::getManaged);
                // 放进redis
                redisOps.set(redisKey, JSON.toJSONString(volumes), 30, TimeUnit.SECONDS);
            }
            // 根据是否支持进行过滤
            if (paramMap.containsKey("support")) {
                boolean support = (boolean) paramMap.get("support");
                volumes.removeIf(volumeModel -> !volumeModel.getSupport().equals(support));
            }
            // 根据名称进行过滤
            if (paramMap.containsKey("name")) {
                String name = (String) paramMap.get("name");
                volumes.removeIf(volumeModel -> !volumeModel.getName().contains(name));
            }
            // 按名称排序以便分页
            volumes.sort(Comparator.comparing(VolumeModel::getName));
            // 计算总页数和起止行数
            int totalCount = volumes.size();
            int pageStart = (page - 1) * rows;
            int pageEnd = page * rows;
            // 截取list
            List<VolumeModel> resultVolumes;
            if (pageStart >= totalCount) {
                resultVolumes = new ArrayList<>();
            } else {
                resultVolumes = volumes.subList(pageStart, Math.min(pageEnd, volumes.size()));
            }
            GridBean gridBean = GridHelper.getBean(page, rows, totalCount, resultVolumes);
            return new BocloudResult<>(true, gridBean, "查询存储器上的所有可管理的卷成功");
        } catch (Exception e) {
            logger.error("query available volume fail:", e);
            return new BocloudResult<>(false, "查询存储器上的所有可管理的卷失败");
        }
    }

    @Override
    public BocloudResult<List<Storage>> listByVendor(long vendorId) {
        try {
            List<Storage> storages = storageRepository.listByVendor(vendorId);
            return new BocloudResult<>(true, storages, "查询成功");
        } catch (Exception e) {
            logger.error("查询失败:", e);
            return new BocloudResult<>(false, "查询失败" + e.getMessage());
        }
    }

    @Override
    public BocloudResult<Storage> detail(Long id) {
        try {
            Storage storage = storageRepository.get(id);
            List<Disk> disks = diskRepository.listByStorage(id);
            storage.setDiskCapacity(Math.round(disks.stream().mapToLong(Disk::getCapacity).sum() / 1024D / 1024D / 1024D * 100) / 100D);
            return new BocloudResult<>(true, storage, "查询成功");
        } catch (Exception e) {
            logger.error("查询失败:", e);
            return new BocloudResult<>(false, "查询失败" + e.getMessage());
        }
    }


}
