package com.persagy.proxy.object.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.dto.RequestData;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.define.client.DigitalDefineFacade;
import com.persagy.dmp.define.client.DigitalObjectInfoFacade;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.define.entity.ObjectTypeDefine;
import com.persagy.dmp.digital.client.DigitalObjectFacade;
import com.persagy.dmp.digital.client.DigitalRelationFacade;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.dmp.simple.client.DigitalObjectSimpleFacade;
import com.persagy.proxy.adm.constant.AdmCommonConstant;
import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
import com.persagy.proxy.adm.request.*;
import com.persagy.proxy.adm.service.IAdmRelationService;
import com.persagy.proxy.adm.utils.AdmContextUtil;
import com.persagy.proxy.adm.utils.AdmQueryCriteriaHelper;
import com.persagy.proxy.adm.utils.ObjectNameUtil;
import com.persagy.proxy.common.entity.RelationDTO;
import com.persagy.proxy.object.model.*;
import com.persagy.proxy.object.service.IAdmEquipmentService;
import com.persagy.proxy.object.service.IAdmProjectService;
import com.persagy.proxy.object.service.IAdmSystemService;
import com.persagy.proxy.report.model.SystemPointExcel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import javax.ws.rs.QueryParam;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Adm 系统 API
 * @author lvxy
 * @date 2021/8/16
 */
@Slf4j
@RestController
@RequestMapping("/object/general-system")
public class AdmSystemController {
    @Autowired
    private IAdmSystemService service;
    @Autowired
    private IAdmRelationService relationService;
    @Autowired
    private IAdmProjectService projectService;
    @Autowired
    private IAdmEquipmentService equipmentService;

    /**
     * 统计

     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/count")
    public AdmResponse count(@RequestBody AdmQueryCriteria request) throws Exception {
        request.setName(AdmSystem.OBJ_TYPE);
        request.setOnlyCount(true);
        return service.query(request);
    }

    /**
     * 创建
     * @param createRequest
     * @return
     * @throws Exception
     */
    @PostMapping("/create")
    public AdmCreateResponse create(@RequestBody AdmCreateRequest<AdmSystem> createRequest, @QueryParam("projectId") String projectId) throws Exception {
        List<AdmSystem> vos = CollUtil.newArrayList(createRequest.getContent());
        vos = service.doInsert(AdmContextUtil.toDmpContext(), AdmSystem.class, vos);
        return AdmCreateResponse.success(vos);
    }

    /**
     * 创建系统信息-处理建筑楼层
     * @param createRequest
     * @return
     * @throws Exception
     */
    @PostMapping("/create-bd-fl")
    public AdmCreateResponse createBdFl(@RequestBody AdmCreateRequest<AdmSystem> createRequest) throws Exception {
        List<AdmSystem> vos = CollUtil.newArrayList(createRequest.getContent());

        Map<String, AdmSystem> admSystemMap = CollUtil.fieldValueMap(vos, "classCode");
        QueryCriteria defineCriteria = new QueryCriteria();
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        ArrayNode array = criteria.putObject("code").putArray("$in");
        admSystemMap.keySet().forEach(classCode -> array.add(classCode));
        defineCriteria.setCriteria(criteria);
        List<ObjectTypeDefine> defineList = DigitalDefineFacade.queryObjectType(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(),AdmContextUtil.toDmpContext().getAppId(),null,defineCriteria);
        Map<String, ObjectTypeDefine> defineMap = CollUtil.fieldValueMap(defineList, "code");
        vos.forEach(admSystem -> {
            if (StrUtil.isBlank(admSystem.getId())){
                admSystem.setId(AdmContextUtil.generateIdStr("Sy",true));
            }
            if(StrUtil.isEmpty(admSystem.getName())){
                if(CollUtil.isNotEmpty(defineList) && defineMap.containsKey(admSystem.getClassCode())){
                    admSystem.setName(ObjectNameUtil.objectName(defineMap.get(admSystem.getClassCode()).getName()+"系统-"));
                }else {
                    admSystem.setName(ObjectNameUtil.objectName("系统-"));
                }
            }
        });
        List<AdmSystem> admSystemList = new ArrayList<>(vos.size());
        List<RelationDTO> relationDTOS = new ArrayList<>(vos.size());
        vos.forEach(admSystem -> {
            if(CollUtil.isNotEmpty(admSystem.getBuildingFloorInfoList())){
                //添加关系
                String systemId = admSystem.getId();
                admSystem.getBuildingFloorInfoList().forEach(info -> {
                    String floorId = info.get("FloorID") == null ? null : info.get("FloorID").textValue();
                    String buildID = info.get("BuildID") == null ? null : info.get("BuildID").textValue();
                    if(StrUtil.isNotEmpty(floorId)){
                        relationDTOS.add(new RelationDTO(StrUtil
                                .format(CommonConstant.DEFAULT_GRAPH_ID_FORMAT, AdmRelationTypeEnum.SY2FL.getGraphCode()),
                                AdmRelationTypeEnum.SY2FL.getGraphCode(), AdmRelationTypeEnum.SY2FL.getRelCode(),
                                null, systemId , floorId));
                    }
                    if(StrUtil.isNotEmpty(buildID)){
                        relationDTOS.add(new RelationDTO(StrUtil
                                .format(CommonConstant.DEFAULT_GRAPH_ID_FORMAT, AdmRelationTypeEnum.SY2BD_MECHINARCH.getGraphCode()),
                                AdmRelationTypeEnum.SY2BD_MECHINARCH.getGraphCode(), AdmRelationTypeEnum.SY2BD_MECHINARCH.getRelCode(),
                                null, systemId , buildID));
                    }
                });
            }
        });
        if(CollUtil.isNotEmpty(relationDTOS)){
            relationService.doSave(AdmContextUtil.toDmpContext(), relationDTOS);
        }
        if(CollUtil.isNotEmpty(vos)){
            List<AdmSystem> systemList = service.doInsert(AdmContextUtil.toDmpContext(), AdmSystem.class, vos);
            admSystemList.addAll(systemList);
        }
        return AdmCreateResponse.success(admSystemList);
    }

