package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.common.FlavorDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.FlavorExtraSpecsModel;
import com.bocloud.cmp.model.FlavorModel;
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.Sign;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.DateTools;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Flavor;
import com.bocloud.ims.entity.resource.FlavorExtraSpecs;
import com.bocloud.ims.entity.resource.Region;
import com.bocloud.ims.interfaces.resource.FlavorService;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.FlavorExtraSpecsRepository;
import com.bocloud.ims.repository.resource.FlavorRepository;
import com.bocloud.ims.repository.resource.RegionRepository;
import com.bocloud.ims.service.transporter.vendor.PowervcTransporter;
import com.bocloud.ims.service.utils.ExcelExporter;
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.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 配置Service接口实现
 *
 * @author dongkai
 */
@Service("flavorService")
public class FlavorServiceImpl implements FlavorService {

    private static Logger logger = LoggerFactory.getLogger(FlavorServiceImpl.class);
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private FlavorRepository flavorRepository;
    @Autowired
    private FlavorExtraSpecsRepository flavorExtraSpecsRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private FlavorDriver flavorDriver;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private PowervcTransporter powervcTransportor;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private RegionRepository regionRepository;

    private static List<String> vendorTypes = new ArrayList<String>(
            Arrays.asList(new String[]{CloudProvider.ALIYUN.name(), CloudProvider.HUAWEI.name(), CloudProvider.HCSO.name()}));

