package com.huatek.busi.service.impl.curve;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.huatek.busi.common.SiteConstants;
import com.huatek.busi.entity.BusiExternalEquipment;
import com.huatek.busi.service.BusiExternalEquipmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.huatek.busi.common.BusiConstant.EqType;
import com.huatek.busi.common.BusiConstant.GzType;
import com.huatek.busi.common.CurveSubType;
import com.huatek.busi.common.CurveType;
import com.huatek.busi.common.SiteConstants.IecTypeEnum;
import com.huatek.busi.common.SiteConstants.SiteTreeEnum;
import com.huatek.busi.common.SiteConstants.TransfromerTreeEnum;
import com.huatek.busi.common.util.DateUtil;
import com.huatek.busi.dao.CurveSensorEntityDao;
import com.huatek.busi.dao.CurveSensorPointEntityDao;
import com.huatek.busi.dto.CurveSensorEntityDto;
import com.huatek.busi.dto.curve.BaseCurveQueryDto;
import com.huatek.busi.dto.curve.CurveSensorRequestDto;
import com.huatek.busi.dto.monitor.BusiOnlineMonitorTrendDataDto;
import com.huatek.busi.entity.CurveSensorEntity;
import com.huatek.busi.entity.CurveSensorPointEntity;
import com.huatek.busi.entity.IecBydworkParam;
import com.huatek.busi.mapper.CurveSensorEntityMapper;
import com.huatek.busi.service.BusiEquipmentRelationshipService;
import com.huatek.busi.service.curve.CurveSensorEntityService;
import com.huatek.busi.service.impl.curve.helper.CurveMapping;
import com.huatek.busi.service.impl.monitor.helper.MonitorDataTableMapping;
import com.huatek.busi.util.AlgorithmSingleton;
import com.huatek.busi.vo.TransformerTmpVo;
import com.huatek.busi.vo.curve.CurveSensorEntityVo;
import com.huatek.busi.vo.curve.PointItemVo;
import com.huatek.busi.vo.echarts.EchartLineDataVo;
import com.huatek.busi.vo.echarts.SeriesItemVo;
import com.huatek.frame.base.util.BeanUtils;
import com.huatek.torch.util.uuid.Uuid;

import lombok.extern.slf4j.Slf4j;

/**
 * sensor曲线图，一天一个点 ServiceImpl
 *
 * @author anker_cheng
 * @date Aug 30, 2019 1:43:30 PM
 */
@Slf4j
@Component
@Service(interfaceClass = CurveSensorEntityService.class)
@Transactional(rollbackFor = Exception.class)
public class CurveSensorEntityServiceImpl implements CurveSensorEntityService {
    private static final String UNDEFINED = "undefined";

    @Autowired
    private CurveSensorEntityMapper curveSensorEntityMapper;
    @Autowired
    private CurveSensorEntityDao curveSensorEntityDao;
    @Autowired
    private CurveSensorPointEntityDao curveSensorPointEntityDao;
    @Autowired
    private BusiExternalEquipmentService busiExternalEquipmentService;

    @Reference(check = false)
    private BusiEquipmentRelationshipService busiEquipmentRelationshipService;

    @Override
    public void doExecute(BaseCurveQueryDto baseCurve, LocalDate day, String subType,
        List<PointItemVo> daylyPointList) {
        // 删除CurveSensorEntity、CurveSensorPointEntity
        curveSensorEntityMapper.deleteCurveSensorEntityList(baseCurve, day, subType);
        // 保存
        CurveSensorEntity curveSensorEntity = new CurveSensorEntity();
        curveSensorEntity.setId(Uuid.getUUID());
        curveSensorEntity.setSensorCode(baseCurve.getSensorCode());
        curveSensorEntity.setMainTreeId(baseCurve.getMainTreeId());
        curveSensorEntity.setDay(day);
        curveSensorEntity.setSubType(subType);
        curveSensorEntityDao.add(curveSensorEntity);
        // 保存点
        for (PointItemVo vo : daylyPointList) {
            CurveSensorPointEntity point = new CurveSensorPointEntity();
            point.setId(Uuid.getUUID());
            point.setRefId(curveSensorEntity.getId());
            point.setTime(vo.getTimeStamp());
            point.setVal(vo.getVal());
            point.setExtendVala(vo.getExtendVala());
            point.setExtendValb(vo.getExtendValb());
            point.setExtendValc(vo.getExtendValc());
            curveSensorPointEntityDao.add(point);
        }
        // 修匀算法计算一个点
        List<Double> list = daylyPointList.stream().filter(e -> e.getVal() != null).map(PointItemVo::getVal)
            .map(BigDecimal::doubleValue).collect(Collectors.toList());
        curveSensorEntity.setVal(new BigDecimal(AlgorithmSingleton.repairValue(list)));
        curveSensorEntityDao.updateLocal(curveSensorEntity);
    }

