package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.model.RackDevice;
import com.bocloud.common.enums.BaseStatus;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.Rack;
import com.bocloud.ims.entity.resource.Server;
import com.bocloud.ims.entity.resource.ServerAssets;
import com.bocloud.ims.interfaces.resource.RackService;
import com.bocloud.ims.repository.resource.DictionaryRepository;
import com.bocloud.ims.repository.resource.RackRepository;
import com.bocloud.ims.repository.resource.ServerAssetsRepository;
import com.bocloud.ims.repository.resource.ServerRepository;
import com.bocloud.ims.service.utils.ParamHelper;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 机柜抽象Service接口实现类
 *
 * @author wei wei
 * @version 1.0
 * @since 2016.02.27
 */
@Service("rackService")
public class RackServiceImpl implements RackService {

    private static Logger logger = LoggerFactory.getLogger(RackServiceImpl.class);
    @Autowired
    private RackRepository rackRepository;
    @Autowired
    private ServerRepository serverRepository;
    @Autowired
    private ServerAssetsRepository serverAssetsRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private DictionaryRepository dictionaryRepository;

    @Override
    public BocloudResult<Rack> create(Rack rack, Long userId) {
        try {
            boolean flag = rackRepository.isNameRepeat(rack.getName(), rack.getRoomId());
            if (flag) {
                return new BocloudResult<>(false, "同一机房下的机柜名称不能重复");
            }
            rack.setCreatorId(userId);
            rack.setMenderId(userId);
            rack.setStatus(BaseStatus.NORMAL.name());
            rackRepository.save(rack);
        } catch (Exception e) {
            logger.error("Create rack fail:", e);
            return new BocloudResult<>(false, "添加失败");
        }
        return new BocloudResult<>(true, rack, "添加成功");
    }

