package com.jsowell.pile.service.impl;

import com.google.common.collect.Lists;
import com.google.common.primitives.Bytes;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.ykc.PileChannelEntity;
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.CRC16Util;
import com.jsowell.common.util.Cp56Time2a.Cp56Time2aUtil;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.pile.domain.ykcCommond.GetRealTimeMonitorDataCommand;
import com.jsowell.pile.domain.ykcCommond.IssueQRCodeCommand;
import com.jsowell.pile.domain.ykcCommond.PileSettingCommand;
import com.jsowell.pile.domain.ykcCommond.ProofreadTimeCommand;
import com.jsowell.pile.domain.ykcCommond.PublishPileBillingTemplateCommand;
import com.jsowell.pile.domain.ykcCommond.QueryWorkParamsCommand;
import com.jsowell.pile.domain.ykcCommond.RebootCommand;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand;
import com.jsowell.pile.domain.ykcCommond.StopChargingCommand;
import com.jsowell.pile.domain.ykcCommond.UpdateFirmwareCommand;
import com.jsowell.pile.service.YKCPushCommandService;
import com.jsowell.pile.service.IPileBasicInfoService;
import com.jsowell.pile.service.IPileBillingTemplateService;
import com.jsowell.pile.service.IPileConnectorInfoService;
import com.jsowell.pile.service.IPileModelInfoService;
import com.jsowell.pile.service.IPileMsgRecordService;
import com.jsowell.pile.service.IPileStationInfoService;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.PileModelInfoVO;
import com.jsowell.pile.vo.web.PileStationVO;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class YKCPushCommandServiceImpl implements YKCPushCommandService {
	@Autowired
	private IPileBillingTemplateService pileBillingTemplateService;

	@Autowired
	private IPileModelInfoService pileModelInfoService;

	@Autowired
	private IPileBasicInfoService pileBasicInfoService;

	@Autowired
	private IPileStationInfoService pileStationInfoService;

	@Autowired
	private RedisCache redisCache;

	@Autowired
	private IPileMsgRecordService pileMsgRecordService;

	@Autowired
	private IPileConnectorInfoService pileConnectorInfoService;

	// 需要记录报文的数据帧类型
	private final List<String> frameTypeList = Lists.newArrayList(
			YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_RESTART_CODE.getBytes()),
			YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_START_CODE.getBytes()),
			YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_STOP_CHARGING_CODE.getBytes())
	);

	public boolean push(byte[] msg, String pileSn, Enum<YKCFrameTypeCode> frameTypeCode) {
		// 通过桩编号获取channel
		Channel channel = PileChannelEntity.getChannelByPileSn(pileSn);
		if (Objects.isNull(channel)) {
			log.error("push命令[{}]失败, 桩号:{}无法获取到长连接, 请检查充电桩连接状态!", ((YKCFrameTypeCode) frameTypeCode).getValue(), pileSn);
			return false;
		}
		/**
		 * 拼接报文
		 */
		// 起始标志
		byte[] head = new byte[]{0x68};

		// 序列号域
		byte[] serialNumber = new byte[]{0x00, 0x00};

		// 加密标志
		byte[] encryptFlag = new byte[]{0x00};

		// 帧类型标志
		byte[] frameType = new byte[]{(byte) ((YKCFrameTypeCode) frameTypeCode).getCode()};

		// 序列号域+加密标志+帧类型标志+消息体
		byte[] temp = Bytes.concat(serialNumber, encryptFlag, frameType, msg);

		// 数据长度
		byte[] length = BytesUtil.intToBytes(temp.length, 1);

		// 帧校验域
		byte[] crc = BytesUtil.intToBytes(CRC16Util.calcCrc16(temp));
		// 返回报文
		byte[] writeMsg = Bytes.concat(head, length, temp, crc);
		// 返回完整的报文 string类型
		String wholeMsg= BytesUtil.binary(writeMsg, 16);
		log.info("[" + channel.remoteAddress() + "] 主动发送push请求信息:{}", wholeMsg);
		ByteBuf byteBuf = channel.alloc().buffer().writeBytes(writeMsg);
		ChannelFuture channelFuture = channel.writeAndFlush(byteBuf);
		channelFuture.addListener(new ChannelFutureListener() {
			@Override
			public void operationComplete(ChannelFuture channelFuture) throws Exception {
				// 检查操作的状态
				if (channelFuture.isSuccess()) {
					log.info("push结果【成功】, remoteAddress:{}, channelId:{}, 报文:{}, ", channel.remoteAddress(), channel.id(), wholeMsg);
				} else {
					// 如果发生错误，则访问描述原因的Throwable
					Throwable cause = channelFuture.cause();
					cause.printStackTrace();
					log.info("push结果【失败】, remoteAddress:{}, channelId:{}, 报文:{}", channel.remoteAddress(), channel.id(), wholeMsg);
					log.error("push发送命令失败", cause);
				}
			}
		});

		// 保存报文
		String frameTypeStr = YKCUtils.frameType2Str(((YKCFrameTypeCode) frameTypeCode).getBytes());
		if (frameTypeList.contains(frameTypeStr)) {
			pileMsgRecordService.save(pileSn, null, frameTypeStr, null, wholeMsg);
		}
		return true;
	}

	/**
	 * 发送启动充电指令
	 */
	@Override
	public void pushStartChargingCommand(StartChargingCommand command) {
		String pileSn = command.getPileSn();
		String connectorCode = command.getConnectorCode();
		String transactionCode = command.getTransactionCode();
		if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode) ) {
			log.warn("远程启动充电, 充电桩编号和枪口号不能为空");
			return;
		}
		if (StringUtils.isEmpty(transactionCode)) {
			log.warn("远程启动充电, 交易流水号不能为空");
			return;
		}
		if (command.getChargeAmount() == null || BigDecimal.ZERO.equals(command.getChargeAmount())) {
			log.warn("远程启动充电, 充电金额不能为0");
			return;
		}

		// 枪口号
		byte[] connectorCodeByteArr = BytesUtil.str2Bcd(connectorCode);

		// 交易流水号
		byte[] orderIdByteArr = BytesUtil.str2Bcd(transactionCode);

		// 桩编号
		byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);

		// 逻辑卡号
		String logicCardNum = StringUtils.isBlank(command.getLogicCardNum())
				? Constants.ZERO
				: command.getLogicCardNum();
		byte[] logicCardNumByteArr = BytesUtil.checkLengthAndFrontAppendZero(BytesUtil.str2Bcd(logicCardNum), 16);

		// 物理卡号
		String physicsCardNum = StringUtils.isBlank(command.getPhysicsCardNum())
				? Constants.ZERO
				: command.getPhysicsCardNum();
		byte[] physicsCardNumByteArr = BytesUtil.checkLengthAndFrontAppendZero(BytesUtil.str2Bcd(physicsCardNum), 16);

		// 账户余额
		BigDecimal chargeAmount = command.getChargeAmount();
		byte[] accountBalanceByteArr = YKCUtils.getPriceByte(chargeAmount.toString(), 2);

		byte[] msgBody = Bytes.concat(orderIdByteArr, pileSnByteArr, connectorCodeByteArr, logicCardNumByteArr, physicsCardNumByteArr, accountBalanceByteArr);
		this.push(msgBody, pileSn, YKCFrameTypeCode.REMOTE_CONTROL_START_CODE);
		log.info("【=====平台下发充电指令=====】:订单id:{}, 桩号:{}, 枪口号:{}, 逻辑卡号:{}, 物理卡号:{}, 账户余额:{}",
				transactionCode, pileSn, BytesUtil.bcd2Str(connectorCodeByteArr), logicCardNum, physicsCardNum, chargeAmount);
	}

	/**
	 * 发送停止ch
	 * @param command
	 */
	@Override
	public void pushStopChargingCommand(StopChargingCommand command) {
		String pileSn = command.getPileSn();
		String connectorCode = command.getConnectorCode();
		// 远程停机
		byte[] msgBody = Bytes.concat(BytesUtil.str2Bcd(pileSn), BytesUtil.str2Bcd(connectorCode));
		this.push(msgBody, pileSn, YKCFrameTypeCode.REMOTE_STOP_CHARGING_CODE);
		log.info("【=====平台下发指令=====】:远程停止充电,桩号：{},枪口号：{}", pileSn, connectorCode);
	}

	@Override
	public void pushGetRealTimeMonitorDataCommand(GetRealTimeMonitorDataCommand command) {
		String pileSn = command.getPileSn();
		String connectorCode = command.getConnectorCode();
		byte[] msg = BytesUtil.str2Bcd(pileSn + connectorCode);
		this.push(msg, pileSn, YKCFrameTypeCode.READ_REAL_TIME_MONITOR_DATA_CODE);
		log.info("【=====平台下发指令=====】:获取充电桩：{} 的 {} 枪口实时数据信息", pileSn, connectorCode);
	}

	@Override
	public void pushRebootCommand(RebootCommand command) {
		String pileSn = command.getPileSn();
		byte[] msg = BytesUtil.str2Bcd(pileSn + Constants.ZERO_ONE);
		log.info("【=====平台下发指令=====】:重启充电桩:,{}", pileSn);
		this.push(msg, pileSn, YKCFrameTypeCode.REMOTE_RESTART_CODE);

	}

	// 下发二维码
	@Override
	public void pushIssueQRCodeCommand(IssueQRCodeCommand command) {
		// log.info("异步下发二维码 thread:{}", Thread.currentThread().getName());
		String pileSn = command.getPileSn();
		// 桩编码
		byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);

		// 二维码格式  0x00：第一种  前缀+桩编号    0x01：第二种  前缀+桩编号+枪编号
		byte[] qrCodeTypeByteArr = Constants.oneByteArray;

		// 二维码前缀  如：“www.baidu.com？No=”
		// String qrCodePrefix = "https://wx.charging.shbochong.cn/prepare_charge?code=";
		// String qrCodePrefix = pileBasicInfoService.getPileQrCodeUrl(null);
		String qrCodePrefix = "";
		if (StringUtils.isNotBlank(command.getQrcodePrefix())) {
			qrCodePrefix = command.getQrcodePrefix();
		} else {
			PileStationVO pileStationVO = pileStationInfoService.getStationInfoByPileSn(pileSn);
			if (StringUtils.isNotBlank(pileStationVO.getQrcodePrefix())) {
				qrCodePrefix = pileStationVO.getQrcodePrefix();
			} else {
				// 为空则给平台二维码前缀
				qrCodePrefix = pileConnectorInfoService.getPileConnectorQrCodeUrl(null);
			}
		}

		byte[] qrCodePrefixByteArr = BytesUtil.str2Asc(qrCodePrefix);

		// 二维码前缀长度  二维码前缀长度长度最大不超过200 字节
		int length = qrCodePrefix.length();
		byte[] qrCodePrefixLengthByteArr = BytesUtil.intToBytes(length, 1);

		// 拼接消息体
		byte[] msg = Bytes.concat(pileSnByteArr, qrCodeTypeByteArr, qrCodePrefixLengthByteArr, qrCodePrefixByteArr);

		// push消息
		boolean result = this.push(msg, pileSn, YKCFrameTypeCode.REMOTE_ISSUE_QRCODE_CODE);
		log.info("=====平台下发指令===== :下发二维码,地址为：{}", qrCodePrefix);
	}

	/**
	 * 0x56 对时设置
	 * @param command
	 */
	@Override
	public void pushProofreadTimeCommand(ProofreadTimeCommand command) {
		// log.info("充电桩对时，thread:{}", Thread.currentThread().getName());
		String pileSn = command.getPileSn();
		// Date date = new Date();
		// Date parseDate = DateUtils.parseDate("2023-02-28 16:45:20");
		Date date = DateUtils.parseDate(DateUtils.getTime());
		// 根据不同程序版本获取工具类
		// String programVersion = redisCache.getCacheMapValue(CacheConstants.PILE_PROGRAM_VERSION, pileSn);
		// AbsCp56Time2aUtil cp56Time2aUtil = Cp56Time2aFactory.getInvokeStrategy(programVersion);
		// String encodeCP56Time2a = cp56Time2aUtil.date2HexStr(date);
		byte[] bytes = Cp56Time2aUtil.date2Hbyte(date);
		byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);
		byte[] msg = Bytes.concat(pileSnByteArr, bytes);

		this.push(msg, pileSn, YKCFrameTypeCode.TIME_CHECK_SETTING_CODE);
		log.info("[充电桩:{}对时, 时间:{}, CP56Time2a:{}]", pileSn, DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date), BytesUtil.binary(bytes, 16));
	}

	/**
	 * 向充电桩发送计费模板
	 * @param command
	 */
	@Override
	public void pushPublishPileBillingTemplate(PublishPileBillingTemplateCommand command) {
		BillingTemplateVO billingTemplateVO = command.getBillingTemplateVO();
		String pileSn = command.getPileSn();
		// 转换
		byte[] messageBody = pileBillingTemplateService.generateBillingTemplateMsgBody(pileSn, billingTemplateVO);
		// 发送
		if (messageBody != null) {
			this.push(messageBody, pileSn, YKCFrameTypeCode.BILLING_TEMPLATE_SETTING_CODE);
		}
	}

	@Override
	public void pushUpdateFileCommand(UpdateFirmwareCommand command) {
		List<String> pileSns = command.getPileSnList();
		if (CollectionUtils.isEmpty(pileSns)) {
			return;
		}

		List<PileModelInfoVO> list = pileModelInfoService.getPileModelInfoByPileSnList(pileSns);
		if (CollectionUtils.isEmpty(list)) {
			return;
		}
		// 获取桩型号   01：直流  02：交流
		byte[] pileModelType;
		for (PileModelInfoVO pileModelInfoVO : list) {
			byte[] pileSnByteArr = BytesUtil.str2Bcd(pileModelInfoVO.getPileSn());

			// 数据库 1- 快充（直流）  	2-慢充（交流）
			/*if (StringUtils.equals(pileModelInfoVO.getSpeedType(), Constants.ONE)) {
				pileModelType = Constants.oneByteArray;
			} else {
				pileModelType = Constants.twoByteArray;
			}*/
			pileModelType = Constants.zeroByteArray;

			// 额定功率
			String ratedPower = pileModelInfoVO.getRatedPower();
			int i = Integer.parseInt(ratedPower);

			// byte[] ratedPowerByteArr = Base64.getDecoder().decode(ratedPower);
			byte[] ratedPowerByteArr = BytesUtil.checkLengthAndBehindAppendZero(Constants.zeroByteArray, 4);

			// 升级服务器地址
			byte[] updateServerAddressByteArr = BytesUtil.checkLengthAndBehindAppendZero(BytesUtil.str2Asc(Constants.updateServerIP), 32);

			// 升级服务器端口
			byte[] updateServerPortByteArr = BytesUtil.checkLengthAndBehindAppendZero(Constants.updateServerPort, 4);
			// byte[] updateServerPortByteArr = BytesUtil.checkLengthAndBehindAppendZero(BytesUtil.str2Bcd("15"), 4);

			// 用户名
			byte[] userNameByteArr = BytesUtil.checkLengthAndBehindAppendZero(BytesUtil.str2Asc(Constants.updateServerUserName), 32);

			// 密码
			byte[] passwordByteArr = BytesUtil.checkLengthAndBehindAppendZero(BytesUtil.str2Asc(Constants.updateServerPassword), 32);

			// 文件路径
			byte[] filePathByteArr = BytesUtil.checkLengthAndBehindAppendZero(BytesUtil.str2Asc(Constants.filePath), 64);

			// 执行控制   01：立即执行  02：空闲执行
			byte[] performTypeByteArr = Constants.oneByteArray;

			// 下载超时时间 单位：min
			byte[] overTimeByteArr = new byte[]{0x05};

			byte[] msgBody = Bytes.concat(pileSnByteArr, pileModelType, ratedPowerByteArr, updateServerAddressByteArr,
					updateServerPortByteArr, userNameByteArr, passwordByteArr, filePathByteArr, performTypeByteArr, overTimeByteArr);

			this.push(msgBody, pileModelInfoVO.getPileSn(), YKCFrameTypeCode.REMOTE_UPDATE_CODE);
			log.info("【=====平台下发指令=====】:远程更新, 桩号：{}, 类型：{}, 额定功率：{}, 服务器地址：{}, 端口号：{}, 用户名：{}, 密码：{}, 文件路径：{}",
					pileModelInfoVO.getPileSn(), pileModelType, BytesUtil.bcd2Str(ratedPowerByteArr), BytesUtil.binary(updateServerAddressByteArr, 16),
					BytesUtil.binary(updateServerPortByteArr, 16), BytesUtil.binary(userNameByteArr, 16), BytesUtil.binary(passwordByteArr, 16),
					BytesUtil.binary(filePathByteArr, 16));
		}
	}

	@Override
	public void pushPileSettingCommand(PileSettingCommand command) {

		// 充电桩编号
		String pileSn = command.getPileSn();
		byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);

		// 工作状态
		String workingState = command.getWorkingState();
		byte[] workingStateByteArr;
		if (StringUtils.equals(workingState, "0")) {
			workingStateByteArr = Constants.zeroByteArray;
		} else {
			workingStateByteArr = Constants.oneByteArray;
		}

		// 最大功率
		Integer maxPower = command.getMaxPower();
		byte[] maxPowerByteArr = BytesUtil.str2Bcd(maxPower.toString());

		byte[] msg = Bytes.concat(pileSnByteArr, workingStateByteArr, maxPowerByteArr);

		this.push(msg, pileSn, YKCFrameTypeCode.CHARGING_PILE_WORKING_PARAMETER_SETTING_CODE);
	}

	/**
	 * 平台査询工作参数
	 * @param command
	 */
	@Override
	public void pushQueryWorkParamsCommand(QueryWorkParamsCommand command) {
		// 桩编号
		String pileSn = command.getPileSn();
		byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);
		// 桩类型
		byte[] pileType = Constants.oneByteArray;
		PileModelInfoVO info = pileModelInfoService.getPileModelInfoByPileSn(pileSn);
		if (info == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_GET_PILE_DETAIL_ERROR);
		}
		String chargerPileType = info.getChargerPileType();
		if (StringUtils.equals("1", chargerPileType)) {
			// 直流
			pileType = Constants.zeroByteArray;
		}
		// 拼装msg信息
		byte[] msg = Bytes.concat(pileSnByteArr, pileType);

		this.push(msg, pileSn, YKCFrameTypeCode.QUERY_PILE_WORK_PARAMS_CODE);
	}
}
