package com.iformall.controller.device;

import com.alibaba.fastjson.JSONObject;
import com.iformall.common.ErrorCode;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.controller.base.BaseController;
import com.iformall.domain.po.KwBox;
import com.iformall.domain.po.KwBoxCmdHistory;
import com.iformall.enums.EnumBoxOnlineStatus;
import com.iformall.enums.EnumBoxRegisterStatus;
import com.iformall.service.kw.KwBoxCmdHistoryService;
import com.iformall.service.kw.KwBoxService;

import com.iformall.utils.HashUtil;
import com.iformall.utils.Utility;
import com.sun.crypto.provider.HmacSHA1;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("kwBox/v1")
@Api(description="开物盒子")
public class KwBoxV1Controller extends BaseController {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
    private KwBoxService kwBoxService;

	@Autowired
	private KwBoxCmdHistoryService kwBoxCmdHistoryService;


	private void saveCmd(String tenantId, String cmd, String imei, String ccid, Date deviceTime, Map<String,Object> paramMap)
	{
		KwBoxCmdHistory kwBoxCmdHistory = new KwBoxCmdHistory();
		kwBoxCmdHistory.setCmd(cmd);
		kwBoxCmdHistory.setCreateTime(new Date());
		kwBoxCmdHistory.setDeviceTime(deviceTime);
		kwBoxCmdHistory.setIccid(ccid);
		kwBoxCmdHistory.setImei(imei);
		kwBoxCmdHistory.setContent(JSONObject.toJSON(paramMap).toString());
		kwBoxCmdHistory.setTenantId(tenantId);
		kwBoxCmdHistoryService.saveOrUpdate(kwBoxCmdHistory);

	}

	private ResultData cmdRegist(String imei, String ccid, Date deviceTime, Map<String,Object> paramMap)
	{

		String fwv = (String)paramMap.get(KwBoxService.PREFIX_FW_VERSION);
		if (StringUtil.isNullOrEmpty(fwv))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		String hwv = (String)paramMap.get(KwBoxService.PREFIX_HW_VERSION);
		if (StringUtil.isNullOrEmpty(hwv))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		KwBox kwBox = kwBoxService.findByImei(imei);

		if (kwBox == null)
			return new ResultData(ErrorCode.DEVICE_NOT_FOUND);

		if (!kwBox.getRegisterStatus().equals(EnumBoxRegisterStatus.INIT.getCode()))
			return new ResultData(ErrorCode.DEVICE_ALREADY_EXIST);

		saveCmd(kwBox.getTenantId(),KwBoxService.CMD_REGISTER,imei,ccid,deviceTime,paramMap);

		KwBox record = new KwBox();
		record.setId(kwBox.getId());
		record.setIccid(ccid);
		record.setFwVersion(fwv);
		record.setHdVersion(hwv);
		record.setUpdateTime(new Date());
		record.setRegisterTime(new Date());
		record.setLastOnlineTime(new Date());
		record.setComKey(Utility.generate32UUID());
		record.setRegisterStatus(EnumBoxRegisterStatus.REGISTERED.getCode());
		kwBoxService.saveOrUpdate(record);
		Map<String,Object> data = new HashMap<>();

		data.put(KwBoxService.PREFIX_SYS_STATUS,record.getRegisterStatus());
		data.put(KwBoxService.PREFIX_COM_KEY,record.getComKey());
		if (kwBox.getConfig() != null)
			data.put(KwBoxService.PREFIX_CONFIG, JSONObject.parseObject(kwBox.getConfig()));

		List<String> meters = kwBoxService.findMetersAddress(kwBox);
		if (!meters.isEmpty())
			data.put(KwBoxService.PREFIX_METERS,meters);
		return new ResultData(data);

	}

	private boolean checkSign(KwBox kwBox, Map<String,Object> paramMap, String sign) {

		if (kwBox.getComKey() == null || sign == null)
			return false;

		HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, kwBox.getComKey());
		String content = JSONObject.toJSON(paramMap).toString();
		String signCom = hmacUtils.hmacHex(content);

		boolean res = signCom.equalsIgnoreCase(sign);

