package com.usky.iot.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.shaded.com.google.gson.JsonArray;
import com.alibaba.nacos.shaded.com.google.gson.JsonObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.mybatis.core.AbstractCrudService;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.demo.RemoteTsdbProxyService;
import com.usky.demo.constant.TdsConstants;
import com.usky.demo.domain.DataTypeEnum;
import com.usky.demo.domain.FieldsVO;
import com.usky.demo.domain.SuperTableDTO;
import com.usky.iot.domain.*;
import com.usky.iot.mapper.DmpProductInfoMapper;
import com.usky.iot.service.*;
import com.usky.iot.service.config.IntegerUtils;
import com.usky.iot.service.vo.DmpProductInfoRequest;
import com.usky.transfer.RemoteTransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 产品信息表 服务实现类
 * </p>
 *
 * @author ya
 * @since 2022-10-08
 */
@Service
public class DmpProductInfoServiceImpl extends AbstractCrudService<DmpProductInfoMapper, DmpProductInfo> implements DmpProductInfoService {


    @Autowired
    private DmpDeviceInfoService dmpDeviceInfoService;

    @Autowired
    private DmpDeviceStatusService dmpDeviceStatusService;

    @Autowired
    private DmpDeviceTypeService dmpDeviceTypeService;

    @Autowired
    private DmpProductAttributeService dmpProductAttributeService;

    @Autowired
    private RemoteTransferService remoteTransferService;

    @Autowired
    private DmpProductCommandService dmpProductCommandService;

