package ink.wgink.module.map.service.grid.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.map.IGridDeleteAfterHandler;
import ink.wgink.interfaces.map.IGridSaveAfterHandler;
import ink.wgink.interfaces.map.IGridUpdateAfterHandler;
import ink.wgink.module.map.dao.grid.IGridDao;
import ink.wgink.module.map.pojo.dtos.grid.GridDTO;
import ink.wgink.module.map.pojo.dtos.grid.GridPointDTO;
import ink.wgink.module.map.pojo.dtos.grid.GridRelationDTO;
import ink.wgink.module.map.pojo.pos.grid.GridPO;
import ink.wgink.module.map.pojo.vos.grid.GridPointVO;
import ink.wgink.module.map.pojo.vos.grid.GridVO;
import ink.wgink.module.map.service.grid.IGridPointService;
import ink.wgink.module.map.service.grid.IGridRelationService;
import ink.wgink.module.map.service.grid.IGridService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.util.ArrayListUtil;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.map.HashMapUtil;
import ink.wgink.util.point.Point;
import ink.wgink.util.point.PointUtil;
import ink.wgink.util.string.WStringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: GridServiceImpl
 * @Description: 网格业务
 * @Author: WangGeng
 * @Date: 2020/10/21 11:14
 * @Version: 1.0
 **/
@Service
public class GridServiceImpl extends DefaultBaseService implements IGridService {

    @Autowired
    private IGridDao gridDao;
    @Autowired
    private IGridRelationService gridRelationService;
    @Autowired
    private IGridPointService gridPointService;
    @Autowired(required = false)
    private IGridSaveAfterHandler gridSaveAfterHandler;
    @Autowired(required = false)
    private IGridUpdateAfterHandler gridUpdateAfterHandler;
    @Autowired(required = false)
    private IGridDeleteAfterHandler gridDeleteAfterHandler;

    @Override
    public void save(GridVO gridVO) throws Exception {
        saveAndReturnId(gridVO);
    }

    @Override
    public String saveAndReturnId(GridVO gridVO) throws Exception {
        String gridId = UUIDUtil.getUUID();
        List<String> relationIdArray = gridVO.getGrid().getRelationIdArray();
        List<GridPointVO> pointArray = gridVO.getGrid().getPointArray();
        // 保存网格
        Map<String, Object> params = HashMapUtil.beanToMap(gridVO);
        params.put("gridSquare", getSquare(pointArray));
        params.put("fillColor", gridVO.getGrid().getFillColor());
        String gridCode = getSaveGridCode(gridVO.getAreaCode());
        params.put("gridCode", gridCode);
        params.put("gridId", gridId);
        params.remove("relationIdArray");
        params.remove("pointArray");
        params.remove("grid");
        setSaveInfo(params);
        gridDao.save(params);
        // 保存拓展属性
        gridRelationService.save(gridId, relationIdArray);
        gridPointService.save(gridId, pointArray);
        if (gridSaveAfterHandler != null) {
            gridSaveAfterHandler.handle(gridId, gridCode, gridVO.getGridName(), gridVO.getGrid().getFillColor(), gridVO.getAreaCode(), gridVO.getAreaName());
        }
        return gridId;
    }

