package com.njcn.prepare.harmonic.service.mysql.Impl.line;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.common.pojo.constant.BizParamConstant;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.FileUtil;
import com.njcn.device.pq.api.DeptLineFeignClient;
import com.njcn.device.pq.api.DeviceUnitClient;
import com.njcn.device.biz.pojo.po.PqsDeviceUnit;
import com.njcn.harmonic.enums.HarmonicResponseEnum;
import com.njcn.harmonic.pojo.dto.ReportTemplateDTO;
import com.njcn.harmonic.pojo.po.ExcelRpt;
import com.njcn.harmonic.pojo.po.ExcelRptTemp;
import com.njcn.harmonic.pojo.po.SysDeptTemp;
import com.njcn.influx.deprecated.InfluxDBSqlConstant;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.prepare.harmonic.constant.AlgorithmParam;
import com.njcn.prepare.harmonic.mapper.mysql.line.DeptTempMapper;
import com.njcn.prepare.harmonic.mapper.mysql.line.ExcelRptMapper;
import com.njcn.prepare.harmonic.mapper.mysql.line.ExcelRptTempMapper;
import com.njcn.prepare.harmonic.pojo.param.LineParam;
import com.njcn.prepare.harmonic.service.mysql.line.ReportService;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.dto.QueryResult;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 自定义报表
 *
 * @author qijian
 * @date 2022/10/20
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ReportServiceImpl implements ReportService {

    private final DeptLineFeignClient deptLineFeignClient;
    private final InfluxDbUtils influxDbUtils;
    private final ExcelRptTempMapper excelRptTempMapper;
    private final ExcelRptMapper excelRptMapper;
    private final DeptTempMapper deptTempMapper;
    private final FileStorageUtil fileStorageUtil;
    private final DeviceUnitClient deviceUnitClient;
    private final DicDataFeignClient dicDataFeignClient;


    @Override
    @Async("asyncExecutor")
    public void batchReport(LineParam reportParam) {
        //查询所有已激活模板
        List<ExcelRptTemp> reportTemplateList = excelRptTempMapper.getActiveTempList();


        if (CollUtil.isEmpty(reportTemplateList)) {
            throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_ACTIVE);
        }

        //有监测点，匹配监测点的数据进行处理
        //无监测点，查询稳态所有监测点
        Map<String, List<String>> map = deptLineFeignClient.getLineByDeptRelation(AlgorithmParam.STEADY_STATE).getData();

        //根据content进行处理
        List<ReportTemplateDTO> dataList;
        JSONArray jsonArray;
        for (ExcelRptTemp excelRptTemp : reportTemplateList) {
            //获取已绑定的部门信息
            List<SysDeptTemp> sysDeptTemps = deptTempMapper.selectList(new LambdaQueryWrapper<SysDeptTemp>()
                    .eq(SysDeptTemp::getTempId, excelRptTemp.getId())
                    .eq(SysDeptTemp::getActivation, 1)
            );
            List<String> list = new ArrayList<>();
            if (CollUtil.isNotEmpty(sysDeptTemps)) {
                //筛选部门下面所包含的监测点
                for (SysDeptTemp sysDeptTemp : sysDeptTemps) {
                    if (map.containsKey(sysDeptTemp.getDeptId())) {
                        list.addAll(map.get(sysDeptTemp.getDeptId()));
                    }
                }
            }
            if (CollUtil.isEmpty(list)) {
                continue;
            }
            try (InputStream fileStream = fileStorageUtil.getFileStream(excelRptTemp.getContent())) {
                //获取content解析数据
                jsonArray = new JSONArray(new JSONTokener(fileStream, new JSONConfig()));
                dataList = getDataList(jsonArray);
            } catch (Exception e) {
                throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_JSON);
            }

            //查询influxDB数据
            List<ReportTemplateDTO> endList = new ArrayList<>();
            for (String lineId : list) {
                //先判断该模板是否是当前监测点部门的

                if (CollUtil.isNotEmpty(dataList)) {
                    //开始组织sql
                    dataList.stream().forEach(data -> {
                        StringBuilder sql = new StringBuilder(InfluxDBSqlConstant.SELECT);
                        switch (data.getStatMethod()) {
                            case InfluxDBTableConstant.MAX:
                                assSql(data, sql, endList, InfluxDBSqlConstant.MAX, lineId, reportParam);
                                break;
                            case InfluxDBTableConstant.MIN:
                                assSql(data, sql, endList, InfluxDBSqlConstant.MIN, lineId, reportParam);
                                break;
                            case InfluxDBTableConstant.AVG:
                                assSql(data, sql, endList, InfluxDBSqlConstant.AVG, lineId, reportParam);
                                break;
                            case InfluxDBTableConstant.CP95:
                                assSql(data, sql, endList, InfluxDBSqlConstant.CP95, lineId, reportParam);
                                break;
                            default:
                                break;
                        }
                    });
                }

                //根据数据填充content
                if (CollUtil.isNotEmpty(endList)) {
                    contentChange(jsonArray, endList, lineId);
                }

                //存入报表库
                //年：例如2022年，传入2022-01-01进行匹配，有则更新无则插入
                //季：例如2022第二季度，传入2022-04-01进行匹配，有则更新无则插入
                //月：例如2022十月份，传入2022-10-01进行匹配，有则更新无则插入
                //周：例如2022年第五周，传入2022-01-23（周一）进行匹配，有则更新无则插入
                //日：直接插入，无需配对
                //文件上传到文件服务器，存入文件名
                if (BizParamConstant.STAT_BIZ_DAY.equals(reportParam.getType().toString())) {
                    rptInsert(reportParam, lineId, excelRptTemp, contentToOss(jsonArray.toString()));
                } else {
                    rptBiz(reportParam, lineId, excelRptTemp, contentToOss(jsonArray.toString()));
                }
            }
        }
    }

    /**
     * rpt业务方法
     *
     * @param reportParam,lineId,excelRptTemp,afterContent 参数
     * @return 结果
     */
    private void rptBiz(LineParam reportParam, String lineId, ExcelRptTemp excelRptTemp, String afterContent) {
        LambdaQueryWrapper<ExcelRpt> lambdaQuery = new LambdaQueryWrapper<>();
        lambdaQuery.eq(ExcelRpt::getLineId, lineId)
                .eq(ExcelRpt::getTempId, excelRptTemp.getId())
                .eq(ExcelRpt::getType, reportParam.getType())
                .eq(ExcelRpt::getDataDate, reportParam.getDataDate());
        ExcelRpt excelRpt = excelRptMapper.selectOne(lambdaQuery);
        if (excelRpt != null) {
            excelRpt.setContent(afterContent);
            excelRptMapper.updateById(excelRpt);
        } else {
            rptInsert(reportParam, lineId, excelRptTemp, afterContent);
        }
    }

    /**
     * 新增rpt方法
     *
     * @param reportParam,lineId,excelRptTemp,afterContent 参数
     * @return 结果
     */
    private void rptInsert(LineParam reportParam, String lineId, ExcelRptTemp excelRptTemp, String afterContent) {
        ExcelRpt excelRpt = new ExcelRpt();
        excelRpt.setName(excelRptTemp.getName());
        excelRpt.setLineId(lineId);
        excelRpt.setDataDate(DateUtil.parse(reportParam.getDataDate()));
        excelRpt.setTempId(excelRptTemp.getId());
        excelRpt.setContent(afterContent);
        excelRpt.setType(reportParam.getType());
        excelRpt.setState(DataStateEnum.ENABLE.getCode());
        excelRpt.setCreateBy("jobAdmin");
        excelRpt.setUpdateBy("jobAdmin");
        excelRptMapper.insert(excelRpt);
    }

    /**
     * 根据数据填充content
     *
     * @param jsonArray 参数
     * @return 结果
     */
    private void contentChange(JSONArray jsonArray, List<ReportTemplateDTO> endList, String lineID) {
        //数据单位信息
        Map<String, String> unit = unitMap(lineID);
        //进行反向赋值到模板
        //1、根据itemName分组
        Map<String, List<ReportTemplateDTO>> assMap = endList.stream().collect(Collectors.groupingBy(ReportTemplateDTO::getItemName));
        //2、把itemName的value赋给v和m
        jsonArray.forEach(item -> {
            JSONObject jsonObject = (JSONObject) item;
            JSONArray itemArr = (JSONArray) jsonObject.get("celldata");
            itemArr.forEach((it) -> {
                if (Objects.nonNull(it) && !"null".equals(it.toString())) {
                    //获取到1列
                    JSONObject data = (JSONObject) it;
                    JSONObject son = (JSONObject) data.get("v");
                    if (son.containsKey("v")) {
                        String v = son.getStr("v");
                        //数据格式：$HA[_25]#B#max#classId$ 或 $HA[_25]#max#classId$
                        if (v.charAt(0) == '$' && v.contains("#")) {
                            String str = assMap.get(v.replace("$", "")).get(0).getValue();
                            //没有值，赋"/"
                            if (StrUtil.isBlank(str)) {
                                str = "/";
                            }
                            son.set("v", str);
                        }
                        //解决数据单位问题 @指标#类型@
                        if (v.charAt(0) == '@' && v.contains("#")) {
                            String replace = v.replace("@", "");
                            if (unit.containsKey(replace)) {
                                son.set("v", unit.get(replace));
                            } else {
                                son.set("v", "/");
                            }

                        }
                    }
                }
            });
        });
    }

    /**
     * 获取content解析数据
     *
     * @param jsonArray 参数
     * @return 结果
     */
    private List<ReportTemplateDTO> getDataList(JSONArray jsonArray) {
        List<ReportTemplateDTO> reportTemplateDTOList = new ArrayList<>();
        jsonArray.forEach(item -> {
            JSONObject jsonObject = (JSONObject) item;
            JSONArray itemArr = (JSONArray) jsonObject.get("celldata");
            itemArr.forEach((it) -> {
                if (Objects.nonNull(it) && !"null".equals(it.toString())) {
                    //获取到1列
                    JSONObject data = (JSONObject) it;
                    JSONObject son = (JSONObject) data.get("v");
                    if (son.containsKey("v")) {
                        String v = son.getStr("v");
                        //数据格式：$HA[_25]#B#max#classId$ 或 $HA[_25]#max#classId$
                        if (v.charAt(0) == '$' && v.contains("#")) {
                            //剔除前后$
                            v = v.replace("$", "");
                            //封装ReportTemplateDTO
                            ReportTemplateDTO reportTemplateDTO = new ReportTemplateDTO();
                            reportTemplateDTO.setItemName(v);
                            //根据#分割数据
                            String[] vItem = v.split("#");
                            if (vItem.length == 4) {
                                //$HA[_25]#B#max#classId$
                                reportTemplateDTO.setTemplateName(vItem[0]);
                                reportTemplateDTO.setPhase(vItem[1]);
                                reportTemplateDTO.setStatMethod(vItem[2].toUpperCase());
                                reportTemplateDTO.setClassId(vItem[3]);
                            } else if (vItem.length == 3) {
                                //$HA[_25]#max#classId$
                                reportTemplateDTO.setTemplateName(vItem[0]);
                                reportTemplateDTO.setPhase("M");
                                reportTemplateDTO.setStatMethod(vItem[1].toUpperCase());
                                reportTemplateDTO.setClassId(vItem[2]);
                            }
                            reportTemplateDTOList.add(reportTemplateDTO);
                        }
                    }
                }
            });
        });

        return reportTemplateDTOList;
    }

    /**
     * 数据单位信息
     *
     * @param lineID
     * @return
     */
    private Map<String, String> unitMap(String lineID) {
        PqsDeviceUnit deviceUnit = deviceUnitClient.lineUnitDetail(lineID).getData();
        Map<String, String> unit = new HashMap<>();
        List<DictData> dictData = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.DEVICE_UNIT.getCode()).getData();
        List<String> list = dictData.stream().map(DictData::getCode).collect(Collectors.toList());
        for (String s : list) {
            //有效值
            if (s.equals(DicDataEnum.EFFECTIVE.getCode())) {
                unit.put(s + "#i", deviceUnit.getIeffective());
                unit.put(s + "#v", deviceUnit.getLineVoltage());
            }
            //功率
            if (s.equals(DicDataEnum.POWER.getCode())) {
                unit.put(s + "#p", deviceUnit.getTotalActiveP());
                unit.put(s + "#q", deviceUnit.getTotalNoP());
                unit.put(s + "#s", deviceUnit.getTotalViewP());
            }
            //畸变率
            if (s.equals(DicDataEnum.DISTORTION.getCode())) {
                unit.put(s + "#v", deviceUnit.getVdistortion());
            }
            //电压偏差
            if (s.equals(DicDataEnum.VOLTAGE.getCode())) {
                unit.put(s + "#v", deviceUnit.getVoltageDev());
            }
            //频率
            if (s.equals(DicDataEnum.UNIT_FREQUENCY.getCode())) {
                unit.put(s + "#freq", deviceUnit.getUnitFrequency());
                unit.put(s + "#freqDev", deviceUnit.getUnitFrequencyDev());
            }
            //三项不平衡度
            if (s.equals(DicDataEnum.UNBALANCE.getCode())) {
                unit.put(s + "#v", "%");
                unit.put(s + "#vPos", deviceUnit.getPositiveV());
                unit.put(s + "#vNeg", deviceUnit.getNoPositiveV());
                unit.put(s + "#vZero", deviceUnit.getNoPositiveV());
                unit.put(s + "#i", "%");
                unit.put(s + "#iPos", "A");
                unit.put(s + "#iNeg", "A");
                unit.put(s + "#iZero", "A");
            }
            //基波
            if (s.equals(DicDataEnum.FUND.getCode())) {
                unit.put(s + "#i", deviceUnit.getIfund());
                unit.put(s + "#v", deviceUnit.getVfundEffective());

            }
        }
        return unit;
    }

    /**
     * 组装influxDB查询sql，查询value并封装endlist
     *
     * @param data,sql,endList,method,reportSearchParam 参数
     * @param reportParam
     * @return 结果
     */
    private void assSql(ReportTemplateDTO data, StringBuilder sql, List<ReportTemplateDTO> endList, String method, String lineId, LineParam reportParam) {
        //sql拼接示例：select MAX(IHA2) as IHA2 from power_quality_data where Phase = 'A' and LineId='1324564568' and Stat_Method='max' tz('Asia/Shanghai')
        //cp95函数特殊处理 PERCENTILE(field_key, N)
        if (InfluxDBSqlConstant.CP95.equals(method)) {
            sql.append(method).append(InfluxDBSqlConstant.LBK).append(data.getTemplateName()).append(InfluxDBSqlConstant.NUM_95).append(InfluxDBSqlConstant.RBK).append(InfluxDBSqlConstant.AS_VALUE);
        } else {
            sql.append(method).append(InfluxDBSqlConstant.LBK).append(data.getTemplateName()).append(InfluxDBSqlConstant.RBK).append(InfluxDBSqlConstant.AS_VALUE);
        }
        sql.append(InfluxDBSqlConstant.FROM).append(data.getClassId()).append(InfluxDBSqlConstant.WHERE).append(InfluxDBTableConstant.LINE_ID).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(lineId).append(InfluxDBSqlConstant.QM);
        //相别特殊处理
        if (!InfluxDBTableConstant.NO_PHASE.equals(data.getPhase())) {
            sql.append(InfluxDBSqlConstant.AND).append(InfluxDBTableConstant.PHASIC_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(data.getPhase()).append(InfluxDBSqlConstant.QM);
        }

        //data_flicker、data_fluc、data_plt 无 value_type
        if (!InfluxDBTableConstant.DATA_FLICKER.equals(data.getClassId()) && !InfluxDBTableConstant.DATA_FLUC.equals(data.getClassId()) && !InfluxDBTableConstant.DATA_PLT.equals(data.getClassId())) {
            sql.append(InfluxDBSqlConstant.AND).append(InfluxDBTableConstant.VALUE_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(data.getStatMethod()).append(InfluxDBSqlConstant.QM);
        }
        String startTime;
        String endTime;
        DateTime dateTime = new DateTime(reportParam.getDataDate(), DatePattern.NORM_DATE_PATTERN);
        //时间范围需要处理下
        switch (reportParam.getType()) {
            case 1:
                startTime = DateUtil.beginOfYear(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                endTime = DateUtil.endOfYear(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                break;
            case 2:
                startTime = DateUtil.beginOfQuarter(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                endTime = DateUtil.endOfQuarter(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                break;
            case 3:
                startTime = DateUtil.beginOfMonth(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                endTime = DateUtil.endOfMonth(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                break;
            case 4:
                startTime = DateUtil.beginOfWeek(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                endTime = DateUtil.endOfWeek(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                break;
            default:
                startTime = DateUtil.beginOfDay(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                endTime = DateUtil.endOfDay(dateTime).toString(DatePattern.NORM_DATETIME_PATTERN);
                break;
        }
        sql
                .append(InfluxDBSqlConstant.AND)
                .append(InfluxDBSqlConstant.TIME).append(InfluxDBSqlConstant.GE).append(InfluxDBSqlConstant.QM).append(startTime).append(InfluxDBSqlConstant.QM)
                .append(InfluxDBSqlConstant.AND)
                .append(InfluxDBSqlConstant.TIME).append(InfluxDBSqlConstant.LT).append(InfluxDBSqlConstant.QM).append(endTime).append(InfluxDBSqlConstant.QM);

        sql.append(InfluxDBSqlConstant.TZ);
        //根据不同的库表赋值
        QueryResult queryResult = influxDbUtils.query(String.valueOf(sql));

        //剖析influx数据，填入value
        List<QueryResult.Result> results = queryResult.getResults();
        if (results.size() != 0) {
            QueryResult.Result result = results.get(0);
            if (result.getSeries() != null) {
                List<QueryResult.Series> seriess = result.getSeries();
                if (seriess.size() != 0) {
                    QueryResult.Series series = seriess.get(0);
                    List<String> columns = series.getColumns();
                    List<List<Object>> values = series.getValues();
                    for (List<Object> columnValue : values) {
                        for (int i = 0; i < columnValue.size(); i++) {
                            if (columns.get(i).equals("value")) {
                                data.setValue(String.format("%.3f", columnValue.get(i)));
                            }
                        }
                    }
                }
            }
        }
        endList.add(data);
    }


    /**
     * 上传文件到Oss
     *
     * @param content 文件
     * @return 成功标记
     */
    private String contentToOss(String content) {
        InputStream reportStream = IoUtil.toStream(content, CharsetUtil.UTF_8);
        return fileStorageUtil.uploadStream(reportStream, OssPath.HARMONIC_EXCEL_REPORT, FileUtil.generateFileName("json"));
    }


}