    @Override
    public CurveSensorEntity saveCurveSensorEntity(CurveSensorEntityDto curveSensorEntityDto) {
        // 删除旧记录
        curveSensorEntityMapper.deleteCurveSensorEntityList(
            new BaseCurveQueryDto(curveSensorEntityDto.getMainTreeId(), curveSensorEntityDto.getSensorCode()),
            curveSensorEntityDto.getDay(), curveSensorEntityDto.getSubType());
        // 保存新记录
        CurveSensorEntity curveSensorEntity = new CurveSensorEntity();
        BeanUtils.copyProperties(curveSensorEntityDto, curveSensorEntity);
        curveSensorEntity.setId(Uuid.getUUID());
        curveSensorEntityDao.add(curveSensorEntity);
        return curveSensorEntity;
    }

    @Override
    public void saveCurveSensorPointEntity(CurveSensorEntity entity, List<PointItemVo> daylyPointList) {
        if (CollectionUtils.isEmpty(daylyPointList)) {
            return;
        }
        // 保存点
        for (PointItemVo vo : daylyPointList) {
            CurveSensorPointEntity point = new CurveSensorPointEntity();
            point.setId(Uuid.getUUID());
            point.setRefId(entity.getId());
            point.setTime(vo.getTimeStamp());
            point.setVal(vo.getVal());
            point.setExtendVala(vo.getExtendVala());
            point.setExtendValb(vo.getExtendValb());
            point.setExtendValc(vo.getExtendValc());
            curveSensorPointEntityDao.add(point);
        }
    }

    @Override
    public EchartLineDataVo getTransformerRunStateData(CurveSensorRequestDto param) {
        String subTitle = getTitleText(param);
        EchartLineDataVo result = new EchartLineDataVo(subTitle + "变压器负荷图");
        param.setSensorCode(param.getMainTreeId());
        if (!EqType.EQ_TYPE1.equals(param.getEqType())) {
            List<TransformerTmpVo> transformerList = busiEquipmentRelationshipService.getCurrentTransformerInfo(param.getMainTreeId());
            if (CollectionUtils.isEmpty(transformerList)) {
                log.warn("未获取到关联的变压器");
                return result;
            }
            param.setSensorCode(transformerList.get(0).getTransformerId());
        }
        CurveType curveType = CurveMapping.getCurveMapping(IecBydworkParam.class.getName());
        if (curveType == null) {
            log.warn("没有对应图表， gzType: {}, sensorCode: {}", param.getGzType(), param.getSensorCode());
            return result;
        }
        int availableCount = 0;
        Map<String, Map<String, Object>> allPointMap = Maps.newLinkedHashMap();
        for (CurveSubType subType : curveType.getSeries()) {
            List<PointItemVo> pointList = curveSensorEntityMapper.getHistoryPointList(param, subType.getColumn());
            if (CollectionUtils.isEmpty(pointList)) {
                continue;
            }
            String lineName = subTitle + subType.getName();
            availableCount += pointList.size();
            Map<String, Object> valMap = Maps.newHashMapWithExpectedSize(pointList.size());
            // 设置X轴坐标
            for (PointItemVo point : pointList) {
                String xVal = DateUtil.format(point.getTimeStamp());
                result.getXAxisData().add(xVal);
                // 工作状态
                String runState = "无";
                IecTypeEnum iecTypeEnum = IecTypeEnum.fromValue(point.getExtendValc());
                if (iecTypeEnum != null) {
                    runState = iecTypeEnum.getName();
                }
                // 值
                Map<String, Object> item = Maps.newLinkedHashMapWithExpectedSize(3);
                item.put("name", Lists.newArrayList("电压", "电流", "状态"));
                item.put("value", point.getVal());
                item.put("values", Lists.newArrayList(point.getExtendVala(), point.getExtendValb(), runState));
                valMap.put(xVal, item);
            }
            allPointMap.put(lineName, valMap);
        }
        //如果没有数据，设置开始时间和结束时间之间每天的凌晨为X轴
        if(allPointMap.isEmpty()) {
            long days = ChronoUnit.DAYS.between(param.getStartDate(), param.getEndDate());
            if (days >= 0) {
                Stream.iterate(param.getStartDate(), d -> d.plusDays(1)).limit(days + 1).forEach(day -> {
                    result.getXAxisData().add(DateUtil.format(day.atStartOfDay()));
                });
            }
        }
        // 根据X轴进行构造曲线
        for (CurveSubType subType : curveType.getSeries()) {
            String line = subTitle + subType.getName();
            Map<String, Object> linePointMap = allPointMap.get(line);
            List<Object> dataList = result.getXAxisData().stream().map(xAxis -> {
                if (linePointMap == null || linePointMap.isEmpty() || !linePointMap.containsKey(xAxis)) {
                    Map<String, Object> item = Maps.newLinkedHashMapWithExpectedSize(3);
                    item.put("name", Lists.newArrayList("电压", "电流", "状态"));
                    item.put("value", UNDEFINED);
                    item.put("values", Lists.newArrayList(UNDEFINED, UNDEFINED, UNDEFINED));
                    return item;
                }
                return linePointMap.get(xAxis);
            }).collect(Collectors.toList());
            result.getSeries().add(new SeriesItemVo(line, dataList));
        }
        result.setAvailableCount(availableCount);
        return result;
    }

