package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.model.Scope;
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.DataCenter;
import com.bocloud.ims.entity.resource.Rack;
import com.bocloud.ims.entity.resource.Room;
import com.bocloud.ims.entity.resource.Server;
import com.bocloud.ims.interfaces.resource.RoomService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.ParamHelper;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.api.client.util.Maps;
import org.apache.commons.lang3.StringUtils;
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.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 机房抽象Service接口实现类
 *
 * @author wei wei
 * @version 1.0
 * @since 2016.02.27
 */
@Service("roomService")
public class RoomServiceImpl implements RoomService {

	private static Logger logger = LoggerFactory.getLogger(RoomServiceImpl.class);
	@Autowired
	private DataCenterRepository dataCenterRepository;
	@Autowired
	private RoomRepository roomRepository;
	@Autowired
	private RackRepository rackRepository;
	@Autowired
	private ServerRepository serverRepository;
	@Autowired
	private DictionaryRepository dictionaryRepository;
	@Autowired
	private LockFactory lockFactory;

	/**
	 * 校验机房面积
	 *
	 * @param center
	 * @param totalArea
	 * @param roomId
	 * @return
	 * @throws Exception
	 */
	private BocloudResult checkArea(DataCenter center, String totalArea, Long roomId) throws Exception {
		Long dcTotalArea = StringUtils.isEmpty(center.getTotalArea()) ? 0 : Long.valueOf(center.getTotalArea());
		Long roomTotalArea = 0L;
		List<Room> rooms = roomRepository.queryByDcId(center.getId());
		for (Room room : rooms) {
			if (room.getId().equals(roomId)) {
				continue;
			}
			Long roomTotal = StringUtils.isEmpty(room.getTotalArea()) ? 0 : Long.valueOf(room.getTotalArea());
			roomTotalArea += roomTotal;
		}
		Long newRoomArea = StringUtils.isEmpty(totalArea) ? 0 : Long.valueOf(totalArea);
		if (roomTotalArea + newRoomArea > dcTotalArea) {
			return new BocloudResult<>(false, "机房总面积不能超过数据中心总面积");
		}
		return new BocloudResult<>(true, "校验通过");
	}

	@Override
	public BocloudResult<Room> create(Room room, Long userId) {
		try {
			DataCenter center = this.dataCenterRepository.query(room.getDcId());
			if (null == center) {
				return new BocloudResult<>(false, "数据中心不存在");
			}
			boolean flag = roomRepository.isNameRepeat(room.getName(), room.getDcId());
			if (flag) {
				return new BocloudResult<>(false, "同一数据中心下的机房名称不能重复");
			}
			BocloudResult result = this.checkArea(center, room.getTotalArea(), null);
			if (result.isFailed()) {
				return result;
			}
			room.setCreatorId(userId);
			room.setMenderId(userId);
			room.setStatus(BaseStatus.NORMAL.name());
			room.setTotalArea(StringUtils.isEmpty(room.getTotalArea()) ? "" : room.getTotalArea());
			roomRepository.save(room);
			return new BocloudResult<>(true, room, "添加成功");
		} catch (Exception e) {
			logger.error("Create room fail:", e);
			return new BocloudResult<>(false, e.getMessage(), "", "");
		}
	}

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

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

			Map<String, Object> map = MapTools.simpleMap(Common.ROOM, id);
			countRack = this.rackRepository.count(ParamHelper.getParams(map, Sign.EQ));
			if (countRack > 0) {
				logger.warn("Remove fail：have racks");
				return new BocloudResult<>(false, "删除失败：该机房下存在机柜，无法删除", "", "");
			}