    /**
     * 根据ID删除
     * @param vos
     * @return
     * @throws Exception
     */
    @PostMapping("/delete")
    public AdmResponse delete( @RequestBody List<AdmSystem> vos) throws Exception {
        service.doDelete(AdmContextUtil.toDmpContext(), vos);
        return AdmResponse.success();
    }

    /**
     * 更新
     * @param createRequest
     * @return
     * @throws Exception
     */
    @PostMapping("/update")
    public AdmResponse update(@RequestBody AdmCreateRequest<AdmSystem> createRequest, @QueryParam("projectId") String projectId) throws Exception {
        List<AdmSystem> vos = CollUtil.newArrayList(createRequest.getContent());
        vos = service.doUpdate(AdmContextUtil.toDmpContext(), AdmSystem.class, vos);
        return AdmResponse.success(vos);
    }

    /**
     * 创建系统下的设备关系
     * @param createRequest
     * @return
     * @throws Exception
     */
    @PostMapping("/link-equip")
    public AdmCreateResponse createRelateEquip(@RequestBody JSONObject createRequest, @QueryParam("projectId") String projectId) throws Exception {
        String sysId = createRequest.getString("sysId");
        List<String> equipIdList = new ArrayList<>();
        if (StrUtil.isNotBlank(createRequest.getString("equipIdList"))
                && createRequest.getString("equipIdList").startsWith(StrUtil.BRACKET_START)){
            equipIdList = JSONObject.parseArray(createRequest.getString("equipIdList"),String.class);
        }
        List<ObjectNode> relations = new ArrayList<>(equipIdList.size());
        equipIdList.stream().forEach(euipId -> {
            ObjectNode objectNode = JsonNodeFactory.instance.objectNode();
            objectNode.put("relCode", "Sy2Eq");
            objectNode.put("graphCode","MechSubset");
            objectNode.put("objFrom", sysId);
            objectNode.put("objTo", euipId);
        });
        List<ObjectRelation> list = DigitalRelationFacade.create(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(), AdmContextUtil.toDmpContext().getAppId(), null, relations);
        return AdmCreateResponse.success(list);
    }

