package com.jpsoft.shinestar.lapi.handler;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jpsoft.shinestar.config.OSSConfig;
import com.jpsoft.shinestar.lapi.dto.PersonVerification;
import com.jpsoft.shinestar.lapi.dto.PersonVerificationResponseInfo;
import com.jpsoft.shinestar.modules.base.entity.*;
import com.jpsoft.shinestar.modules.base.service.*;
import com.jpsoft.shinestar.modules.business.service.WorkAttendanceService;
import com.jpsoft.shinestar.modules.common.utils.LApiUtil;
import com.jpsoft.shinestar.modules.common.utils.OSSUtil;
import com.jpsoft.shinestar.modules.sys.entity.SysLog;
import com.jpsoft.shinestar.modules.sys.service.DataDictionaryService;
import com.jpsoft.shinestar.modules.sys.service.SysLogService;
import com.jpsoft.shinestar.modules.uniview.dto.PersonNotification;
import com.jpsoft.shinestar.modules.uniview.service.ILapiService;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@ChannelHandler.Sharable
public class PersonVerificationHandler extends SimpleChannelInboundHandler<PersonVerification> {
    @Autowired
    private OSSConfig ossConfig;

    @Autowired
    private PersonDeviceLogService personDeviceLogService;

    @Autowired
    private PersonDeviceFilterLogService personDeviceFilterLogService;

    @Autowired
    private WorkAttendanceService workAttendanceService;

    @Autowired
    private SysLogService sysLogService;

    @Autowired
    private ProjectInfoService projectInfoService;

    @Autowired
    private PersonInfoService personInfoService;

    @Autowired
    private RolePersonCheckOnService rolePersonCheckOnService;

    @Autowired
    private ValueOperations<String,Object> valueOperations;

    @Autowired
    private ILapiService lapiService;

    @Autowired
    private DeviceInfoService deviceInfoService;

    @Autowired
    private DataDictionaryService dataDictionaryService;

    @Autowired
    private WorkPersonSchedulingService workPersonSchedulingService;

    @Autowired
    private WorkShiftInfoService workShiftInfoService;