		if (!res) {
			logger.warn("SIGN check failed." + "content:"+content +"\n" + "signCom:" + signCom);
		}
		return res;
	}

	private ResultData cmdLogin(String imei, String ccid, Date deviceTime, Map<String,Object> paramMap, String sign)
	{
		KwBox kwBox = kwBoxService.findByImei(imei);

		if (kwBox == null)
			return new ResultData(ErrorCode.DEVICE_NOT_FOUND);

		if (!kwBox.getRegisterStatus().equals(EnumBoxRegisterStatus.REGISTERED.getCode()))
			return new ResultData(ErrorCode.DEVICE_NOT_FOUND);

		if (!checkSign(kwBox, paramMap, sign))
			return new ResultData(ErrorCode.DEVICE_SIGN_INVALID);

		saveCmd(kwBox.getTenantId(),KwBoxService.CMD_LOGIN,imei,ccid,deviceTime,paramMap);

		KwBox record = new KwBox();
		record.setId(kwBox.getId());
		record.setIccid(ccid);
		record.setUpdateTime(new Date());
		record.setLastOnlineTime(new Date());
		kwBoxService.saveOrUpdate(record);
		Map<String,Object> data = new HashMap<>();
		if (kwBox.getConfig() != null)
			data.put(KwBoxService.PREFIX_CONFIG,JSONObject.parseObject(kwBox.getConfig()));
		return new ResultData(data);
	}



	private ResultData cmdHb(String imei, String ccid, Date deviceTime, Map<String,Object> paramMap, String sign)
	{
		KwBox kwBox = kwBoxService.findByImei(imei);
		if (kwBox == null)
			return new ResultData(ErrorCode.DEVICE_NOT_FOUND);

		if (!kwBox.getRegisterStatus().equals(EnumBoxRegisterStatus.REGISTERED.getCode()))
			return new ResultData(ErrorCode.DEVICE_NOT_FOUND);

		if (!checkSign(kwBox, paramMap, sign))
			return new ResultData(ErrorCode.DEVICE_SIGN_INVALID);

		saveCmd(kwBox.getTenantId(),KwBoxService.CMD_HEARTBEAT,imei,ccid,deviceTime,paramMap);

		KwBox record = new KwBox();
		record.setId(kwBox.getId());
		record.setLastOnlineTime(new Date());
        record.setOnlineStatus(EnumBoxOnlineStatus.ONLINE.getCode());
		kwBoxService.saveOrUpdate(record);
		
		Instant instant = deviceTime.toInstant();
		ZoneId zone = ZoneId.systemDefault();
		LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
		if (localDateTime.getYear() != LocalDateTime.now().getYear())
			deviceTime = new Date();

		List<Map<String,String>> metersData = (List<Map<String,String>>)paramMap.get("meters");
		kwBoxService.saveMeterData(kwBox, metersData, deviceTime);

		List<String> meters = kwBoxService.findMetersAddress(kwBox);
		boolean updateMeters = false;
		boolean updateConfig = false;

		if (metersData.size() != meters.size())
		{
			updateMeters = true;
		} else {
			if (!meters.containsAll(metersData.stream().map(m->m.get(KwBoxService.METER_ADDRESS)).collect(Collectors.toList())))
				updateMeters = true;
		}

		Map<String,Object> resData = new HashMap<>();
		if (updateMeters) {
			resData.put(KwBoxService.PREFIX_METERS, meters);
		}
		if (updateConfig) {
			resData.put(KwBoxService.PREFIX_CONFIG, kwBox.getConfig());
		}
		return new ResultData(resData);

	}


	//“TIME”:”19/05/29 08:24:57 +32”,


	@ApiOperation("开物盒子V1")
	@PostMapping("command")
	public Result command(@RequestBody Map<String,Object> paramMap,@RequestParam String sign) {


		String cmd = (String)paramMap.get(KwBoxService.PREFIX_CMD);
		if (StringUtil.isNullOrEmpty(cmd))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		String imei = (String)paramMap.get(KwBoxService.PREFIX_IMEI);
		if (StringUtil.isNullOrEmpty(imei))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		String ccid = (String)paramMap.get(KwBoxService.PREFIX_ICCID);
		if (StringUtil.isNullOrEmpty(ccid))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		String time = (String)paramMap.get(KwBoxService.PREFIX_TIME);
		if (StringUtil.isNullOrEmpty(time))
			return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);

		SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yy/MM/dd,HH:mm:ss+32");
		simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+:00:00"));
		Date deviceTime = null;
		try {
			deviceTime=simpleDateFormat.parse(time);
		} catch(Exception e1) {
			simpleDateFormat=new SimpleDateFormat("yy/MM/dd,HH:mm:ss+00");
			simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+:00:00"));
			try {
				deviceTime=simpleDateFormat.parse(time);
			} catch(Exception e2) {
				return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);
			}
		}

		switch(cmd)
		{
			case KwBoxService.CMD_REGISTER:
				return cmdRegist(imei,ccid,deviceTime,paramMap);
			case KwBoxService.CMD_LOGIN:
				return cmdLogin(imei,ccid,deviceTime,paramMap,sign);
			case KwBoxService.CMD_HEARTBEAT:
				return cmdHb(imei,ccid,deviceTime,paramMap,sign);
		}

		return new ResultData(ErrorCode.SYS_PARAMETER_ERROR);
	}

}