    @Override
    public BocloudResult<GridBean<Flavor>> 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());
            Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);

            Long serverId = null;
            for (Param param : params) {
                Map<String, Object> paramMap = param.getParam();
                if (paramMap.containsKey("family")) {
                    String family = (String) paramMap.get("family");
                    String[] split = family.split("\\.");
                    paramMap.put("family", split[0]);
                }
                if (paramMap.containsKey("serverId")) {
                    serverId = Long.parseLong(paramMap.get("serverId").toString());
                    paramMap.remove("serverId");
                }
            }
            sorter.put("cpu", Common.ZERO);
            sorter.put("memory", Common.ZERO);
            int total = flavorRepository.count(params);
            List<Flavor> list = new ArrayList<>();
            if (simple) {
                list = flavorRepository.list(params, sorter);
            } else {
                list = flavorRepository.list(page, rows, params, sorter);
            }
            if (ListTool.isEmpty(list)) {
                return new BocloudResult<>(true, GridHelper.getBean(page, rows, total, list), "查询成功");
            }
            Flavor fla = list.get(0);
            CloudVendor cloudVendor = cloudVendorRepository.query(fla.getVendorId());
            for (Flavor flavor : list) {
                if (null != cloudVendor) {
                    flavor.setVendorName(cloudVendor.getName());
                    if (CloudProvider.POWERVC.name().equals(cloudVendor.getType())) {
                        FlavorExtraSpecs extraSpecs = flavorExtraSpecsRepository.query(flavor.getExtraSpecsId());
                        flavor.setExtraSpecs(extraSpecs);
                    }
                }
            }
            // 阿里云/huawei规格变更 需要根据云主机查询可变更的规格
            if (null != serverId && null != cloudVendor && vendorTypes.contains(cloudVendor.getType())) {
            	CloudServer cloudServer = cloudServerRepository.query(serverId);
            	params.add(new Param(MapTools.simpleMap("regionId", cloudServer.getRegionId()), Sign.EQ));
            	list = flavorRepository.list(1, Integer.MAX_VALUE, params, sorter);
                BocloudResult usedFlavorsResult = usedFlavors(cloudServer, cloudVendor, list, pager);
                if (usedFlavorsResult.isFailed()) {
                    return new BocloudResult<>(false, usedFlavorsResult.getMessage());
                }
                JSONObject flavorObject = (JSONObject) usedFlavorsResult.getData();
                list = (List<Flavor>) flavorObject.get("data");
                total = flavorObject.getIntValue("total");
            }
            GridBean gridBean = GridHelper.getBean(page, rows, total, list);
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list flavor failure:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
    }

    public BocloudResult usedFlavors(CloudServer cloudServer, CloudVendor cloudVendor, List<Flavor> list, Pager pager) {
    	JSONObject object = new JSONObject();
    	object.put("total", 0);
    	object.put("data", new ArrayList<>());
        try {
            BocloudResult<List<String>> queryUsedFlavors = flavorDriver.listUsedFlavors(cloudVendor.getUuid(),
                    cloudServer.getInstanceId(), cloudServer.getRegionId());
            if (queryUsedFlavors.isSuccess()) {
                List<String> flavorUuids = queryUsedFlavors.getData();
                if (ListTool.isEmpty(flavorUuids)) {
                    return new BocloudResult<>(true, object, "查询成功！");
                }
                for (int i = list.size() - 1; i >= 0; i--) {
                    if (!flavorUuids.contains(list.get(i).getFlavorUuid())) {
                        list.remove(list.get(i));
                    }
                }
                object.put("total", list.size());
                if(!ListTool.isEmpty(list)) {
                	if(list.size() > pager.getRows() * pager.getPage()) {
                		list = new ArrayList<>(list.subList(pager.getRows() * (pager.getPage() - 1), pager.getRows() * pager.getPage()));
                	} else {
                		list = new ArrayList<>(list.subList(pager.getRows() * (pager.getPage() - 1), list.size()));
                	}
                	object.put("data", list);
                }
            }
        } catch (Exception e) {
        }
        return new BocloudResult<>(true, object, "查询成功");
    }

    @Override
    public BocloudResult<Flavor> detail(Long id) {
        try {
            Flavor flavor = flavorRepository.query(id);
            if (flavor != null) {
                flavor.setExtraSpecs(flavorExtraSpecsRepository.query(flavor.getExtraSpecsId()));
            }
            return new BocloudResult<>(true, flavor, "查询详情成功!");
        } catch (Exception e) {
            logger.error("query flavor detail failure:", e);
            return new BocloudResult<>(false, "查询详情失败!");
        }
    }

    @Override
    public BocloudResult<List<Flavor>> listFlavor(Long vendorId, String name) {
        try {
            String[] names = name.split(",");
            List<Flavor> flavors = new ArrayList<>();
            Flavor flavor = null;
            for (String str : names) {
                flavor = flavorRepository.getByVid(vendorId, str);
                flavors.add(flavor);
            }
            return new BocloudResult<>(true, flavors, "查询成功!");
        } catch (Exception e) {
            logger.error("list Flavor error:", e);
            return new BocloudResult<>(false, "查询失败!");
        }

    }

    @Override
    public BocloudResult<String> create(Flavor flavor, RequestContext context) {
        Long vendorId = flavor.getVendorId();
        BocloudResult<String> result = null;
        try {
            CloudVendor cloudVendor = cloudVendorRepository.query(vendorId);
            if (null == cloudVendor) {
                logger.error("get cloudVendor failure!");
                return new BocloudResult<>(false, "创建失败!");
            }
            FlavorModel model = JSONObject.parseObject(JSON.toJSONString(flavor), FlavorModel.class);
            Flavor flavorCheck = flavorRepository.getByName(model.getName());
            if (null != flavorCheck) {
                return new BocloudResult<>(false, "名称已存在!");
            }
            if (CloudProvider.POWERVC.name().equals(cloudVendor.getType())) {
                if (model.getExtraSpecs().getProcUnits() != null
                        && (model.getCpu() / model.getExtraSpecs().getProcUnits() > 20
                        || model.getCpu() / model.getExtraSpecs().getProcUnits() < 1)) {
                    return new BocloudResult<>(false, "虚拟处理器数和处理单元数的比值需在1-20之间");
                }
                if (model.getDisk() == null) {
                    model.setDisk(0);
                }
            }
            BocloudResult bocloudResult = flavorDriver.create(cloudVendor.getUuid(), model);
            if (bocloudResult.isFailed()) {
                return new BocloudResult<>(false, "计算模板创建失败:" + bocloudResult.getMessage());
            }
            if (CloudProvider.POWERVC.name().equals(cloudVendor.getType())) {
                FlavorModel flavorModel = (FlavorModel) bocloudResult.getData();
                powervcTransportor.insertOrUpdate(cloudVendor, flavorModel, context.getTarget());
            } else {
                Flavor createflavor = JSONObject.parseObject(JSONObject.toJSONString(bocloudResult.getData()),
                        Flavor.class);
                createflavor.setVendorId(vendorId);
                flavorRepository.save(createflavor);
            }
            // 发送给云平台统计队列对云平台重新统计
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(Common.VENDORID, vendorId);
            amqpTemplate.convertAndSend("vendor.statistics.queue", jsonObject);
            result = new BocloudResult<>(true, "创建成功!");
        } catch (Exception e) {
            logger.error("create flavor failure:", e);
            return new BocloudResult<>(false, "创建失败!");
        }
        return result;
    }

    @Override
    public BocloudResult<String> remove(Long id, RequestContext context) {
        String path = Flavor.class.getSimpleName() + "_" + id;
        Boolean result = false;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Flavor flavor = flavorRepository.query(id);
            if (null == flavor) {
                logger.warn("flavor  data not exist");
                return new BocloudResult<>(false, "数据不存在", "", "");
            }
            // 云供应商
            CloudVendor cloudVendor = cloudVendorRepository.query(flavor.getVendorId());
            BocloudResult bocloudResult = flavorDriver.remove(flavor.getFlavorUuid(), cloudVendor.getUuid());
            if (null == bocloudResult || !bocloudResult.isSuccess()) {
                return new BocloudResult<>(false, "删除失败");
            }
            result = flavorExtraSpecsRepository.removeByFlavorId(id);
            if (!result) {
                new BocloudResult<String>(false, "删除失败");
            }
            result = flavorRepository.delete(id);
            // 发送给云平台统计队列对云平台重新统计
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(Common.VENDORID, cloudVendor.getId());
            amqpTemplate.convertAndSend("vendor.statistics.queue", jsonObject);
        } catch (Exception e) {
            logger.error("remove flavor fail:", e);
            return new BocloudResult<>(false, "删除失败", "", "");
        }
        return result ? new BocloudResult<>(true, "删除成功") : new BocloudResult<>(false, "删除失败", "", "");
    }

    @Override
    public BocloudResult<String> modify(Flavor flavorParam, RequestContext context) {
        Long id = flavorParam.getId();// 规格ID
        String name = flavorParam.getName();// 规格名称
        Integer cpu = flavorParam.getCpu();// cpu
        BigDecimal memory = flavorParam.getMemory();// 内存
        Integer disk = flavorParam.getDisk(); // 磁盘
        String path = Flavor.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Flavor flavor = flavorRepository.query(id);
            if (null == flavor) {
                logger.warn("flavor data not exist");
                return new BocloudResult<>(false, "数据不存在", "", "");
            }
            // 云供应商
            CloudVendor cloudVendor = cloudVendorRepository.query(flavor.getVendorId());
            if (null == cloudVendor) {
                logger.warn("cloud vendor data not exist");
                return new BocloudResult<>(false, "云平台不存在", "", "");
            }
            if (CloudProvider.OPENSTACK.name().equals(cloudVendor.getType())||CloudProvider.EASYSTACK.name().equals(cloudVendor.getType())) {
            	if(StringUtils.isEmpty(flavor.getFlavorUuid())) {
            		flavorRepository.delete(id);
            		return new BocloudResult<>(false, "数据异常，请同步后再次操作");
            	}
            }
            FlavorModel flavorModel = new FlavorModel();
            flavorModel.setFlavorUuid(flavor.getFlavorUuid());
            flavorModel.setName(name);
            flavorModel.setCpu(cpu);
            flavorModel.setMemory(memory);
            flavorModel.setDisk(disk);
            if (CloudProvider.POWERVC.name().equals(cloudVendor.getType())) {
                Double procUnits = flavorParam.getExtraSpecs() == null ? null
                        : flavorParam.getExtraSpecs().getProcUnits();
                String procCompatibility = flavorParam.getExtraSpecs() == null ? null
                        : flavorParam.getExtraSpecs().getProcCompatibility();
                if (procUnits != null && (cpu / procUnits > 20 || cpu / procUnits < 1)) {
                    return new BocloudResult<>(false, "虚拟处理器数和处理单元数的比值需在1-20之间");
                }
                if (flavorModel.getDisk() == null) {
                    flavorModel.setDisk(0);
                }
                FlavorExtraSpecsModel flavorExtra = new FlavorExtraSpecsModel();
                flavorExtra.setProcUnits(procUnits);
                flavorExtra.setProcCompatibility(procCompatibility);
                flavorModel.setExtraSpecs(flavorExtra);
            }

            BocloudResult bocloudResult = flavorDriver.update(cloudVendor.getUuid(), flavorModel);
            if (bocloudResult.isFailed()) {
                return new BocloudResult<>(false, bocloudResult.getMessage());
            }
            if (CloudProvider.POWERVC.name().equals(cloudVendor.getType())) {
                FlavorModel model = (FlavorModel) bocloudResult.getData();
                flavor.setFlavorUuid(model.getFlavorUuid());
                flavorRepository.update(flavor);
                powervcTransportor.insertOrUpdate(cloudVendor, model, context.getTarget());
            } else {
                FlavorModel model = JSONObject.parseObject(JSONObject.toJSONString(bocloudResult.getData()),
                        FlavorModel.class);
                BeanUtils.copyProperties(model, flavor);
                flavorRepository.update(flavor);
            }
            return new BocloudResult<>(true, "修改成功");
        } catch (Exception e) {
            logger.error("update flavor fail:", e);
            return new BocloudResult<>(false, "修改失败");
        }
    }

    @Override
    public BocloudResult<List<Flavor>> listByVendor(Long vendorId, String status) {
        try {
            List<Flavor> list = flavorRepository.listByVendorId(vendorId);
            List<Flavor> result = new ArrayList<>();
            for (Flavor flavor : list) {
                if (StringUtils.isEmpty(status) || flavor.getStatus().equals(status)) {
                    flavor.setExtraSpecs(flavorExtraSpecsRepository.query(flavor.getExtraSpecsId()));
                    result.add(flavor);
                }
            }
            return new BocloudResult<>(true, result, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message:", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<String> batchRemove(List<Long> idArray, RequestContext context) {
        if (ListTool.isEmpty(idArray)) {
            return new BocloudResult<>(false, "没有需要删除的规格！");
        }
        try {
            List<BocloudResult<String>> succList = Lists.newArrayList();
            List<BocloudResult<String>> failList = Lists.newArrayList();
            Long vendorId = null;
            for (Long id : idArray) {
                Flavor flavor = flavorRepository.query(id);
                if (null != flavor) {
                    if (null == vendorId) {
                        vendorId = flavor.getVendorId();
                    }
                    BocloudResult<String> result = remove(id, context);
                    if (result.isSuccess()) {
                        succList.add(new BocloudResult<>(true, "【" + flavor.getName() + "】删除成功"));
                    } else {
                        failList.add(new BocloudResult<>(false,
                                "【" + flavor.getName() + "】删除失败：" + result.getMessage()));
                    }
                }
            }
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            String operate = vendor.getType().toLowerCase() + ".flavor";
            // 删除成功的数量
            int success = succList.size();
            // 删除失败的数量
            int fail = failList.size();
            String content = null;
            if (fail == idArray.size()) {
                content = "规格批量删除失败，详情如下：\n";
                for (BocloudResult<String> bocloudResult : failList) {
                    content += bocloudResult.getMessage() + "\n";
                }
                resourceEventPublisher
                        .send(new OperateResult(false, content, operate, MapTools.simpleMap("vendorId", vendor.getId()),
                                OperateResult.OperateCategory.ResourceEvent, context));
            } else {
                content = "规格批量删除成功【" + success + "】个,失败【" + fail + "】个，详情如下：\n";
                for (BocloudResult<String> bocloudResult : succList) {
                    content += bocloudResult.getMessage() + "\n";
                }
                for (BocloudResult<String> bocloudResult : failList) {
                    content += bocloudResult.getMessage() + "\n";
                }
                resourceEventPublisher
                        .send(new OperateResult(true, content, operate, MapTools.simpleMap("vendorId", vendor.getId()),
                                OperateResult.OperateCategory.ResourceEvent, context));
            }
        } catch (Exception e) {
            logger.error("batch remove flavor fail:", e);
            return new BocloudResult<>(false, "批量删除任务下发失败");
        }
        return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
    }

    @Override
    public BocloudResult<List<Flavor>> condition(String condition, RequestContext context) {
        condition = Optional.ofNullable(condition).orElse("");
        JSONObject object = JSONObject.parseObject(condition);
        if ("listFlavor".equalsIgnoreCase(object.getString("condition"))) {
            return this.listFlavor(object.getLong(Common.ID), object.getString(Common.NAME));
        } else if ("listByVendor".equalsIgnoreCase(object.getString("condition"))) {
            return this.listByVendor(object.getLong("vendorId"), object.getString("status"));
        }
        return new BocloudResult<>(false, "参数格式错误");
    }

    @Override
    public BocloudResult<Void> status(Long id, String status) {
        try {
            Flavor flavor = flavorRepository.query(id);
            if (ObjectUtils.isEmpty(flavor)) {
                return new BocloudResult<>(false, "数据不存在");
            }
            flavor.setStatus(status);
            flavorRepository.update(flavor);
            return new BocloudResult<>(true, "规格上下架成功！");
        } catch (Exception e) {
            logger.error("updata status flavor fail:", e);
            return new BocloudResult<>(false, "规格上下架失败");
        }
    }

    @Override
    public void downloadTemplate(HttpServletResponse response, Long vendorId) {
        CloudVendor vendor = cloudVendorRepository.query(vendorId);
        if (ObjectUtils.isEmpty(vendor)) {
            logger.error("get cloudVendor failure");
        }
        List<Region> regionList = regionRepository.list(vendorId);
        String[] regions = new String[regionList.size()];
        for (int i = 0; i < regionList.size(); i++) {
            regions[i] = regionList.get(i).getRegionId();
        }

        List<List<Object>> firstFewRows = new ArrayList<>();
        // excel标题行
        String[] headers = new String[]{"规格名称"};
        String[] dataColumns = new String[]{"name"};
        // 样例数据
        List<Map<String, Object>> allRows = new ArrayList<>();
        Map<String, Object> row;
        row = new HashMap<>(2);
        row.put(dataColumns[0], "Standard_A1_v2");
        allRows.add(row);
        // 文件名
        String timeStr = DateTools.formatTime2String(new Date(), "yyyy-MM-dd-HH-mm-ss");
        String fileName = "flavor-template-" + timeStr + ".xls";

        String header = "attachment; filename=\"" + fileName + "\"";
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", header);
        try {
            OutputStream out = response.getOutputStream();
            ExcelExporter.azureFlavorExport("Sheet", firstFewRows, headers, dataColumns, allRows, out, "yyyy-MM-dd HH:mm:ss", regions);
            out.flush();
            out.close();
            logger.info("模板下载成功！");
        } catch (IOException e) {
            logger.error("export excel file fails.", e);
        }
    }

    @Override
    public BocloudResult<Map<String, String>> importCreate(List<Map<String, String>> flavors, Long vendorId) {
        try {
            logger.info("Excel总数据：{}", flavors.size());
            int successNum = 0;
            int failureNum = 0;
            CloudVendor vendor = cloudVendorRepository.query(vendorId);
            if (ObjectUtils.isEmpty(vendor)) {
                logger.error("get cloudVendor failure");
                return new BocloudResult(false, "平台验证失败");
            }
            //获取excle数据集合
            Map<String, Map<String, String>> maps = flavors.stream().collect(Collectors.toMap(entity -> entity.get("name").toLowerCase(), entity -> entity));

            List<Flavor> flavorList = flavorRepository.listByVendorId(vendorId);
            logger.info("数据库规格数据：{}", flavorList.size());
            for (Flavor flavor : flavorList) {
                if(!flavor.getAvailable()){
                    continue;
                }
                if (maps.containsKey(flavor.getName().toLowerCase())) {
                    flavor.setStatus("IN_USE");
                    try {
                        flavorRepository.update(flavor);
                        successNum++;
                    } catch (Exception e) {
                        failureNum++;
                    }
                }
            }

            String message = new StringBuffer().append(" Excel总数据：")
                    .append(flavors.size())
                    .append(" 导入成功数量:")
                    .append(successNum)
                    .append(" 导入失败数量:")
                    .append(failureNum).toString();
            Map<String, String> map = new HashMap<>();
            map.put("data", message);
            logger.info("导入完成：{}", message);
            return new BocloudResult<>(true,map, message );
        } catch (Exception e) {
            logger.error("importCreate flavor error!", e);
            return new BocloudResult<>(false, "导入失败:" + e.getMessage());
        }
    }
}