    @Autowired
    private RemoteTsdbProxyService remoteTsdbProxyService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(List<DmpProductInfo> dmpProductInfo) {
        if (CollectionUtils.isEmpty(dmpProductInfo)) {
            throw new BusinessException("数据不能为空");
        }

        for (int k = 0; k < dmpProductInfo.size(); k++) {
            if (checkNameUnique(dmpProductInfo.get(k))){
                throw new BusinessException("新增产品信息'" + dmpProductInfo.get(k).getProductCode() + "'失败，产品编码已存在");
            }
            if (checkProductNameUnique(dmpProductInfo.get(k))){
                throw new BusinessException("新增产品信息'" + dmpProductInfo.get(k).getProductName() + "'失败，产品名称已存在");
            }
            int deviceType = dmpProductInfo.get(k).getDeviceType();
            LambdaQueryWrapper<DmpDeviceType> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.select(DmpDeviceType::getDataField,DmpDeviceType::getCommandField)
                    .eq(DmpDeviceType::getTypeCode,deviceType);
            DmpDeviceType one  = dmpDeviceTypeService.getOne(queryWrapper);
            String dd = one.getDataField();
            if (StringUtils.isBlank(dd) || dd.length() == 0){
                throw new BusinessException("新增产品信息'" + dmpProductInfo.get(k).getProductCode() + "'失败，设备类型缺少属性");
            }

            QueryWrapper<DmpProductInfo> query = Wrappers.query();
            query.select("max(id) as id");
            DmpProductInfo info1 = this.getOne(query);
            int id = info1.getId()+1;
            String productCode = String.format("%03d",deviceType)+"_"+id;
            dmpProductInfo.get(k).setProductCode(productCode);

            dmpProductInfo.get(k).setCreatedBy(SecurityUtils.getUsername());
            dmpProductInfo.get(k).setCreatedTime(new Date());
            dmpProductInfo.get(k).setDeleteFlag(0);
            dmpProductInfo.get(k).setTenantId(SecurityUtils.getTenantId());
            this.save(dmpProductInfo.get(k));

            remoteTransferService.deleteProductCache();

            int productId = dmpProductInfo.get(k).getId();
            if(null != one){
                if(Objects.nonNull(one.getDataField())){
                    String dataField = one.getDataField();
                    JSONArray array = new JSONArray();
                    array =  JSONArray.parseArray(dataField);

                    SuperTableDTO superTableDTO = new SuperTableDTO();
                    superTableDTO.setSuperTableName("super_"+productCode);
                    List<FieldsVO> schemaFields = new ArrayList<>(Arrays.asList(
                            new FieldsVO(TdsConstants.TS, "TIMESTAMP", null)
                    ));

                    if(array.size()>0){
                        for(int i=0;i<array.size();i++){
                            JSONObject obj = JSONObject.parseObject(array.getString(i));
                            String attribute_name = obj.getString("attribute_name");
                            String attribute_code = obj.getString("attribute_code");
                            int attribute_port = obj.getInteger("attribute_port");
                            int data_type = obj.getInteger("data_type");
                            int attribute_length = obj.getInteger("attribute_length");
                            String attribute_unit = obj.getString("attribute_unit");

                            DmpProductAttribute dmpProductAttribute = new DmpProductAttribute();
                            dmpProductAttribute.setProductId(productId);
                            dmpProductAttribute.setAttributeName(attribute_name);
                            dmpProductAttribute.setAttributeCode(attribute_code);
                            dmpProductAttribute.setAttributePort(attribute_port);
                            dmpProductAttribute.setAttributeType(1);
                            dmpProductAttribute.setDataType(data_type);
                            String dataType = "";
                            if(data_type == 1){
                                dataType = "INT";
                            }else if(data_type == 2){
                                dataType = "VARCHAR";
                            }else if(data_type == 3){
                                dataType = "BOOL";
                            }else if(data_type == 4) {
                                dataType = "DOUBLE";
                            }

                            dmpProductAttribute.setAttributeLength(attribute_length);
                            dmpProductAttribute.setAttributeUnit(attribute_unit);
                            if(Objects.nonNull(obj.getInteger("maximum"))){
                                dmpProductAttribute.setMaximum(BigDecimal.valueOf(obj.getInteger("maximum")));
                            }
                            if(Objects.nonNull(obj.getInteger("minimum"))){
                                dmpProductAttribute.setMinimum(BigDecimal.valueOf(obj.getInteger("minimum")));
                            }
                            if(!(obj.getString("attribute_dict")).equals("")){
                                dmpProductAttribute.setAttributeDict(obj.getString("attribute_dict"));
                            }
                            dmpProductAttribute.setDeleteFlag(0);
                            dmpProductAttribute.setCreatedBy(SecurityUtils.getUsername());
                            dmpProductAttribute.setCreatedTime(new Date());
                            dmpProductAttribute.setTenantId(SecurityUtils.getTenantId());
                            dmpProductAttributeService.save(dmpProductAttribute);

                            schemaFields.add(new FieldsVO(attribute_code, dataType, attribute_length));
                        }
                    }

                    List<FieldsVO> tagsFields = Collections.singletonList(new FieldsVO(TdsConstants.DEVICE_IDENTIFICATION, "VARCHAR", 50));

                    superTableDTO.setSchemaFields(FieldsVO.toFieldsList(schemaFields));
                    superTableDTO.setTagsFields(FieldsVO.toFieldsList(tagsFields));
                    //创建超级表及列
                    remoteTsdbProxyService.createSuperTableAndColumn(superTableDTO);
                }

                if(Objects.nonNull(one.getCommandField())){
                    String commandField = one.getCommandField();
                    JSONArray array = JSONArray.parseArray(commandField);
                    if(array.size()>0){
                        for(int i=0;i<array.size();i++){
                            JSONObject obj = JSONObject.parseObject(array.getString(i));
                            String command_name = obj.getString("command_name");
                            String command_code = obj.getString("command_code");
                            int data_type = obj.getInteger("data_type");

                            DmpProductCommand dmpProductCommand = new DmpProductCommand();
                            dmpProductCommand.setProductCode(productCode);
                            dmpProductCommand.setCommandName(command_name);
                            dmpProductCommand.setCommandCode(command_code);
                            dmpProductCommand.setDataType(data_type);
                            if(!(obj.getString("command_dict")).equals("")){
                                dmpProductCommand.setCommandDict(obj.getString("command_dict"));
                            }
                            if(!(obj.getString("command_unit")).equals("")){
                                dmpProductCommand.setCommandUnit(obj.getString("command_unit"));
                            }
                            if(!(obj.getString("maximum")).equals("")){
                                dmpProductCommand.setMaximum(BigDecimal.valueOf(Long.parseLong(obj.getString("maximum"))));
                            }
                            if(!(obj.getString("minimum")).equals("")){
                                dmpProductCommand.setMinimum(BigDecimal.valueOf(Long.parseLong(obj.getString("minimum"))));
                            }
                            dmpProductCommand.setDeleteFlag(0);
                            dmpProductCommand.setCreatedBy(SecurityUtils.getUsername());
                            dmpProductCommand.setCreatedTime(LocalDateTime.now());
                            dmpProductCommand.setTenantId(SecurityUtils.getTenantId());
                            dmpProductCommandService.save(dmpProductCommand);
                        }
                    }
                }
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(DmpProductInfo dmpProductInfo) {
        dmpProductInfo.setUpdatedTime(new Date());
        if (checkNameUnique(dmpProductInfo)){
            throw new BusinessException("修改产品信息'" + dmpProductInfo.getProductCode() + "'失败，产品编码已存在");
        }
        if (checkProductNameUnique(dmpProductInfo)){
            throw new BusinessException("修改产品信息'" + dmpProductInfo.getProductName() + "'失败，产品名称已存在");
        }
        this.updateById(dmpProductInfo);
    }

    @Override
    public boolean checkNameUnique(DmpProductInfo dmpProductInfo) { //根据产品编码判断唯一
        Integer id = null == dmpProductInfo.getId() ? -1 : dmpProductInfo.getId();
        LambdaQueryWrapper<DmpProductInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DmpProductInfo::getProductCode,dmpProductInfo.getProductCode())
                .eq(DmpProductInfo::getTenantId,SecurityUtils.getTenantId())
                .eq(DmpProductInfo::getDeleteFlag,0);
        DmpProductInfo one = this.getOne(queryWrapper);
        return null != one && !Objects.equals(one.getId(), id);
    }

    @Override
    public boolean checkProductNameUnique(DmpProductInfo dmpProductInfo) { //根据产品名称判断唯一
        Integer id = null == dmpProductInfo.getId() ? -1 : dmpProductInfo.getId();
        LambdaQueryWrapper<DmpProductInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DmpProductInfo::getProductName,dmpProductInfo.getProductName())
                .eq(DmpProductInfo::getTenantId,SecurityUtils.getTenantId())
                .eq(DmpProductInfo::getDeleteFlag,0);
        DmpProductInfo one = this.getOne(queryWrapper);
        return null != one && !Objects.equals(one.getId(), id);
    }


    @Override
    public CommonPage<DmpProductInfo> page(DmpProductInfoRequest piRequest) {
        IPage<DmpProductInfo> page = new Page<>(piRequest.getCurrent(), piRequest.getSize());
        LambdaQueryWrapper<DmpProductInfo> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery
                .like(StringUtils.isNotBlank(piRequest.getProductName()),DmpProductInfo::getProductName,piRequest.getProductName())
                .eq(StringUtils.isNotBlank(piRequest.getDeviceModel()),DmpProductInfo::getDeviceModel,piRequest.getDeviceModel())
                .eq(null != piRequest.getId(),DmpProductInfo::getId,piRequest.getId())
                .in(CollectionUtils.isNotEmpty(piRequest.getIdList()), DmpProductInfo::getId, piRequest.getIdList())
                .eq(DmpProductInfo::getTenantId,SecurityUtils.getTenantId())
                .eq(DmpProductInfo::getDeleteFlag,0);
        List<DmpProductInfo> records = this.list(lambdaQuery);
        List<Integer> productIds = records.stream().map(DmpProductInfo::getId).collect(Collectors.toList());
        List<Integer> deviceTypes = records.stream().map(DmpProductInfo::getDeviceType).collect(Collectors.toList());
        List<Map<String, Object>> maps = dmpDeviceInfoService.deviceCollect(productIds);
        List<Map<String, Object>> collectByProduct = dmpDeviceStatusService.getCollectByProduct(productIds);
        List<Map<String, Object>> imges = dmpDeviceTypeService.getDeviceTypeImges(deviceTypes);

//        maps.forEach(s -> {enhanceByDevice(records,s);});
//        collectByProduct.forEach(s -> {enhanceByDeviceStatus(records,s);});
        if(records.size()>0){
            for(int i=0;i<records.size();i++){
                if(maps.size()>0){
                    for(int j=0;j<maps.size();j++){
                        if(Integer.parseInt(maps.get(j).get("productId").toString())==(records.get(i).getId())){
                            String coun = maps.get(j).get("count").toString();
                            Integer it = Integer.parseInt(maps.get(j).get("count").toString());
                            records.get(i).setDeviceCount(Integer.parseInt(maps.get(j).get("count").toString()));
                            records.get(i).setServiceStatusCount(Integer.parseInt(maps.get(j).get("serviceCount").toString()));
                            continue;
                        }
                    }
                }
                if(collectByProduct.size()>0){
                    for(int j=0;j<collectByProduct.size();j++){
                        if(Integer.parseInt(collectByProduct.get(j).get("productId").toString())==(records.get(i).getId())){
                            records.get(i).setDeviceStatusCount(Integer.parseInt(collectByProduct.get(j).get("deviceCount").toString()));
                            continue;
                        }
                    }
                }
                if(imges.size()>0){
                    for(int j=0;j<imges.size();j++){
                        if(Integer.parseInt(imges.get(j).get("deviceType").toString())==(records.get(i).getDeviceType())){
                            if(StringUtils.isNotBlank(imges.get(j).get("typeImg").toString())){
                                records.get(i).setTypeImg(imges.get(j).get("typeImg").toString());
                            }

                        }
                    }

                }
            }

        }
        int total = 0;
        if(records.size()>0){
            total = records.size();
        }
        int current = 0;
        Integer pageNum = piRequest.getCurrent();
        Integer pageSize = piRequest.getSize();
        if(pageNum != null && pageSize > 0){
            current = (pageNum - 1)*pageSize;
        }
        List<DmpProductInfo> list = records.stream().sorted(Comparator.comparing(DmpProductInfo::getCreatedTime,Comparator.reverseOrder())).skip(current).limit(pageSize).collect(Collectors.toList());

        return new CommonPage<>(list,total,page.getCurrent(),page.getSize());
    }

//    public void enhanceByDevice(List<DmpProductInfo> dmpProductInfo, Map<String,Object> map){
//        dmpProductInfo.stream()
//                .filter(s -> map.get("productId").equals(s.getId().toString()))
//                .findAny()
//                .ifPresent(s -> {
//                    s.setDeviceCount(Integer.parseInt(map.get("count").toString()));
//                    s.setServiceStatusCount(Integer.parseInt(map.get("serviceCount").toString()));
//                });
//
//    }
//
//    public void enhanceByDeviceStatus(List<DmpProductInfo> dmpProductInfo, Map<String,Object> map){
//        dmpProductInfo.stream()
//                .filter(s -> map.get("productId").equals(s.getId().toString()))
//                .findAny()
//                .ifPresent(s -> {
//                    s.setDeviceStatusCount(Integer.parseInt(map.get("deviceCount").toString()));
//                }
//                );
//    }

    @Override
    public boolean remove(Integer id) {
        int deviceCount = dmpDeviceInfoService.deviceCountByProductId(id);
        if (deviceCount > 0){
            throw new BusinessException("产品下存在设备,请先删除设备，再删除产品");
        }
        DmpProductInfo dmpProductInfo = this.getById(id);
        Optional.ofNullable(dmpProductInfo).orElseThrow(() -> new BusinessException("产品不存在"));

        LambdaQueryWrapper<DmpProductAttribute> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper
                .eq(DmpProductAttribute::getProductId,id);
        List<DmpProductAttribute> list = dmpProductAttributeService.list(queryWrapper);
        if(CollectionUtils.isNotEmpty(list)){
            for(int i=0;i<list.size();i++){
                DmpProductAttribute dmpProductAttribute = new DmpProductAttribute();
                dmpProductAttribute.setId(list.get(i).getId());
                dmpProductAttribute.setDeleteFlag(1);
                dmpProductAttributeService.updateById(dmpProductAttribute);
            }
        }

        dmpProductInfo.setDeleteFlag(1);

        remoteTransferService.deleteProductCache();

        return this.updateById(dmpProductInfo);
    }

    @Override
    public List<Map<String, Object>> collect(Integer tenantId) {
        return baseMapper.selectCollect(tenantId);
    }

    @Override
    public List<Map<String, Object>> deviceCollect(Integer deptId) {
        List<Integer> productIds2 = new ArrayList<>();
        QueryWrapper<DmpProductInfo> query2 = Wrappers.query();
        query2.select("id as productId","product_name as productName")
                .eq("delete_flag",0)
                .eq("tenant_id",SecurityUtils.getTenantId());
        List<Map<String, Object>> productIds1 = this.listMaps(query2);
        if (productIds1.size()>0){
            for (int i = 0; i < productIds1.size(); i++) {
                productIds2.add(Integer.valueOf(productIds1.get(i).get("productId").toString()));
            }
        }
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(productIds2)){
            QueryWrapper<DmpDeviceStatus> query1 = Wrappers.query();
            query1.select("product_id as productId","count(*) as count","count(device_status != 1 or null) as " +
                    "faultCount")
                    .in("product_id",productIds2)
                    .groupBy("product_id");
            list = dmpDeviceStatusService.listMaps(query1);
            for (int l = 0; l < productIds1.size(); l++) {
                for (int j = 0; j < list.size(); j++){
                    if (productIds1.get(l).get("productId").equals(list.get(j).get("productId"))){
                        list.get(j).put("productName",productIds1.get(l).get("productName"));
                    }
                }
            }
        }
        return list;
    }