    @Override
    public EchartLineDataVo getHistoryData(CurveSensorRequestDto param) {
        String subTitle = getTitleText(param);
        EchartLineDataVo result = new EchartLineDataVo(subTitle + "历史曲线");
        CurveType curveType = CurveMapping.getCurveMapping(param.getEqType(), param.getGzType());
        if (curveType == null) {
            log.warn("没有对应图表， gzType: {}, sensorCode: {}", param.getGzType(), param.getSensorCode());
            return result;
        }
        int availableCount = 0;
        // 设置X轴坐标值：升序且不重复；保存每条曲线每个X坐标对应的值在allPointMap里，X轴坐标值相同的点会覆盖
        Map<String, Map<String, Object>> allPointMap = Maps.newLinkedHashMap();
        for (CurveSubType subType : curveType.getSeries()) {
            List<PointItemVo> pointList = curveSensorEntityMapper.getHistoryPointList(param, subType.getColumn());
            if (CollectionUtils.isEmpty(pointList)) {
                continue;
            }
            availableCount += pointList.size();
            String lineName = subTitle + subType.getName();
            Map<String, Object> valMap = Maps.newLinkedHashMapWithExpectedSize(pointList.size());
            for (PointItemVo point : pointList) {
                String xVal = DateUtil.format(point.getTimeStamp());
                result.getXAxisData().add(xVal);
                valMap.put(xVal, point.getVal());
            }
            allPointMap.put(lineName, valMap);
        }
        //如果没有数据，设置开始时间和结束时间之间每天的凌晨为X轴
        if(allPointMap.isEmpty()) {
            long days = ChronoUnit.DAYS.between(param.getStartDate(), param.getEndDate());
            if (days >= 0) {
                Stream.iterate(param.getStartDate(), d -> d.plusDays(1)).limit(days + 1).forEach(day -> {
                    result.getXAxisData().add(DateUtil.format(day.atStartOfDay()));
                });
            }
        }
        // 根据X轴进行构造曲线
        setYAxisData(result, curveType, allPointMap, subTitle);
        result.setAvailableCount(availableCount);
        // 返回
        return result;
    }

    @Override
    public EchartLineDataVo getTrendData(CurveSensorRequestDto param) {
        CurveType curveType = CurveMapping.getCurveMapping(param.getEqType(), param.getGzType());
        return getTrendData(param, curveType);
    }