    @Override
    public void remove(List<String> ids) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridIds", ids);
        setUpdateInfo(params);
        gridDao.remove(params);
        if (gridDeleteAfterHandler != null) {
            gridDeleteAfterHandler.handler(ids);
        }
    }

    @Override
    public void deleteByRelationId(String relationId) {
        List<String> gridIds = gridRelationService.listGridId(relationId);
        if (gridIds.isEmpty()) {
            return;
        }
        Map<String, Object> params = getHashMap(2);
        params.put("gridIds", gridIds);
        // 删除网格
        gridDao.delete(params);
        if (gridDeleteAfterHandler != null) {
            gridDeleteAfterHandler.handler(gridIds);
        }
        // 删除网格点
        gridPointService.deleteByGridIds(gridIds);
        // 删除关联关系
        gridRelationService.deleteByRelationId(relationId);
    }

    @Override
    public void deleteByGridIds(List<String> gridIds) {
        if (gridIds == null || gridIds.isEmpty()) {
            return;
        }
        // 删除网格
        Map<String, Object> params = getHashMap(2);
        params.put("gridIds", gridIds);
        gridDao.delete(params);
        if (gridDeleteAfterHandler != null) {
            gridDeleteAfterHandler.handler(gridIds);
        }
        // 删除点数组
        gridPointService.deleteByGridIds(gridIds);
        // 删除关联
        gridRelationService.deleteByGridIds(gridIds);
    }

    @Override
    public void deleteEmpty(List<String> gridIds) {
        if (gridIds == null || gridIds.isEmpty()) {
            return;
        }
        // 所有的网格和人员关联关系列表
        List<GridRelationDTO> gridRelationDTOs = gridRelationService.listByGridIds(gridIds);
        // 得到为空的网格
        List<String> emptyGridIds = new ArrayList<>();
        for (String gridId : gridIds) {
            boolean isEmpty = true;
            for (GridRelationDTO gridRelationDTO : gridRelationDTOs) {
                if (StringUtils.equals(gridId, gridRelationDTO.getGridId())) {
                    isEmpty = false;
                    break;
                }
            }
            if (isEmpty) {
                emptyGridIds.add(gridId);
            }
        }
        // 删除空网格
        deleteByGridIds(emptyGridIds);
    }

    @Override
    public void update(String gridId, GridVO gridVO) throws Exception {
        GridDTO gridDTO = get(gridId);
        if (gridDTO == null) {
            throw new SearchException("网格不存在");
        }
        GridVO.Grid grid = gridVO.getGrid();
        Map<String, Object> params = HashMapUtil.beanToMap(gridVO);
        params.put("fillColor", gridVO.getGrid().getFillColor());
        params.put("gridSquare", getSquare(grid.getPointArray()));
        if (!StringUtils.equals(gridDTO.getAreaCode(), gridVO.getAreaCode())) {
            String gridCode = getUpdateGridCode(gridVO.getAreaCode(), gridDTO.getGridCode());
            params.put("gridCode", gridCode);
        }
        params.put("gridId", gridId);
        setUpdateInfo(params);
        gridDao.update(params);
        if (gridUpdateAfterHandler != null) {
            gridUpdateAfterHandler.handle(gridId, gridVO.getGridName(), grid.getFillColor());
        }
        // 删除原有节点
        gridPointService.deleteByGridId(gridId);
        // 新增原有节点
        gridPointService.save(gridId, grid.getPointArray());
    }

    @Override
    public GridDTO get(Map<String, Object> params) {
        GridDTO gridDTO = gridDao.get(params);
        return gridDTO;
    }

    @Override
    public GridDTO get(Map<String, Object> params, boolean withRelations, boolean withPoints) {
        GridDTO gridDTO = get(params);
        if (withPoints) {
            setGridPoints(gridDTO);
        }
        if (withRelations) {
            setGridRelation(gridDTO);
        }
        return gridDTO;
    }

    @Override
    public GridDTO get(String gridId) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridId", gridId);
        return get(params);
    }

    @Override
    public GridDTO get(String gridId, boolean withRelations, boolean withPoints) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridId", gridId);
        return get(params, withRelations, withPoints);
    }

    @Override
    public GridPO gridPO(Map<String, Object> params) {
        return gridDao.getPO(params);
    }

    @Override
    public GridPO gridPO(String gridId) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridId", gridId);
        return gridPO(params);
    }

    @Override
    public List<GridDTO> list(Map<String, Object> params) {
        return ArrayListUtil.deepClone(gridDao.list(params), GridDTO.class);
    }

    @Override
    public List<GridDTO> list(Map<String, Object> params, boolean withRelations, boolean withPoints) {
        List<GridDTO> gridDTOs = list(params);
        if (withPoints) {
            setGridPoints(gridDTOs);
        }
        if (withRelations) {
            setGridRelation(gridDTOs);
        }
        return gridDTOs;
    }

    @Override
    public List<GridDTO> list(List<String> gridIds) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridIds", gridIds);
        return list(params);
    }

    @Override
    public List<GridDTO> list(List<String> gridIds, boolean withRelations, boolean withPoints) {
        Map<String, Object> params = getHashMap(2);
        params.put("gridIds", gridIds);
        return list(params, true, true);
    }

    @Override
    public List<GridDTO> listByRelationId(String relationId) throws SearchException {
        if (StringUtils.isBlank(relationId)) {
            return new ArrayList<>();
        }
        List<String> gridIds = gridRelationService.listGridId(relationId);
        if (gridIds.isEmpty()) {
            return new ArrayList<>();
        }
        return list(gridIds);
    }

    @Override
    public List<GridDTO> listGroupByRelationIds(List<String> relationIds) throws SearchException {
        if (relationIds == null || relationIds.size() == 0) {
            return new ArrayList<>();
        }
        List<String> gridIds = gridRelationService.listGridId(relationIds);
        if (gridIds.isEmpty()) {
            return new ArrayList<>();
        }
        List<GridDTO> gridDTOs = list(gridIds);
        if (gridDTOs.isEmpty()) {
            return gridDTOs;
        }
        // 聚合后的网格
        List<GridDTO> gridGroupDTOs = listGridGroup(gridDTOs);
        // 只能展示包含关联人员列表的网格，比如：查询AB，那么可以展示的应该是AB...而不能是A
        for (int i = 0; i < gridGroupDTOs.size(); i++) {
            GridDTO gridDTO = gridGroupDTOs.get(i);
            // 关联人数多于片区人数，排除
            if (relationIds.size() > gridDTO.getRelationIdArray().size()) {
                gridGroupDTOs.remove(i);
                i--;
                continue;
            }
            // 判断关联人是否全部被片区包含
            boolean isAllExist = true;
            for (String relationId : relationIds) {
                if (!gridDTO.getRelationId().contains(relationId)) {
                    isAllExist = false;
                    break;
                }
            }
            // 不是全部包含，就删除
            if (!isAllExist) {
                gridGroupDTOs.remove(i);
                i--;
                continue;
            }
        }
        return gridGroupDTOs;
    }

    @Override
    public List<GridDTO> listByRelationIds(List<String> relationIds) throws SearchException {
        if (relationIds == null || relationIds.size() == 0) {
            return new ArrayList<>();
        }
        List<String> gridIds = gridRelationService.listGridId(relationIds);
        if (gridIds.isEmpty()) {
            return new ArrayList<>();
        }
        List<GridDTO> gridDTOs = list(gridIds);
        if (gridDTOs.isEmpty()) {
            return gridDTOs;
        }
        return gridDTOs;
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPageByRelationIds(ListPage page, List<String> relationIds) {
        if (relationIds == null || relationIds.size() == 0) {
            return new SuccessResultList<>(new ArrayList<>(), 1, 0L);
        }
        List<String> gridIds = gridRelationService.listGridId(relationIds);
        if (gridIds.isEmpty()) {
            return new SuccessResultList<>(new ArrayList<>(), 1, 0L);
        }
        PageHelper.startPage(page.getPage(), page.getRows());
        List<GridDTO> gridDTOs = list(relationIds);
        PageInfo<GridDTO> pageInfo = new PageInfo<>(gridDTOs);
        return new SuccessResultList<>(gridDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPage(ListPage page) {
        PageHelper.startPage(page.getPage(), page.getRows());
        List<GridDTO> gridDTOs = list(page.getParams());
        PageInfo<GridDTO> pageInfo = new PageInfo<>(gridDTOs);
        return new SuccessResultList<>(gridDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPage(ListPage page, boolean withRelations, boolean withPoints) {
        PageHelper.startPage(page.getPage(), page.getRows());
        List<GridDTO> gridDTOs = list(page.getParams(), withRelations, withPoints);
        PageInfo<GridDTO> pageInfo = new PageInfo<>(gridDTOs);
        return new SuccessResultList<>(gridDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    @Override
    public List<GridDTO> listExcept(String gridId, Map<String, Object> params) {
        List<GridDTO> gridDTOs = list(params, true, true);
        for (GridDTO gridDTO : gridDTOs) {
            if (StringUtils.equals(gridId, gridDTO.getGridId())) {
                gridDTOs.remove(gridDTO);
                break;
            }
        }
        return gridDTOs;
    }

    @Override
    public List<GridDTO> listByAreaCode(String areaCode, Map<String, Object> params) {
        params.put("areaCode", areaCode);
        return list(params);
    }

    @Override
    public List<GridDTO> listAllByAreaCode(String areaCode, Map<String, Object> params) {
        params.put("areaCodeLike", areaCode);
        return list(params);
    }

    @Override
    public List<GridDTO> listSubByAreaCode(String areaCode, Map<String, Object> params) {
        params.put("areaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(areaCode, '0', 3));
        params.put("excludeAreaCode", areaCode);
        return list(params);
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPageByAreaCode(String areaCode, ListPage page) {
        page.getParams().put("areaCode", areaCode);
        return listPage(page);
    }

    @Override
    public List<GridDTO> listWithPointByRelationId(String relationId) {
        List<GridDTO> gridDTOs = listByRelationId(relationId);
        gridDTOs.forEach(gridDTO -> {
            gridDTO.setPointArray(gridPointService.list(gridDTO.getGridId()));
        });
        return gridDTOs;
    }

    @Override
    public List<GridDTO> listAllWithPointByAreaCode(String areaCode, Map<String, Object> params) {
        params.put("areaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(areaCode, '0', 3));
        return list(params, false, true);
    }

    @Override
    public List<GridDTO> listOfMine(String token, Map<String, Object> params) {
        String userId = getAppTokenUser(token).getId();
        params.put("relationId", userId);
        return list(params);
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPageOfMine(String token, ListPage page) {
        String userId = getAppTokenUser(token).getId();
        page.getParams().put("relationId", userId);
        return listPage(page);
    }

    @Override
    public List<GridDTO> listNotMine(String token, Map<String, Object> params) {
        String userId = getAppTokenUser(token).getId();
        params.put("excludeRelationId", userId);
        return list(params);
    }

    @Override
    public SuccessResultList<List<GridDTO>> listPageNotMine(String token, ListPage page) {
        String userId = getAppTokenUser(token).getId();
        page.getParams().put("excludeRelationId", userId);
        return listPage(page);
    }

    /**
     * 设置网格点
     *
     * @param gridDTO
     */
    private void setGridPoints(GridDTO gridDTO) {
        if (gridDTO == null) {
            return;
        }
        List<GridPointDTO> gridPointDTOs = gridPointService.list(gridDTO.getGridId());
        List<GridPointDTO> pointArray = new ArrayList<>();
        for (GridPointDTO gridPointDTO : gridPointDTOs) {
            pointArray.add(gridPointDTO);
        }
        gridDTO.setPointArray(pointArray);
    }

    /**
     * 设置网格点
     *
     * @param gridDTOs
     */
    private void setGridPoints(List<GridDTO> gridDTOs) {
        if (gridDTOs.isEmpty()) {
            return;
        }
        List<String> gridIds = new ArrayList<>();
        for (GridDTO gridDTO : gridDTOs) {
            gridIds.add(gridDTO.getGridId());
        }
        // 获取所有点
        List<GridPointDTO> gridPointDTOs = gridPointService.list(gridIds);
        if (gridPointDTOs.isEmpty()) {
            return;
        }
        // 构建所有点
        for (GridDTO gridDTO : gridDTOs) {
            List<GridPointDTO> pointArray = new ArrayList<>();
            for (GridPointDTO gridPointDTO : gridPointDTOs) {
                if (StringUtils.equals(gridDTO.getGridId(), gridPointDTO.getGridId())) {
                    pointArray.add(gridPointDTO);
                }
            }
            gridDTO.setPointArray(pointArray);
        }
    }

    /**
     * 设置关联
     *
     * @param gridDTO
     */
    private void setGridRelation(GridDTO gridDTO) {
        if (gridDTO == null) {
            return;
        }
        List<GridRelationDTO> gridRelationDTOs = gridRelationService.listByGridId(gridDTO.getGridId());
        List<String> gridRelationIds = gridDTO.getRelationIdArray();
        for (GridRelationDTO gridRelationDTO : gridRelationDTOs) {
            gridRelationIds.add(gridRelationDTO.getRelationId());
        }
        gridDTO.setRelationIdArray(gridRelationIds);
    }

    /**
     * 设置关联
     *
     * @param gridDTOs 网格点
     */
    private void setGridRelation(List<GridDTO> gridDTOs) {
        if (gridDTOs.isEmpty()) {
            return;
        }
        List<String> gridIds = new ArrayList<>();
        for (GridDTO gridDTO : gridDTOs) {
            gridIds.add(gridDTO.getGridId());
        }
        List<GridRelationDTO> gridRelationDTOs = gridRelationService.listByGridIds(gridIds);
        if (gridRelationDTOs.isEmpty()) {
            return;
        }
        for (GridDTO gridDTO : gridDTOs) {
            for (GridRelationDTO gridRelationDTO : gridRelationDTOs) {
                if (StringUtils.equals(gridDTO.getGridId(), gridRelationDTO.getGridId())) {
                    List<String> gridRelationIds = gridDTO.getRelationIdArray();
                    if (gridRelationIds == null || gridRelationIds.isEmpty()) {
                        gridRelationIds = new ArrayList<>();
                        gridDTO.setRelationIdArray(gridRelationIds);
                    }
                    gridRelationIds.add(gridRelationDTO.getRelationId());
                    break;
                }
            }
        }
    }

    /**
     * 得到聚合后的列表（共有的
     *
     * @param gridDTOs
     * @return
     */
    private List<GridDTO> listGridGroup(List<GridDTO> gridDTOs) {
        Map<String, GridDTO> gridMap = new HashMap<>(16);
        for (GridDTO gridDTO : gridDTOs) {
            if (gridMap.get(gridDTO.getGridId()) == null) {
                gridMap.put(gridDTO.getGridId(), gridDTO);
            }
        }
        List<GridDTO> gridGroups = new ArrayList<>();
        for (Map.Entry<String, GridDTO> kv : gridMap.entrySet()) {
            GridDTO gridDTO = kv.getValue();
            Collections.sort(gridDTO.getRelationIdArray());
            gridDTO.setRelationId(WStringUtil.listToStr(gridDTO.getRelationIdArray(), ","));
            gridGroups.add(gridDTO);
        }
        return gridGroups;
    }

    /**
     * 得到面积，单位平方米
     *
     * @param gridPointVOs
     * @return
     */
    private Double getSquare(List<GridPointVO> gridPointVOs) {
        List<Point> points = new ArrayList<>();
        for (GridPointVO gridPointVO : gridPointVOs) {
            Point point = new Point(Double.valueOf(gridPointVO.getLng()), Double.valueOf(gridPointVO.getLat()));
            points.add(point);
        }
        return new BigDecimal(PointUtil.getSqua(points)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 区域最后网格
     *
     * @param areaCode
     * @return
     */
    private GridPO getAreaLastPO(String areaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("areaCode", areaCode);
        return gridDao.getAreaLastPO(params);
    }

    /**
     * 获取新增网格编码
     *
     * @param areaCode
     * @return
     */
    private String getSaveGridCode(String areaCode) {
        GridPO areaLastPO = getAreaLastPO(areaCode);
        return areaLastPO == null ? String.format("%s001", areaCode.substring(0, 12)) : String.valueOf(Long.valueOf(areaLastPO.getGridCode()) + 1);
    }

    /**
     * 获取修改网格编码
     *
     * @param areaCode
     * @param gridCode
     * @return
     */
    private String getUpdateGridCode(String areaCode, String gridCode) {
        GridPO areaLastPO = getAreaLastPO(areaCode);
        if (areaLastPO == null) {
            return String.format("%s001", areaCode);
        }
        return areaCode + String.format("%03d", Integer.parseInt(gridCode.substring(12, 15)) + 1);
    }

}