    @Override
    public List<Object> deviceStatusStatistic(Integer deptId,String productCode) {
        List<Object> list2 = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        LambdaQueryWrapper<DmpDeviceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DmpDeviceInfo::getTenantId,SecurityUtils.getTenantId());
        List<DmpDeviceInfo> deviceList = dmpDeviceInfoService.list(queryWrapper);
        List<String> deviceIds = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(deviceList)){
            for(int i=0;i<deviceList.size();i++){
                deviceIds.add(deviceList.get(i).getDeviceId());
            }
            QueryWrapper<DmpDeviceStatus> query = Wrappers.query();
            query.select("count(*) as count","COUNT(device_status=1 or null) as " +
                    "onCount","COUNT(device_status=2 or null) as offCount")
                    .eq(StringUtils.isNotBlank(productCode),"product_code",productCode)
                    .in("device_id",deviceIds);
            List<Map<String, Object>> list = dmpDeviceStatusService.listMaps(query);
            QueryWrapper<DmpDeviceInfo> query1 = Wrappers.query();
            query1.select("count(*) as count","COUNT(service_status=1 or null) as notActiveCount")
                    .eq(StringUtils.isNotBlank(productCode),"product_code",productCode)
                    .eq("tenant_id",SecurityUtils.getTenantId());
            List<Map<String, Object>> list1 = dmpDeviceInfoService.listMaps(query1);
            if (Integer.valueOf(list.get(0).get("offCount").toString()).equals(0)){
                map.put("count", 0);
                map.put("onCount", 0);
                map.put("offCount", 0);
                map.put("notActiveCount", 0);
                map.put("onCountBig", 0);
                map.put("offCountBig", 0);
                map.put("notActiveCountBig", 0);
            }else {
                BigDecimal onCountBig = new BigDecimal(Integer.valueOf(list.get(0).get("onCount").toString()));
                BigDecimal temp = new BigDecimal(Integer.valueOf(list.get(0).get("count").toString()));
                onCountBig = onCountBig.divide(temp,2,RoundingMode.HALF_UP);
                BigDecimal offCountBig = new BigDecimal(Integer.valueOf(list.get(0).get("offCount").toString()));
                offCountBig = offCountBig.divide(temp,2,RoundingMode.HALF_UP);
                BigDecimal notActiveCountBig = new BigDecimal(Integer.valueOf(list1.get(0).get("notActiveCount").toString()));
                notActiveCountBig = notActiveCountBig.divide(temp,2,RoundingMode.HALF_UP);
                map.put("count", Integer.valueOf(list.get(0).get("count").toString()));
                map.put("onCount", Integer.valueOf(list.get(0).get("onCount").toString()));
                map.put("offCount", Integer.valueOf(list.get(0).get("offCount").toString()));
                map.put("notActiveCount", Integer.valueOf(list1.get(0).get("notActiveCount").toString()));
                map.put("onCountBig", onCountBig);
                map.put("offCountBig", offCountBig);
                map.put("notActiveCountBig", notActiveCountBig);
            }
        }else {
            map.put("count", 0);
            map.put("onCount", 0);
            map.put("offCount", 0);
            map.put("notActiveCount", 0);
            map.put("onCountBig", 0);
            map.put("offCountBig", 0);
            map.put("notActiveCountBig", 0);
        }
        list2.add(map);
        return list2;
    }
}