			boolean result = this.roomRepository.remove(id, userId);
			return result ? new BocloudResult<>(true, "删除成功") : new BocloudResult<>(true, "已被删除");
		} catch (Exception e) {
			logger.error("Remove room fail:", e);
			return new BocloudResult<>(false, "删除失败", null, null);
		}
	}

	@Override
	public BocloudResult<Room> basic(Long id) {
		Room room = null;
		try {
			room = this.roomRepository.getRoomBean(id);
			if (null == room) {
				logger.warn("Room does not exist!");
				return new BocloudResult<>(false, room, "该机房不存在");
			}
			room.setTypeName(dictionaryRepository.getNameByValue(room.getType()));
		} catch (Exception e) {
			logger.error("Query room fail:", e);
			return new BocloudResult<>(false, "", null, null);
		}
		return new BocloudResult<>(true, room, "查询成功");
	}

	@Override
	public BocloudResult<GridBean<Room>> list(Pager pager, RequestContext context) {
		try {
			List<Room> 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.roomRepository.count(params);
			if (simple) {
				beans = this.roomRepository.list(params, sorter);
				gridBean = new GridBean(1, 1, total, beans);
			} else {
				list = this.roomRepository.list(page, rows, params, sorter);
				List<Room> rooms = new ArrayList<Room>();
				for (Room room : list) {
					room.setTypeName(dictionaryRepository.getNameByValue(room.getType()));
					List<Rack> racks = rackRepository.listFree(room.getId());
					if (racks == null || racks.size() == 0) {
						room.setRacks(0);
						room.setServers(0);
						rooms.add(room);
					} else {
						room.setRacks(racks.size());
						for (Rack rack : racks) {
							room.setServers(serverRepository.listByRack(rack.getId()).size());
						}
						rooms.add(room);
					}
				}
				gridBean = GridHelper.getBean(page, rows, total, rooms);
			}
			return new BocloudResult<>(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("Query room list fail:", e);
			return new BocloudResult<>(false, "查询失败", null, null);
		}
	}

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

	@Override
	public BocloudResult<Room> detail(Long id) {
		Room room = null;
		try {
			if (id == 0) {
				room = new Room();
				room.setRooms(this.roomRepository.count(null));
				room.setRacks(this.rackRepository.count(null));
				room.setServers(this.serverRepository.count(null, new ArrayList<>()));
			} else {
				room = this.roomRepository.getRoomBean(id);
				if (null == room) {
					logger.warn("Room does not exist!");
					return new BocloudResult<>(true, room, "该机房不存在");
				}
				room.setTypeName(dictionaryRepository.getNameByValue(room.getType()));
				Map<String, Object> roomMap = MapTools.simpleMap(Common.ROOM, id);
				List<Rack> rackList = this.rackRepository.list(1, Integer.MAX_VALUE,
						ParamHelper.getParams(roomMap, Sign.EQ), null);
				int servers = 0;
				if (null != rackList && !rackList.isEmpty()) {
					Map<String, Object> rackMap = new HashMap<String, Object>();
					for (Rack rack : rackList) {
						rackMap.put(Common.RACK, rack.getId());
						servers = servers + this.serverRepository.count(ParamHelper.getParams(rackMap, Sign.EQ),
								new ArrayList<>());
					}
				}
				room.setRacks(this.rackRepository.count(ParamHelper.getParams(roomMap, Sign.EQ)));
				room.setServers(servers);
			}
		} catch (Exception e) {
			logger.error("Query room detail fail:", e);
			return new BocloudResult<>(false, "查询失败", null, null);
		}
		return new BocloudResult<>(true, room, "查询成功");
	}

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

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

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

	public BocloudResult checkRange(Room room, Integer rowNum, Integer colNum) {
		try {
			if (rowNum < 0 || colNum < 0) {
				logger.warn("Can not be less than 0");
				return new BocloudResult<>(false, "行数或列数不能小于0");
			}

			if (rowNum >= room.getRowNum() && colNum >= room.getColNum()) {
				return new BocloudResult<>(true, "范围可以变动");
			}

			Scope scope = this.rackRepository.range(room.getId());
			if (null == scope) {
				return new BocloudResult<>(true, "范围可以变动");
			}

			if (scope.getRow() > rowNum) {
				logger.warn("Scope does not meet the conditions!");
				return new BocloudResult<>(false, "当前行数小于机柜所在的最大行数，行数建议值为" + (scope.getRow() + 1) + "行");
			}

			if (scope.getCol() > colNum) {
				logger.warn("Scope does not meet the conditions!");
				return new BocloudResult<>(false, "当前列数小于机柜所在的最大列数，列数建议值为" + (scope.getCol() + 1) + "列");
			}

			return new BocloudResult<>(true, "范围可以变动");
		} catch (Exception e) {
			logger.error("Check room range fail:", e);
			return new BocloudResult<>(false, "范围不可变动");
		}
	}

	@Override
	public BocloudResult<String> checkPoint(Long id, Integer axisX, Integer axisY) {
		try {
			Room room = this.roomRepository.query(id);
			if (null == room) {
				logger.warn("Room does not exist!");
				return new BocloudResult<>(true, "该机房不存在");
			}

			if (axisX > room.getRowNum()) {
				logger.warn("Rows beyond the scope!");
				return new BocloudResult<>(false,
						"位置超出机房的行数范围，本机房具有" + room.getRowNum() + "行，" + room.getColNum() + "列");
			}

			if (axisY > room.getColNum()) {
				logger.warn("Cols beyond the scope!");
				return new BocloudResult<>(false,
						"位置超出机房的列数范围，本机房具有" + room.getRowNum() + "行，" + room.getColNum() + "列");
			}

			Map<String, Object> map = MapTools.simpleMap(Common.ROOM, id);
			map.put(Common.X, axisX);
			map.put(Common.Y, axisY);
			boolean result = this.rackRepository.exists(ParamHelper.getParams(map, Sign.EQ));
			if (result) {
				logger.warn("Point has rack!");
				return new BocloudResult<>(false, "当前位置存在机柜！");
			}

			return new BocloudResult<>(true, "可添加机柜");
		} catch (Exception e) {
			logger.error("Check room point fail:", e);
			return new BocloudResult<>(false, "不可添加机柜", "", "");
		}
	}

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

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

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

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