    @Override
    public EchartLineDataVo getTrendData(BusiOnlineMonitorTrendDataDto busiOnlineMonitorDataDto) {
        CurveSensorRequestDto param = new CurveSensorRequestDto();
        param.setMainTreeId(busiOnlineMonitorDataDto.getMainTreeId());
        param.setSensorCode(busiOnlineMonitorDataDto.getSensorCode());
        param.setStartDate(busiOnlineMonitorDataDto.getDay());
        param.setEndDate(busiOnlineMonitorDataDto.getDayEnd());
        if(SiteTreeEnum.BYQ_EQM.getValue().equals(busiOnlineMonitorDataDto.getEquipmentType())) {
            param.setEqType(EqType.EQ_TYPE1);
            if(TransfromerTreeEnum.TX_EQM.getValue().equals(busiOnlineMonitorDataDto.getSubEquipmentType())) {
                param.setGzType(GzType.GZ_TYPE3);
            }else if(TransfromerTreeEnum.JJ_EQM.getValue().equals(busiOnlineMonitorDataDto.getSubEquipmentType())) {
                param.setGzType(GzType.GZ_TYPE8);
            }
        }else if(SiteTreeEnum.OTHER_EQM.getValue().equals(busiOnlineMonitorDataDto.getEquipmentType())) {
            param.setEqType(EqType.EQ_TYPE5);
            param.setGzType(GzType.GZ_TYPE17);
            BusiExternalEquipment busiExternalEquipment = busiExternalEquipmentService.findBySensorCode(busiOnlineMonitorDataDto.getSensorCode(), SiteConstants.ExternalTypeEnum.OTHER.getValue());
            if (busiExternalEquipment != null) {
                param.setMainTreeId(busiExternalEquipment.getId());
            }
        }
        CurveType curveType = CurveMapping.getCurveMapping(MonitorDataTableMapping.getTableMapping(
            busiOnlineMonitorDataDto.getEquipmentType(), busiOnlineMonitorDataDto.getSubEquipmentType()));
        return getTrendData(param, curveType);
    }

    private EchartLineDataVo getTrendData(CurveSensorRequestDto param, CurveType curveType) {
        if(param.getStartDate() == null || param.getEndDate() == null) {
            param.check();
        }
        String subTitle = getTitleText(param);
        EchartLineDataVo result = new EchartLineDataVo(subTitle + "趋势曲线");
        if (curveType == null) {
            log.warn("没有对应图表， gzType: {}, sensorCode: {}", param.getGzType(), param.getSensorCode());
            return result;
        }
        int availableCount = 0;
        // 设置X轴坐标值：升序且不重复；保存每条曲线每个X坐标对应的值在allPointMap里，X轴坐标值相同的点会覆盖
        Map<String, Map<String, Object>> allPointMap = Maps.newLinkedHashMap();
        for (CurveSubType subType : curveType.getSeries()) {
            List<CurveSensorEntityVo> pointList = curveSensorEntityMapper.getTrendPointList(param, subType.getColumn());
            if (CollectionUtils.isEmpty(pointList)) {
                continue;
            }
            availableCount += pointList.size();
            String lineName = subTitle + subType.getName();
            Map<String, Object> valMap = Maps.newHashMapWithExpectedSize(pointList.size());
            for (CurveSensorEntityVo point : pointList) {
                valMap.put(point.getDay().toString(), point.getVal());
            }
            allPointMap.put(lineName, valMap);
        }
        //设置开始日期和结束日期之间的X轴值
        long days = ChronoUnit.DAYS.between(param.getStartDate(), param.getEndDate());
        if (days >= 0) {
            Stream.iterate(param.getStartDate(), d -> d.plusDays(1)).limit(days + 1)
                .forEach(day -> result.getXAxisData().add(day.toString()));
        }
        // 根据X轴进行构造曲线
        setYAxisData(result, curveType, allPointMap, subTitle);
        result.setAvailableCount(availableCount);
        return result;
    }

    static void setYAxisData(EchartLineDataVo result, CurveType curveType,
        Map<String, Map<String, Object>> allPointMap, String subTitle) {
        for (CurveSubType subType : curveType.getSeries()) {
            String line = subTitle + subType.getName();
            Map<String, Object> linePointMap = allPointMap.get(line);
            List<Object> dataList =
                result.getXAxisData().stream().map(xAxis -> getVal(linePointMap, xAxis)).collect(Collectors.toList());
            result.getSeries().add(new SeriesItemVo(line, dataList));
        }
    }


    static Object getVal(Map<String, Object> map, String key) {
        if(map == null || map.isEmpty()) {
            return UNDEFINED;
        }
        Object val = map.get(key);
        return val == null ? UNDEFINED : val;
    }

    static String getTitleText(CurveSensorRequestDto param) {
        if (GzType.GZ_TYPE3.equals(param.getGzType())) {
            return "铁芯";
        } else if (GzType.GZ_TYPE8.equals(param.getGzType())) {
            return "夹件";
        }
        return "";
    }
}
