package com.jsowell.netty.handler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
import com.jsowell.common.enums.ykc.OrderStatusEnum;
import com.jsowell.common.enums.ykc.YKCPileFaultReasonEnum;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.YKCOperateFactory;
import com.jsowell.pile.domain.OrderBasicInfo;
import com.jsowell.pile.domain.PileBasicInfo;
import com.jsowell.pile.domain.StationSettingRelation;
import com.jsowell.pile.domain.ThirdPartySettingInfo;
import com.jsowell.pile.service.*;
import com.jsowell.pile.vo.base.StationSettingRelationVO;
import com.jsowell.thirdparty.lianlian.service.LianLianService;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * 获取桩上传的实时监测数据
 *
 * @author JS-ZZA
 * @date 2022/9/19 9:08
 */
@Slf4j
@Component
public class UploadRealTimeMonitorHandler extends AbstractHandler {
	private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.UPLOAD_REAL_TIME_MONITOR_DATA_CODE.getBytes());
	private final String oldVersionType = YKCUtils.frameType2Str(YKCFrameTypeCode.UPLOAD_REAL_TIME_MONITOR_DATA_OLD_VERSION_CODE.getBytes());

	@Override
	public void afterPropertiesSet() throws Exception {
		YKCOperateFactory.register(type, this);
		YKCOperateFactory.register(oldVersionType, this);
	}

	@Autowired
	private IPileBasicInfoService pileBasicInfoService;

	@Autowired
	private IOrderBasicInfoService orderBasicInfoService;

	@Autowired
	private IPileConnectorInfoService pileConnectorInfoService;

	@Autowired
	private IThirdPartySettingInfoService thirdPartySettingInfoService;

	@Autowired
	private LianLianService lianLianService;

	@Autowired
	private IStationSettingRelationService stationSettingRelationService;

	@Override
	public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, Channel channel) {
		log.info("[===获取桩上传的实时监测数据===] param:{}, channel:{}", JSONObject.toJSONString(ykcDataProtocol), channel.toString());
		RealTimeMonitorData realTimeMonitorData = new RealTimeMonitorData();

		// 获取消息体
		byte[] msgBody = ykcDataProtocol.getMsgBody();
		// log.info("上传实时数据msgBody:{}", BytesUtil.bcd2Str(msgBody));
		int startIndex = 0;
		int length = 16;

		// 交易流水号
		byte[] orderCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String transactionCode = BytesUtil.bcd2Str(orderCodeByteArr);
		realTimeMonitorData.setTransactionCode(transactionCode);

		// 桩编码
		startIndex += length;
		length = 7;
		byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
		realTimeMonitorData.setPileSn(pileSn);

		// 保存时间
		saveLastTime(pileSn);

		// 枪号
		startIndex += length;
		length = 1;
		byte[] pileConnectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String connectorCode = BytesUtil.bcd2Str(pileConnectorCodeByteArr);
		realTimeMonitorData.setConnectorCode(connectorCode);

		// 枪口状态 0x00:离线  0x01:故障  0x02:空闲  0x03:充电
		startIndex += length;
		length = 1;
		byte[] connectorStatusByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String connectorStatus = BytesUtil.bcd2Str(connectorStatusByteArr);
		realTimeMonitorData.setConnectorStatus(connectorStatus);

		// 是否归位 0x00:否  0x01:是  0x02:未知（无法检测到枪是否插回枪座即 未知）
		startIndex += length;
		length = 1;
		byte[] homingFlagByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String homingFlag = BytesUtil.bcd2Str(homingFlagByteArr);
		realTimeMonitorData.setHomingFlag(homingFlag);

		// 是否插枪 0x00:否  0x01:是
		startIndex += length;
		length = 1;
		byte[] putGunTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String putGunType = BytesUtil.bcd2Str(putGunTypeByteArr);
		realTimeMonitorData.setPutGunType(putGunType);

		// 输出电压 精确到小数点后一位；待机置零
		startIndex += length;
		length = 2;
		byte[] outputVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String outputVoltage = YKCUtils.convertVoltageCurrent(outputVoltageByteArr);
		realTimeMonitorData.setOutputVoltage(outputVoltage);

		// 输出电流 精确到小数点后一位；待机置零
		startIndex += length;
		length = 2;
		byte[] outputCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String outputCurrent = YKCUtils.convertVoltageCurrent(outputCurrentByteArr);
		realTimeMonitorData.setOutputCurrent(outputCurrent);

		// 枪线温度 整形, 偏移量-50；待机置零
		startIndex += length;
		length = 1;
		byte[] gunLineTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String gunLineTemperature = YKCUtils.transitionTemperature(gunLineTemperatureByteArr);
		realTimeMonitorData.setGunLineTemperature(gunLineTemperature);

		// 枪线编码 没有置零
		startIndex += length;
		length = 8;
		byte[] gunLineCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String gunLineCode = BytesUtil.bcd2Str(gunLineCodeByteArr);
		realTimeMonitorData.setGunLineCode(gunLineCode);

		// SOC 待机置零；交流桩置零
		startIndex += length;
		length = 1;
		byte[] SOCByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String SOC = String.valueOf(SOCByteArr[0]);
		realTimeMonitorData.setSOC(SOC);

		// 电池组最高温度 整形, 偏移量-50 ºC；待机置零； 交流桩置零
		startIndex += length;
		length = 1;
		byte[] batteryMaxTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String batteryMaxTemperature = YKCUtils.transitionTemperature(batteryMaxTemperatureByteArr);
		realTimeMonitorData.setBatteryMaxTemperature(batteryMaxTemperature);

		// 累计充电时间 单位: min；待机置零
		startIndex += length;
		length = 2;
		byte[] sumChargingTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		int sumChargingTime = BytesUtil.bytesToIntLittle(sumChargingTimeByteArr);
		realTimeMonitorData.setSumChargingTime(String.valueOf(sumChargingTime));

		// 剩余时间 单位: min；待机置零、交流桩置零
		startIndex += length;
		length = 2;
		byte[] timeRemainingByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		int timeRemaining = BytesUtil.bytesToIntLittle(timeRemainingByteArr);
		realTimeMonitorData.setTimeRemaining(String.valueOf(timeRemaining));

		// 充电度数 精确到小数点后四位；待机置零
		startIndex += length;
		length = 4;
		byte[] chargingDegreeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String chargingDegree = YKCUtils.convertDecimalPoint(chargingDegreeByteArr, 4);
		realTimeMonitorData.setChargingDegree(chargingDegree);

		// 计损充电度数 精确到小数点后四位；待机置零 未设置计损比例时等于充电度数
		startIndex += length;
		length = 4;
		byte[] lossDegreeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String lossDegree = YKCUtils.convertDecimalPoint(lossDegreeByteArr, 4);
		realTimeMonitorData.setLossDegree(lossDegree);

		// 已充金额 精确到小数点后四位；待机置零 （电费+服务费） *计损充电度数
		startIndex += length;
		length = 4;
		byte[] chargingAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String chargingAmount = YKCUtils.convertDecimalPoint(chargingAmountByteArr, 4);
		realTimeMonitorData.setChargingAmount(chargingAmount);

		/**
		 * 硬件故障
		 *
		 * Bit 位表示（0 否 1 是）, 低位到高位顺序
		 * Bit1:急停按钮动作故障；
		 * Bit2:无可用整流模块；
		 * Bit3:出风口温度过高；
		 * Bit4:交流防雷故障；
		 * Bit5:交直流模块 DC20 通信中断；
		 * Bit6:绝缘检测模块 FC08 通信中断；
		 * Bit7:电度表通信中断；
		 * Bit8:读卡器通信中断；
		 * Bit9: RC10 通信中断；
		 * Bit10:风扇调速板故障；
		 * Bit11:直流熔断器故障；
		 * Bit12:高压接触器故障；
		 * Bit13:门打开；
		 */
		startIndex += length;
		length = 2;
		byte[] hardwareFaultTempByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
		String hardwareFaultTemp = BytesUtil.bcd2Str(hardwareFaultTempByteArr);
		String faultReason = "无";

		if (!StringUtils.equals(hardwareFaultTemp, "0000")) {
			// 不等于0000说明有故障
			StringBuffer sb = new StringBuffer(hardwareFaultTemp);
			String lowOrder = sb.substring(0, 2);
			String highOrder = sb.substring(2, 4);

			// String hardwareFault = highOrder + lowOrder;
			byte[] hardwareFaultByteArr = BytesUtil.str2Bcd(highOrder + lowOrder);
			String binStr = BytesUtil.bytes2BinStr(hardwareFaultByteArr);
			// log.info("binStr:{}", binStr); // 0000 0000 0000 0001
			int faultCode = 0;
			for (int i = 0; i < binStr.length(); i++) {
				if (binStr.charAt(i) == '1') {
					faultCode = 15 - i;
					break;
				}
			}
			faultReason = YKCPileFaultReasonEnum.getValueByCode(faultCode);
			// log.info("故障码:{}, 故障原因:{}", faultCode, faultReason);
		}
		realTimeMonitorData.setHardwareFault(hardwareFaultTemp);

		if (!StringUtils.equals(connectorStatus, "02")) {
			log.info("0x13上传实时监测数据==交易流水号:{}, 桩编号:{}, 枪号:{}, 状态:{}, 枪是否归位:{}, 是否插枪:{}, 输出电压:{}, 输出电流:{}, 枪线温度:{}, " +
							"枪线编码:{}, SOC:{}, 电池组最高温度:{}, 累计充电时间:{}, 剩余时间:{}, 充电度数:{}, 记损充电度数:{}, 已充金额:{}, " +
							"硬件故障:{}, 故障码转换结果:{}", transactionCode, pileSn, connectorCode, connectorStatus, homingFlag, putGunType, outputVoltage,
					outputCurrent, gunLineTemperature, gunLineCode, SOC, batteryMaxTemperature, sumChargingTime, timeRemaining,
					chargingDegree, lossDegree, chargingAmount, hardwareFaultTemp, faultReason
			);
		}

		// 公共方法修改状态
		pileBasicInfoService.updateStatus(BytesUtil.bcd2Str(ykcDataProtocol.getFrameType()), pileSn, connectorCode, connectorStatus, putGunType);

		// 03表示充电中
		if (StringUtils.equals(connectorStatus, "03")) {
			// 充电时保存实时数据到redis
			pileBasicInfoService.saveRealTimeMonitorData2Redis(realTimeMonitorData);

			// 查询数据库中该订单当前信息
			// OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(transactionCode);
			OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
			if (Objects.nonNull(orderInfo)) {
				boolean updateFlag = false;
				if (StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.NOT_START.getValue())
						|| StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.ABNORMAL.getValue())) {
					updateFlag = true;
					// 如果是未启动状态或者异常状态, 修改这个订单状态为充电中
					orderInfo.setOrderStatus(OrderStatusEnum.IN_THE_CHARGING.getValue());
				}

				// 如果原来没有开始充电时间就保存当前时间为开始充电时间
				if (orderInfo.getChargeStartTime() == null) {
					updateFlag = true;
					orderInfo.setChargeStartTime(new Date());
				}

				if (updateFlag) {
					orderBasicInfoService.updateOrderBasicInfo(orderInfo);
				}
			}
		}

		try {
			// 推送联联平台
			OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
			if (orderInfo == null) {
				return null;
			}
			Map<String, String> pileStatusMap = pileConnectorInfoService.getPileStatus(Lists.newArrayList(pileSn));
			String pileStatus = pileStatusMap.get(pileSn);
			if (StringUtils.equals(connectorStatus, pileStatus)) {
				return null;
			}
			// 状态不一样，传给联联
			// 查询该站点是否推送联联平台
			StationSettingRelation relation = new StationSettingRelation();
			if (StringUtils.isBlank(orderInfo.getStationId())) {
				return null;
			}
			relation.setStationId(Long.parseLong(orderInfo.getStationId()));
			StationSettingRelationVO relationInfo = stationSettingRelationService.selectRelationInfo(relation);
			// ThirdPartySettingInfo thirdPartySettingInfo = thirdPartySettingInfoService.getInfoByStationId(pileBasicInfo.getStationId());
			if (Objects.nonNull(relationInfo)) {
				String pileConnectorCode = pileSn + connectorCode;
				log.info("推送联联平台实时数据 params: orderBasicInfo:{}", JSON.toJSONString(orderInfo));
				// 设备状态变化推送  notification_stationStatus
				lianLianService.pushConnectorStatus(pileConnectorCode, connectorStatus);
				if (StringUtils.equals(connectorStatus, "03")) {
					// 充电中
					lianLianService.pushPileChargeStatusChange(orderInfo.getOrderCode());
				}
				// 推送充电状态
				lianLianService.pushChargeStatus(orderInfo.getOrderCode());
			}
		} catch (Exception e) {
			log.error("推送联联平台 实时数据 异常", e);
		}

		return null;
	}

}