    @Autowired
    private PersonPopedomService personPopedomService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    private  final String X_FRAME_OPTIONS = "X-Frame-Options";
    private  final String SAMEORIGIN = "SAMEORIGIN";
    private  final String JSON_PLAIN = "application/json";
    private  final String CONTENT_LENGTH = "Content-Length";
    private  final String CONTENT_TYPE = "Content-Type";
    private  final String CONNECTION = "Connection";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, PersonVerification personVerification) throws Exception {
        new Thread(()->{
            asyncWork(ctx,personVerification);
        }).start();
    }

    public void asyncWork(ChannelHandlerContext ctx,PersonVerification personVerification) {
        log.warn("===人员记录异步处理 begin===");

        JSONObject jsonObject = personVerification.getJsonObject();

                // log.warn("jsonObject:"+jsonObject);

                JSONObject faceJson = new JSONObject(jsonObject.getStr("FaceInfoList").substring(1,jsonObject.getStr("FaceInfoList").length()-1));

        Long timestamp = faceJson.getLong("Timestamp");

        if (timestamp.toString().length()<13){
            double timestampDouble = timestamp *  Math.pow(10, 13 - timestamp.toString().length());
            timestamp =new Double(timestampDouble).longValue() ;
        }

        //log.warn(String.valueOf(timestamp));
        Date date = new Date(timestamp);

        //设备序列号
        String deviceNo = jsonObject.getStr("DeviceCode");

        //人员温度信息
        BigDecimal temperature = faceJson.getBigDecimal("Temperature");

        //戴口罩状态（0：未开启口罩验证，1：未戴口罩，2：戴口罩）
        Integer maskFlag = faceJson.getInt("MaskFlag");

        //人员抓拍信息
        JSONObject faceImageJson = faceJson.getJSONObject("FaceImage");

        //匹配状态信息
        JSONObject libMatInfoListJson = jsonObject.getJSONArray("LibMatInfoList").getJSONObject(0);

        if(libMatInfoListJson==null){
            libMatInfoListJson = new JSONObject();
        }

        //匹配人员信息
        JSONObject matchPersonInfo = new JSONObject();
        if (StringUtils.isNotBlank(libMatInfoListJson.getStr("MatchPersonInfo"))){
            matchPersonInfo = libMatInfoListJson.getJSONObject("MatchPersonInfo");
        }

        log.warn("人员记录写入");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        HashMap map = new HashMap<>();
        map.put("deviceNo",deviceNo);
        map.put("temperature",temperature);
        map.put("libMatInfoListJson",libMatInfoListJson);
        map.put("matchPersonInfo",matchPersonInfo);
        map.put("recordTime",sdf.format(date));

        log.warn(map.toString());

//        SysLog sysLog = new SysLog();
//        sysLog.setPointcut(deviceNo);
//        sysLog.setUrl("/LAPI/V1.0/System/Event/Notification/PersonVerification");
//        sysLog.setData(map.toString());
//        sysLog.setRemark("收到人脸识别信息");
//        SocketAddress remoteAddress = ctx.channel().remoteAddress();
//        sysLog.setRemoteIp(remoteAddress.toString());
//        sysLog.setCreateTime(new Date());
//        sysLogService.insert(sysLog);

        //上传人脸信息
        String faceImageUrl = "";

        try {
            if (StringUtils.isNotBlank(faceImageJson.getStr("Data"))){
                BASE64Decoder decoder = new BASE64Decoder();

                byte[] imgData = decoder.decodeBuffer(faceImageJson.getStr("Data"));

                for (int i = 0; i < imgData.length; ++i) {
                    if (imgData[i] < 0) {// 调整异常数据
                        imgData[i] += 256;
                    }
                }

                ByteArrayInputStream inputStream = new ByteArrayInputStream(imgData);
                faceImageUrl = OSSUtil.upload(ossConfig, "/devicePersonLog", faceImageJson.getStr("Name"), inputStream);
            }

        } catch (Exception e) {
            log.error("保存图片路径错误" + e.getMessage());
        }

        log.warn("记录人员测温记录");
        String personDeviceLogId = personDeviceLogService.deviceInsertLog(deviceNo,temperature,faceImageJson,libMatInfoListJson,matchPersonInfo,date,maskFlag,faceImageUrl);

        log.warn("记录人员测温过滤记录");
        personDeviceFilterLogService.deviceInsertFilterLog(deviceNo,temperature,faceImageJson,libMatInfoListJson,matchPersonInfo,date,maskFlag,faceImageUrl,null,null);

        log.warn("人员打卡记录上报后加入队列以便后续处理");

        try {
            if (rabbitTemplate!=null) {
                PersonInfo personInfo = LApiUtil.getPersonCode(libMatInfoListJson, matchPersonInfo);

                PersonNotification personNotification = new PersonNotification();

                personNotification.setPersonId(personInfo.getId());
                personNotification.setDeviceNo(deviceNo);
                personNotification.setTemperature(temperature);
                personNotification.setRecordTime(date);
                personNotification.setPersonDeviceLogId(personDeviceLogId);

                rabbitTemplate.convertAndSend("personNotificationQueue", personNotification);

                //访客记录表添加进厂时间
                rabbitTemplate.convertAndSend("personGuestRecordQueue",personNotification);
            }
            else{
                log.error("消息队列未配置!");
            }
        }
        catch (Exception ex){
            log.error(ex.getMessage());
        }

        log.warn("===人员记录异步处理 end===");

        //返回数据
        try {
            log.warn("===人员记录回包处理 start===");
            PersonVerificationResponseInfo responseInfo = new PersonVerificationResponseInfo();

            responseInfo.getResponse().setResponseURL("/LAPI/V1.0/PACS/Controller/Event/Notifications");
            responseInfo.getResponse().setStatusCode(0);
            responseInfo.getResponse().setStatusString("Succeed");
//            responseInfo.getResponse().setStatusCode(1);
//            responseInfo.getResponse().setStatusString("Failed");

            //即记录推送中的"ID"（长连接V1）或"Seq"（长连接 V2）
            if (jsonObject.containsKey("ID")) {
                responseInfo.getResponse().getData().setRecordID(jsonObject.getInt("ID"));
            } else {
                responseInfo.getResponse().getData().setRecordID(jsonObject.getInt("Seq"));
            }

            responseInfo.getResponse().getData().setTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));

            String json = JSONUtil.toJsonStr(responseInfo);

            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(json.getBytes("UTF-8")));
            response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
            response.headers().set(CONTENT_TYPE, JSON_PLAIN);
            response.headers().set(CONNECTION, HttpHeaderValues.CLOSE);
            response.headers().set(X_FRAME_OPTIONS, SAMEORIGIN);

            ctx.channel().writeAndFlush(response);

            log.warn("===人员记录回包结束 end===");

            long matchPersonID = libMatInfoListJson.getLong("MatchPersonID");
            String personName = matchPersonInfo.getStr("PersonName");

            new Thread(()->{
                //查询大门是否需要远程控制
                remoteControl(ctx,deviceNo, matchPersonID, personName, date);
            }).start();
        }
        catch (Exception ex){
            log.error(ex.getMessage(),ex);
        }
    }

    /**
     * 远程开门需要关闭记录上传响应，否则会不停重发(因为没有正确返回ID或Seq)
     * @param ctx
     * @param deviceNo
     * @param matchPersonID
     */
    private void remoteControl(ChannelHandlerContext ctx,String deviceNo, Long matchPersonID,String matchPersonName, Date recordTime) {
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmmss");

        System.out.println(sdf1.format(recordTime));

        //这是首先要判断设备是否用于大门
        DeviceInfo deviceInfo = deviceInfoService.getByDeviceNo(deviceNo);

        if (deviceInfo==null){
            log.error("{}无对应设备信息!", deviceNo);
            return;
        }

        List<String> sceneNameList = deviceInfoService.findSceneListByDeviceNo(deviceNo);

        long doorCount = sceneNameList.stream().filter(scene->scene.indexOf("大门")==-1).count();

        if (doorCount==0) {
            return;
        }

        RolePersonCheckOn rolePersonCheckOn = rolePersonCheckOnService.findByPersonId(String.valueOf(matchPersonID));

        if (rolePersonCheckOn==null){
           List<PersonPopedom> popedomList = personPopedomService.findByPersonId(matchPersonID);

           if (popedomList.size()>0) {
               PersonPopedom personPopedom = popedomList.get(0);
               rolePersonCheckOn = rolePersonCheckOnService.findByRoleId(personPopedom.getPersonRoleId());
           }
        }

        String prefix = "";

        if (rolePersonCheckOn!=null && rolePersonCheckOn.getCheckOnCountFlag()){
            try {
                DateTime now = DateTime.now();

                String date = now.toString("yyyy-MM-dd");

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");

                //指定时间段
                List<Map> timeRangeList = new ArrayList<>();

                if (rolePersonCheckOn.getCheckOnTimeFlag()){
                    if(StringUtils.isNotEmpty(rolePersonCheckOn.getCheckStartTime())
                    && StringUtils.isNotEmpty(rolePersonCheckOn.getCheckEndTime())){
                        //指定时间段
                        DateTime startTime = new DateTime(sdf.parse(date + " " + rolePersonCheckOn.getCheckStartTime()));
                        DateTime endTime = new DateTime(sdf.parse(date + " " + rolePersonCheckOn.getCheckEndTime()));

                        Map<String,DateTime> timeRangeMap = new HashMap<>();
                        timeRangeMap.put("startTime" ,startTime);
                        timeRangeMap.put("endTime", endTime);

                        timeRangeList.add(timeRangeMap);

                        prefix = String.valueOf(matchPersonID);
                    }
                }
                else{
                    //读取用户当前排班
                    WorkPersonScheduling workPersonScheduling = workPersonSchedulingService.findByPersonIdSchedulingDay(matchPersonID,
                            now.withTimeAtStartOfDay().toDate());

                    if (workPersonScheduling==null){
                        log.error("{}当天未排班!", matchPersonID);
                        return;
                    }

                    String shiftIds = workPersonScheduling.getShiftIds();

                    String[] shiftIdArr = shiftIds.split(",");

                    for (String shiftId : shiftIdArr) {
                        WorkShiftInfo workShiftInfo = workShiftInfoService.get(shiftId);

                        if(workShiftInfo!=null) {
                            if (!workShiftInfo.getNumber().equals("0")){
                                if(StringUtils.isNotEmpty(workShiftInfo.getWorkStartTime())
                                && StringUtils.isNotEmpty(workShiftInfo.getClosingEndTime())) {
                                    DateTime startTime = new DateTime(sdf.parse(date + " " + workShiftInfo.getWorkStartTime()));
                                    DateTime endTime = new DateTime(sdf.parse(date + " " + workShiftInfo.getClosingEndTime()));

                                    if (startTime.compareTo(endTime) > 0) {
                                        //如果开始时间>结束时间，则结束时间加1天
                                        endTime = endTime.plusDays(1);
                                    }

                                    Map<String, DateTime> timeRangeMap = new HashMap<>();
                                    timeRangeMap.put("startTime", startTime);
                                    timeRangeMap.put("endTime", endTime);

                                    timeRangeList.add(timeRangeMap);

                                    prefix = workPersonScheduling.getId();
                                }
                            }
                        }
                    }
                }

                JSONObject checkOnCount = new JSONObject(rolePersonCheckOn.getCheckOnCount());

                for (Map<String,DateTime> map : timeRangeList) {
                    DateTime startTime = map.get("startTime");
                    DateTime endTime = map.get("endTime");

                    if (now.compareTo(startTime) >= 0 && now.compareTo(endTime) <= 0) {
                        //时间范围内

                        //获取进出门方向
                        long direction = sceneNameList.stream().filter(scene->scene.indexOf("进")!=-1).count();

                        String action = direction>0 ? "come" : "out";

                        int limit = checkOnCount.getInt(action);

                        //利用redis保存当日该设备的进出次数
                        String key = prefix + "_" + date + "_" + action;

                        Object countObj = valueOperations.get(key);

                        boolean allow = false;

                        if(countObj!=null){
                            int count = Integer.valueOf(countObj.toString());

                            if (count<limit){
                                allow  = true;
                            }
                        }
                        else{
                            allow = true;
                        }

                        if (allow){
                            //发送开门命令
//                        lapiService.remoteOpen(deviceInfo.getId());

                            //当日进出设备数自增
                            if(countObj==null){
                                valueOperations.set(key, 1, 1, TimeUnit.DAYS);
                            }
                            else{
                                valueOperations.increment(key);
                            }
                        }
                        else{
                            log.warn("{}当日进出已达上限!", matchPersonID);
                        }

                        //发送人机交互命令
                        int resultCode = allow ? 1 : 0; //0:不开门， 1:开门
                        String resultMsg = allow ? "允许通行" : "无权限";
                        String passTime = sdf1.format(recordTime);
                        String personName = matchPersonName;

                        lapiService.sendGUIShowInfo(deviceNo,personName,passTime,resultCode,resultMsg);

                        break;
                    }
                }
            }
            catch (Exception ex){
                log.error(ex.getMessage(),ex);
            }
        }
        else{
            //如果没有设置
            int resultCode = 1; //0:不开门， 1:开门
            String resultMsg = "允许通行";
            String passTime = sdf1.format(recordTime);
            String personName = matchPersonName;

            lapiService.sendGUIShowInfo(deviceNo,personName,passTime,resultCode,resultMsg);
        }
    }
}