    /**
     * 查询
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/query")
    public AdmResponse query(@RequestBody AdmQueryCriteria request) {
        request.setName(AdmSystem.OBJ_TYPE);
        if(CollUtil.isEmpty(request.getCascade()) && CollUtil.isNotEmpty(request.getProjection()) && request.getDistinct() && !request.getProjection().contains(BaseEntity.PROP_ID)){
            RequestData requestData = new RequestData();
            requestData.setProjection(request.getProjection());
            requestData.setObjType(AdmSystem.OBJ_TYPE);
            requestData.setPage(request.getPageNumber().longValue());
            requestData.setSize(request.getPageSize().longValue());
            CommonResult<List<ObjectNode>> result = DigitalObjectSimpleFacade.queryObjListByConditions(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(), AdmContextUtil.toDmpContext().getAppId(), null, requestData);
            if(result == null || CollUtil.isEmpty(result.getData())){
                return AdmResponse.success();
            }
            List<AdmSystem> admSystemList = JSONUtil.toList(result.getData().toString(), AdmSystem.class);
            service.ensureSpecialValues(AdmContextUtil.toDmpContext(), admSystemList);
            AdmResponse response = AdmResponse.success(admSystemList,request);
            return response;
        }else {
            return service.query(request);
        }
    }


    /**
     * 查询系统信息-系统建筑楼层一对多
     * MajorName 专业名称 ,级联查询 系统分类名（categoryNames），系统下的设备（equipmentList），系统下的竖井（shaftList），业务空间（zoneSpaceBaseTableList）
     *
     * 明确建筑楼层的 adm级联 查询获取建筑楼层信息  使用正常接口 先查询楼层 再根据楼层信息拼接建筑
     * 未明确建筑/未明确楼层 中台定制接口
     *
     * 这个参见中心代码，
     * @param buildingId buildingId=1,未明确建筑
     * @param floorId   floorId=1,未明确楼层
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/query-new")
    public AdmResponse queryNew(@QueryParam("buildingId") String buildingId,
                                @QueryParam("floorId") String floorId,
                                @RequestBody AdmQueryCriteria request) throws Exception {
        return service.queryNewInternal(buildingId,floorId,request);
    }

    /**
     * 查询系统信息-试图级联建筑楼层 暂未使用

     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/query-bdfl")
    public AdmResponse queryUnBdFl(@RequestBody AdmQueryCriteria request) throws Exception {
        request.setName(AdmSystem.OBJ_TYPE);
        return service.query(request);
    }

    /**
     * 查询没有和当前系统关联的设备
     * id 为系统id

     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/un-sys-equip")
    public AdmResponse queryUnEquip( @NotNull @QueryParam("sysId") String sysId,
                                     @RequestBody AdmQueryCriteria request) {
        request.setName(AdmEquipment.OBJ_TYPE);
        request.resetRelationCond(false, "MechSubset", "Sy2Eq", sysId, false);
        return equipmentService.doQuery(AdmContextUtil.toDmpContext(), request, AdmEquipment.class);
    }


    /**
     * 查询没有和当前系统关联的竖井
     *  id 为系统id

     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/un-sys-shaft")
    public AdmResponse queryUnSh( @NotNull @QueryParam("sysId") String sysId,
                                  @RequestBody AdmQueryCriteria request) throws Exception {
        //AdmResponse admResponse = queryObjectByType(sysId,request,"Sy2Sh",null, AdmShaft.OBJ_TYPE);
        ObjectNode relationTo = JsonNodeFactory.instance.objectNode();
        relationTo.put("objFrom", sysId);
        relationTo.put("relCode", "Sy2Sh");
        relationTo.put("$bindFlag", false);
        request.setRelationTo(relationTo);
        request.setName(AdmShaft.OBJ_TYPE);
        QueryCriteria queryCriteriaObj = AdmQueryCriteriaHelper.toDmpCriteria(request);
        List<ObjectNode> obj = DigitalObjectFacade.query(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(), AdmContextUtil.toDmpContext().getAppId(), null, queryCriteriaObj);
        return AdmResponse.success(obj,request);
    }


    /**
     * 查询没有和当前系统关联的业务空间
     * id 为系统id
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/un-sys-zoneSpaceBaseTable")
    public AdmResponse queryUnZoneSp(@NotNull @QueryParam("sysId") String sysId,
                                     @RequestBody AdmQueryCriteria request) throws Exception {
       // AdmResponse admResponse = queryObjectByType(sysId,request,"Sy2Sp",null, AdmSpace.OBJ_TYPE);
        ObjectNode relationTo = JsonNodeFactory.instance.objectNode();
        relationTo.put("objFrom", sysId);
        relationTo.put("relCode", "Sy2Sp");
        relationTo.put("$bindFlag", false);
        request.setRelationTo(relationTo);
        request.setName(AdmSpace.OBJ_TYPE);
        QueryCriteria queryCriteriaObj = AdmQueryCriteriaHelper.toDmpCriteria(request);
        List<ObjectNode> obj = DigitalObjectFacade.query(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(), AdmContextUtil.toDmpContext().getAppId(), null, queryCriteriaObj);
        return AdmResponse.success(obj,request);
    }


    /**
     * 查询没有和当前空间绑定的系统
     * 例如：prijectId=Pj4201050001,type=AirConditioningZone spaceId=Sp4201050001df2091cdf719443b80bb737aeabf0525 spaceId 为当前空间id type为当前空间类型
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/unspace-sys")
    public AdmResponse queryUnSpSy(@QueryParam("type") String type, @QueryParam("spaceId") String spaceId,
                                     @RequestBody AdmQueryCriteria request) throws Exception {
        if(StrUtil.isNotEmpty(type)){
            if(StrUtil.isBlank(request.getFilters())){
                request.setFilters("classCode = '"+type+"'");
            }else{
                request.setFilters(request.getFilters()+";classCode = '"+type+"'");
            }
        }
        //AdmResponse admResponse = queryObjectByType(spaceId, request,"Sy2Sp",null, AdmSystem.OBJ_TYPE);

        ObjectNode relationFrom = JsonNodeFactory.instance.objectNode();
        relationFrom.put("objTo", spaceId);
        relationFrom.put("relCode", "Sy2Sp");
        relationFrom.put("$bindFlag", false);
        request.setRelationFrom(relationFrom);
        request.setName(AdmSystem.OBJ_TYPE);
        QueryCriteria queryCriteriaObj = AdmQueryCriteriaHelper.toDmpCriteria(request);
        List<ObjectNode> obj = DigitalObjectFacade.query(AdmContextUtil.toDmpContext().getGroupCode(),AdmContextUtil.toDmpContext().getProjectId(), AdmContextUtil.toDmpContext().getAppId(), null, queryCriteriaObj);
        return AdmResponse.success(obj,request);
    }

    /**
     * 导出系统的静态或动态数据
     * @param type: static(静态)，iot (动态)
     */
    @GetMapping("/export")
    public void exportInfos(@RequestParam("type") String type, @RequestParam("projectId") String projectId, HttpServletResponse response) throws Exception {
        try {
            response.setContentType("application/vnd.ms-excel");
            String encode = StandardCharsets.UTF_8.name();
            response.setCharacterEncoding(encode);
            AdmQueryCriteria projectRequest = new AdmQueryCriteria();
            projectRequest.setName(AdmProject.OBJ_TYPE);
            projectRequest.setFilters("id = '" + projectId + "';");
            AdmResponse responsePro = projectService.doQuery(AdmContextUtil.toDmpContext(), projectRequest, AdmProject.class);

            if (CollUtil.isEmpty(responsePro.getContent())) {
                throw new BusinessException(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
            } else {
                AdmProject admProject = (AdmProject) responsePro.getContent().get(0);
                // 防止中文乱码
                String fileName = admProject.getLocalName() + "设备已交付信息点";
                fileName = URLEncoder.encode(fileName, encode);

                response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
                List<SystemPointExcel> systemPointExcels = dealSystemPoint(admProject.getGroupCode(),admProject.getId(),type, false, null);
                EasyExcel.write(response.getOutputStream(), SystemPointExcel.class).sheet("sheet1").doWrite(systemPointExcels);
            }
        }catch (Exception e){
            log.error("设备数据导出异常,获取项目失败",e);
        }
    }

    /**
     * 组装需要导出的设备信息点
     * @param groupCode
     * @param projectId
     * @return
     */
    private List<SystemPointExcel> dealSystemPoint(String groupCode, String projectId, String type, Boolean withBuilding, String buildingId){
        if(StrUtil.isAllNotEmpty(groupCode,projectId)){
            // 结果封装
            List<SystemPointExcel> excelList = new ArrayList<>();
            /** 统计项目设备类型 */
            RequestData requestData = new RequestData();
            requestData.setProjectId(projectId);
            requestData.setGroupCode(groupCode);
            requestData.setObjType(AdmSystem.OBJ_TYPE);
            requestData.setObjTypes(CollUtil.newHashSet(AdmSystem.OBJ_TYPE));
            List<Map<String, Object>> sysTypeList = DigitalObjectSimpleFacade.queryDefineInfoByObjType(groupCode,projectId,null,null,requestData);
            /** 统计信息不为空继续 */
            if (sysTypeList.size() > 0 ) {
                for(int i = 0; i < sysTypeList.size(); i++){
                    AdmDictQueryCriteria admQueryCriteriaInfo = new AdmDictQueryCriteria();
                    /** 是否查询静态信息点 */
                    if (type.equals("static")){
                        admQueryCriteriaInfo.addFilters("category = 'STATIC'");
                    } else {
                        admQueryCriteriaInfo.addFilters("category != 'STATIC'");
                    }
                    admQueryCriteriaInfo.addFilters("valid = 1 ");
                    admQueryCriteriaInfo.addFilters("classCode = '" + sysTypeList.get(i).get("code").toString() + "'");

                    AdmQueryCriteria admQueryCriteriaSys = new AdmQueryCriteria();
                    admQueryCriteriaSys.addFilters("classCode = '" + sysTypeList.get(i).get("code").toString() + "'");
                    admQueryCriteriaSys.addFilters("not infos isnull");
                    admQueryCriteriaSys.setName(AdmSystem.OBJ_TYPE);
                    if(withBuilding){
                        if(StrUtil.isNotEmpty(buildingId)){
                            admQueryCriteriaSys.addFilters("buildingId = '"+buildingId+"'");
                        }else{
                            admQueryCriteriaSys.addFilters("buildingId isnull");
                        }
                    }
                    AdmResponse eqResponse = service.query(admQueryCriteriaSys);
                    List<AdmSystem> equipmentList = (List<AdmSystem>) eqResponse.getContent();
                    if (equipmentList.size() > 0) {
                        /** 字典信息点 */
                        //AdmResponse responseDic = dictService.query(context, admQueryCriteriaInfo, AdmDictConstant.CLASS_CODE, AdmObjectInfo.class);
                        //List<AdmObjectInfo> defFuncIdList = (List<AdmObjectInfo>) responseDic.getContent();
                        QueryCriteria queryCriteria = AdmQueryCriteriaHelper.toDmpCriteria(admQueryCriteriaInfo);
                        queryCriteria.setOnlyCount(false);
                        List<ObjectInfoDefine> defFuncIdList = DigitalObjectInfoFacade.query(AdmContextUtil.toDmpContext().getGroupCode(), AdmContextUtil.toDmpContext().getProjectId(), AdmCommonConstant.APP_ID,null,queryCriteria);

                        if (defFuncIdList.size() > 0) {
                            Map mapInfo = new HashMap<String,String>();
                            Map mapInputType = new HashMap<String,String>();
                            Map mapInputFirstName = new HashMap<String,String>();
                            for (ObjectInfoDefine defFuncId : defFuncIdList) {
                                if(StrUtil.isEmpty(defFuncId.getCode())){
                                    continue;
                                }
                                mapInfo.put(defFuncId.getCode(),defFuncId.getName());
                                if(StrUtil.isNotEmpty(defFuncId.getDataType())){
                                    mapInputType.put(defFuncId.getCode(), defFuncId.getDataType());
                                }
                                if(StrUtil.isNotEmpty(defFuncId.getFirstTag())){
                                    mapInputFirstName.put(defFuncId.getCode(),defFuncId.getFirstTag());
                                }
                            }
                            for (AdmSystem admSystem : equipmentList) {
                                if(CollUtil.isEmpty(admSystem.getInfos())){
                                    continue;
                                }
                                Map<String, Object> info = admSystem.getInfos();
                                for(String key : info.keySet()){
                                    if(info.get(key) != null && StrUtil.isNotEmpty(info.get(key).toString())){
                                        boolean containsKey = mapInfo.containsKey(key);
                                        if(!containsKey){
                                            continue;
                                        }
                                        /** 处理数据写入文件 */
                                        SystemPointExcel excel = new SystemPointExcel();
                                        /** id */
                                        excel.setId(admSystem.getId());
                                        /** 本地编码 */
                                        excel.setLocalId(admSystem.getLocalId());
                                        /** 本地名称 */
                                        excel.setLocalName(admSystem.getLocalName());
                                        /** 类型 */
                                        excel.setClassCode(admSystem.getClassCode());
                                        /** 类型 */
                                        excel.setClassCodeName(sysTypeList.get(i).get("name").toString());
                                        /** 一级标签 */
                                        boolean containsFirstName = mapInputFirstName.containsKey(key);
                                        if (containsFirstName) {
                                            /** 一级标签 */
                                            excel.setMapInputFirstName(mapInputFirstName.get(key).toString());
                                        }
                                        /** 信息点名 */
                                        excel.setInfoName(mapInfo.get(key).toString());
                                        /** 信息点 */
                                        excel.setInfoCode(key);
                                        boolean containsInputType = mapInputType.containsKey(key);
                                        if (containsInputType) {
                                            /** 输入类型 */
                                            excel.setInputType(mapInputType.get(key).toString());
                                        }
                                        /** 值 */
                                        excel.setInputValue(info.get(key).toString());
                                        excelList.add(excel);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return excelList;
        }else{
            return null;
        }
    }
}