    @Override
    public BocloudResult<String> modify(Rack bean, Long userId) {
        String path = Rack.class.getSimpleName() + "_" + bean.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Rack rack = this.rackRepository.query(bean.getId());
            if (null == rack) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }
            // 若机柜名称被修改，则新名称不能重复
            if (!rack.getName().equals(bean.getName())) {
                boolean flag = rackRepository.isNameRepeat(bean.getName(), rack.getRoomId());
                if (flag) {
                    return new BocloudResult<>(false, "同一机房下的机柜名称不能重复");
                }
            }
            // 若机房被修改，则机柜名称不能为新机房下已存在名称
            if (!rack.getRoomId().equals(bean.getRoomId())) {
                boolean flag = rackRepository.isNameRepeat(bean.getName(), bean.getRoomId());
                if (flag) {
                    return new BocloudResult<>(false, "新机房下存在重复名称的机柜，请先修改机柜名称再修改机房");
                }
            }
            BeanUtils.copyProperties(bean, rack);
            rack.setMenderId(userId);
            this.rackRepository.update(rack);
            return new BocloudResult<>(true, "修改成功");
        } catch (Exception e) {
            logger.error("Modify rack fail:", e);
            return new BocloudResult<>(false, "修改失败", null, null);
        }
    }

    @Override
    public BocloudResult<String> remove(Long id, Long userId) {
        String path = Rack.class.getSimpleName() + "_" + id;
        int count = 0;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Rack object = this.rackRepository.query(id);
            if (null == object) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }

            Map<String, Object> map = new HashMap<String, Object>();
            map.put(Common.RACK, id);
            count = this.serverRepository.count(ParamHelper.getParams(map, Sign.EQ), new ArrayList<>());
            if (count > 0) {
                logger.warn("Remove fail：have servers");
                return new BocloudResult<>(false, "删除失败：该机柜下存在主机，无法删除");
            }

            boolean result = this.rackRepository.remove(id, userId);
            if (result) {
                return new BocloudResult<>(true, "删除成功");
            }
            logger.info("Rack has been removed");
            return new BocloudResult<>(true, "已被删除");
        } catch (Exception e) {
            logger.error("Remove rack fail:", e);
            return new BocloudResult<>(false, "删除失败", null, null);
        }
    }

    @Override
    public BocloudResult<Rack> basic(Long id) {
        Rack rack = null;
        try {
            rack = this.rackRepository.getRackBean(id);
            if (null == rack) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, rack, "该机柜不存在");
            }
            rack.setTypeName(dictionaryRepository.getNameByValue(rack.getType()));
            rack.setMaterialName(dictionaryRepository.getNameByValue(rack.getMaterial()));
        } catch (Exception e) {
            logger.error("Query rack fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, rack, "查询成功");
    }

    @Override
    public BocloudResult<GridBean<Rack>> list(Pager pager, RequestContext context) {
        try {
            List<Rack> list = null;
            List<SimpleBean> beans = null;
            int total = 0;
            GridBean gridBean = null;
            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);
            total = this.rackRepository.count(params);
            if (simple) {
                beans = this.rackRepository.list(params, sorter);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = this.rackRepository.list(page, rows, params, sorter);
                List<Rack> racks = new ArrayList<>();
                for (Rack rack : list) {
                    // int alarmTotal = 0;
                    rack.setTypeName(dictionaryRepository.getNameByValue(rack.getType()));
                    rack.setMaterialName(dictionaryRepository.getNameByValue(rack.getMaterial()));
                    List<Server> serverList = serverRepository.listByRack(rack.getId());
                    rack.setServers(serverList.size());
                    // 计算机柜使用率
                    Integer unit = rack.getUnit();
                    if (null == unit || 0 == unit) {
                        unit = 1;
                    }
                    BigDecimal usage = BigDecimal.valueOf(serverList.size()).divide(BigDecimal.valueOf(unit),2, RoundingMode.HALF_UP);
                    rack.setUsage(usage);
                    racks.add(rack);
                }
                gridBean = GridHelper.getBean(page, rows, total, racks);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("Query rack list fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
    }

    @Override
    public BocloudResult<GridBean<Server>> listServers(int page, int rows, List<Param> params,
                                                       Map<String, String> sorter) {
        List<Server> list = null;
        int total = 0;
        GridBean gridBean = null;
        try {
            list = this.serverRepository.list(page, rows, params, sorter, new ArrayList<>());
            total = this.serverRepository.count(params, new ArrayList<>());
            gridBean = GridHelper.getBean(page, rows, total, list);
        } catch (Exception e) {
            logger.error("Query server of rack list fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, gridBean, "查询成功");
    }

    @Override
    public BocloudResult detail(Long id) {
        Rack rack = null;
        try {
            if (id == 0) {
                rack = new Rack();
                rack.setRacks(this.rackRepository.count(null));
                rack.setServers(this.serverRepository.count(null, new ArrayList<>()));
            } else {
                rack = this.rackRepository.getRackBean(id);
                if (null == rack) {
                    logger.warn("Rack does not exist!");
                    return new BocloudResult<>(true, rack, "该机柜不存在");
                }
                rack.setTypeName(dictionaryRepository.getNameByValue(rack.getType()));
                rack.setMaterialName(dictionaryRepository.getNameByValue(rack.getMaterial()));

                Map<String, Object> map = new HashMap<String, Object>();
                map.put(Common.RACK, id);
                Integer servers = this.serverRepository.count(ParamHelper.getParams(map, Sign.EQ), new ArrayList<>());
                rack.setServers(servers);
                // 计算机柜使用率
                Integer unit = rack.getUnit();
                if (null == unit || 0 == unit) {
                    unit = 1;
                }
                BigDecimal usage = BigDecimal.valueOf(servers).divide(BigDecimal.valueOf(unit),2, RoundingMode.HALF_UP);
                rack.setUsage(usage);
            }
        } catch (Exception e) {
            logger.error("Query rack detail fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, rack, "查询成功");
    }

    @Override
    public BocloudResult inRoom(Long id, Long roomId, Long userId) {
        String path = Rack.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            this.rackRepository.belongRoom(id, roomId, userId);
            return new BocloudResult<>(true, "添加成功");
        } catch (Exception e) {
            logger.error("Join rack to room fail:", e);
            return new BocloudResult<>(false, "添加失败");
        }
    }

    @Override
    public BocloudResult outRoom(Long rackId, Long userId) {
        String path = Rack.class.getSimpleName() + "_" + rackId;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            this.rackRepository.releaseRoom(rackId, userId);
            return new BocloudResult<>(true, "移除成功");
        } catch (Exception e) {
            logger.error("Remove rack from room fail:", e);
            return new BocloudResult<>(false, "移除失败");
        }
    }

    @Override
    public BocloudResult freeRacks() {
        List<Rack> list = null;
        try {
            list = this.rackRepository.listFree();
        } catch (Exception e) {
            logger.error("Query free rack list fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, list, "查询成功");
    }

    @Override
    public BocloudResult freeRacks(Long roomId) {
        List<Rack> list = null;
        try {
            list = this.rackRepository.listFree(roomId);
        } catch (Exception e) {
            logger.error("Query free rack list fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, list, "查询成功");
    }

    @Override
    public BocloudResult checkRange(Long id, Integer slotAmount) {
        try {
            Rack rack = this.rackRepository.query(id);
            if (null == rack) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, "该机柜不存在");
            }

            if (slotAmount <= 0) {
                logger.warn("Can not be less than or equal to 0!");
                return new BocloudResult<>(false, "卡槽数量不可小于等于0");
            }

            if (slotAmount >= rack.getSlotAmount()) {
                return new BocloudResult<>(true, "卡槽数量可以变动");
            }

            List<RackDevice> list = this.serverRepository.positions(id, Common.ONE);
            if (list.isEmpty()) {
                return new BocloudResult<>(true, "卡槽数量可以变动");
            }

            RackDevice rackDevice = list.get(0);
            int slotStart = rackDevice.getSlotStart();
            int slotNum = rackDevice.getSlotNum();
            if (slotStart > slotAmount) {
                logger.warn("Slot does not meet the conditions!");
                return new BocloudResult<>(false, "当前卡槽数量小于机柜被占用的最大起始数，数量建议值为" + (slotStart + slotNum));
            }

            if ((slotStart + slotNum - 1) > slotAmount) {
                logger.warn("Slot does not meet the conditions!");
                return new BocloudResult<>(false, "当前卡槽数量小于机柜被占用的最大起始数加其所占卡槽数量，数量建议值为" + (slotStart + slotNum));
            }

            return new BocloudResult<>(true, "卡槽数量可以变动");
        } catch (Exception e) {
            logger.error("Check slot range fail:", e);
            return new BocloudResult<>(false, "卡槽数量不可变动");
        }
    }

    @Override
    public BocloudResult checkSlot(Long id, Integer slotStart, Integer slotNum, Long serverId) {
        try {
            Rack rack = this.rackRepository.query(id);
            if (null == rack) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, "该机柜不存在");
            }
            if (null != slotStart && null != slotNum) {

            }
            int slotEnd = slotStart + slotNum - 1;
            if (null != serverId) {
                Server server = this.serverRepository.query(serverId);
                ServerAssets serverAssets = serverAssetsRepository.query(server.getId());
                if (null == serverAssets) {
                    logger.warn("serverAssets does not exist!");
                    return new BocloudResult<>(false, "该设备不存在");
                }
                int oldStart = serverAssets.getSlotStart();
                int oldEnd = oldStart + serverAssets.getSlotNum() - 1;
                if (slotStart >= oldStart && slotStart < oldEnd && slotEnd > oldStart && slotEnd <= oldEnd) {
                    return new BocloudResult<>(true, "可添加服务器");
                }
            }

            if (slotStart < 1 || slotNum < 1 || (slotStart + slotNum - 1) < 1) {
                logger.warn("Slot parameters error!");
                return new BocloudResult<>(false, "卡槽参数错误");
            }

            int amount = rack.getSlotAmount();

            if (slotStart > amount || slotNum > amount || (slotStart + slotNum - 1) > amount) {
                logger.warn("Slot beyond the scope!");
                return new BocloudResult<>(false, "服务器所占的卡槽超出机柜的卡槽范围");
            }

            List<RackDevice> list = this.serverRepository.positions(id, Common.ONE);
            if (list.isEmpty()) {
                return new BocloudResult<>(true, "可添加服务器");
            }

            boolean flag = true;

            int start = 0;
            int end = 0;
            for (RackDevice device : list) {
                start = device.getSlotStart();
                end = device.getSlotStart() + device.getSlotNum() - 1;
                if (slotStart >= start && slotStart <= end) {
                    flag = false;
                    break;
                }

                if (start >= slotStart && start <= slotEnd) {
                    flag = false;
                    break;
                }

                if (slotEnd >= start && slotEnd <= end) {
                    flag = false;
                    break;
                }

                if (end >= slotStart && end <= slotEnd) {
                    flag = false;
                    break;
                }
            }

            if (flag) {
                return new BocloudResult<>(true, "可添加服务器");
            } else {
                logger.warn("Slot parameters error!");
                return new BocloudResult<>(false, "卡槽" + start + "到" + end + "已被占用");
            }

        } catch (Exception e) {
            logger.error("Check slot fail:", e);
            return new BocloudResult<>(false, "不可添加服务器");
        }
    }

    @Override
    public BocloudResult freeNodes(Long id) {
        List<Server> list = null;
        try {
            list = this.serverRepository.listFreeNode(id, Rack.class);
        } catch (Exception e) {
            logger.error("Query free server list from rack fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, list, "查询成功");
    }

    @Override
    public BocloudResult freeManagers(Long id) {
        List<Server> list = null;
        try {
            list = this.serverRepository.listFreeManager(id, Rack.class);
        } catch (Exception e) {
            logger.error("Query free server list from rack fail:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
        return new BocloudResult<>(true, list, "查询成功");
    }

    @Override
    public BocloudResult slots(Long id) {
        try {
            Rack rack = this.rackRepository.query(id);
            if (null == rack) {
                logger.warn("Rack does not exist!");
                return new BocloudResult<>(false, "该机柜不存在");
            }

            int amount = rack.getSlotAmount();

            List<RackDevice> devices = this.serverRepository.positions(id, Common.ZERO);
            Map<Integer, Boolean> slots = new HashMap<Integer, Boolean>();
            int start = 0;
            int num = 0;
            for (RackDevice device : devices) {
                start = device.getSlotStart();
                num = device.getSlotNum();
                for (int i = 0; i < num; i++) {
                    slots.put(start + i, false);
                }
            }

            for (int i = 1; i <= amount; i++) {
                if (slots.containsKey(i)) {
                    continue;
                }
                slots.put(i, true);
            }

            List<Integer> keys = new ArrayList<Integer>(slots.keySet());
            Collections.sort(keys);
            Map<Integer, Boolean> map = new HashMap<Integer, Boolean>();
            for (Integer key : keys) {
                map.put(key, slots.get(key));
            }
            return new BocloudResult<>(false, map, "查询成功");
        } catch (Exception e) {
            logger.error("Check slot fail:", e);
            return new BocloudResult<>(false, "不可添加服务器");
        }
    }

    @Override
    public List<String> listServersRackIps(Long id) {
        try {
            return serverRepository.rackIps(id);
        } catch (Exception e) {
            logger.error("listServersRackIps fail!", e);
            return null;
        }
    }

    @Override
    public List<String> listServersRoomIps(Long id) {
        try {
            return serverRepository.roomIps(id);
        } catch (Exception e) {
            logger.error("listServersRoomIps fail!", e);
            return null;
        }
    }

    @Override
    public List<String> listServersDCIps(Long id) {
        try {
            return serverRepository.dcIps(id);
        } catch (Exception e) {
            logger.error("listServersRoomIps fail!", e);
            return null;
        }
    }

    @Override
    public BocloudResult<Map<String, Object>> resources(Long id) {
        Map<String, Object> maps = Maps.newHashMap();
        List<Param> params = Lists.newArrayList();
        try {
            params.add(new Param(MapTools.simpleMap("rackId", id), Sign.EQ));
            List<Server> servers = this.serverRepository.list(1, Integer.MAX_VALUE, params, null, new ArrayList<>());
            maps.put("server", servers);
        } catch (Exception e) {
            logger.error("list resource fail!", e);
            return new BocloudResult<>(false, "查询失败");
        }
        return new BocloudResult<>(true, maps, "查询成功");
    }

    @Override
    public BocloudResult details(Long id, String action) {
        if ("basic".equalsIgnoreCase(action)) {
            return this.basic(id);
        }
        if ("resources".equalsIgnoreCase(action)) {
            return this.resources(id);
        }
        return this.detail(id);
    }

    @Override
    public BocloudResult<String> operations(RequestContext context, String params, String action) {
        return new BocloudResult<>(false, "无效的操作类型！");
    }

    @Override
    public BocloudResult<List<Rack>> condition(String condition, RequestContext context) {
        condition = Optional.ofNullable(condition).orElse("");
        JSONObject conditionObj = JSONObject.parseObject(condition);
        String conditionType = conditionObj.getString("condition");
        if ("listRacks".equalsIgnoreCase(conditionType)) {
            List<Rack> racks = rackRepository.list(conditionObj.getLong("roomId"), conditionObj.getBoolean("simple"));
            return new BocloudResult<>(true, racks, "查询成功");
        }
        return new BocloudResult<>(false, "参数格式错误");
    }
}
