package com.sv.netty.service.impl;

import com.enums.FaceRecognizeEnum;
import com.sv.dto.EnterResult;
import com.sv.dto.api.MemberMessageDto;
import com.sv.entity.Device;
import com.sv.entity.Member;
import com.sv.entity.MemberEnterVenueLog;
import com.sv.entity.Venue;
import com.sv.entity.face.FaceRecognizeResponse;
import com.sv.netty.config.Constant;
import com.sv.netty.netty.*;
import com.sv.netty.service.MessageService;
import com.sv.netty.utils.JsonMapper;
import com.sv.service.api.MemberEnterVenueLogService;
import com.sv.service.api.MemberService;
import com.sv.service.api.VenueService;
import com.sv.service.oms.ConfigService;
import com.sv.service.oms.DeviceService;
import com.ydd.framework.core.common.dto.ResponseDTO;
import com.ydd.oms.entity.sys.Admin;
import com.ydd.oms.entity.sys.Config;
import io.netty.channel.Channel;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;


/**
 * 消息实现类
 *
 * @Author peakren
 * @Date 08/05/2017 10:43 PM
 */
@Service("messageService")
public class TcpMessageHandlerAdapter implements MessageService {

    private Logger logger = LoggerFactory.getLogger(TcpMessageHandlerAdapter.class);


    @Value("${nettym.url}")
    private String url;

    @Resource
    private MemberService memberService;
    @Resource
    private VenueService venueService;

    private Set<Channel> channels = new HashSet<>();

    @Resource
    private DeviceService deviceService;

    @Resource
    private ConfigService configService;

    private Channel enter;

    private Channel out;

    @Resource(name = "scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;

    @Resource
    private MemberEnterVenueLogService memberEnterVenueLogService;


    @Resource
    private RestTemplate restTemplate;

    public void sendMessage(MemberDto memberDto, Integer deviceId) {
        MessageDto messageDto = new MessageDto();
        messageDto.setCmdId(Cmd.FACEID.id);
        messageDto.setResult(memberDto);
        logger.info("发送消息" + channels.size() + "；deviceId:" + deviceId);
        if (deviceId == 1) {
            if (enter != null) {
                enter.writeAndFlush(messageDto);
            }
        } else {
            out.writeAndFlush(messageDto);
        }
    }

    public void sendOpenMessage(MessageDto messageDto, Integer deviceId) {
        logger.info("发送消息开门" + channels.size());
        if (enter != null) {
            enter.writeAndFlush(messageDto);
        }
    }

    /**
     * 接收设备发送过来的协议数据,业务处理后并返回
     *
     * @param channel netty的连接通道会话
     * @param content 消息内容
     */
    @Override
    public void receive(Channel channel, String content) {
        try {
            //解析数据
            Cmd cmd = resolveCmd(content);
            logger.info("收到消息" + cmd.text);
            switch (cmd) {
                case HB:
                    break;
                case FACEID_RESPONSE:
                    synchronized (this) {
                        resolve(channel, content);
                    }
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            logger.error("消息内容：" + content);
            logger.error("处理TCP消息异常", e);
        }
    }

    @Override
    public void online(Channel channel) {
        channels.add(channel);
    }


    public synchronized void resolve(Channel channel, String content) {
        BaseDto baseDto = JsonMapper.nonEmptyMapper().fromJson(content, BaseDto.class);
        FaceRecognizeResponse response = JsonMapper.nonNullMapper().fromJson(baseDto.getContent(), FaceRecognizeResponse.class);
        Device device = deviceService.findById(Integer.parseInt(baseDto.getDeviceId()));
        Venue venue = venueService.findById(device.getVenueId());
        if (FaceRecognizeEnum.RECOGNIZED.name.equals(response.getType())) {
            //识别成功
            if (response.getPerson() != null) {
                if (device.getId() == 1) {
                    //进门
                    logger.info("开门");
                    enter = channel;
                    enter(device, response, venue);
                } else if (device.getId() == 2) {
                    //出门
                    logger.info("出门");
                    out = channel;
                    out(device, response, venue);
                }
            }
        }
    }

    public void enter(Device device, FaceRecognizeResponse response, Venue venue) {
        try {
            int code = 1;
            Member member = memberService.findByFaceId(response.getPerson().getId());
            if (member != null) {
                //最后进场记录
                MemberEnterVenueLog enterVenueLog = memberEnterVenueLogService.findMemberLastLogNoType(member.getId(), device.getVenueId());
                if (enterVenueLog != null) {
                    //有记录 查看 最后一次是否是出场
                    if (enterVenueLog.getType() == 1) {
                        //是出场 不用限制
                    } else {
                        //是进场
                        Date date = new Date();
                        //本日连续进场次数
                        int continuityEnterCount = memberEnterVenueLogService.countEnterByDate(member.getId(), device.getVenueId(), date);
                        logger.info("连续进场次数:" + continuityEnterCount);
                        //距离上次入场差多少秒
                        int differenceSeconds = (int) (date.getTime() - enterVenueLog.getCreatedTime().getTime()) / (1000);
                        if (continuityEnterCount <= 0) {
                            //没有连续进场 间隔5分钟
                            Config config = configService.findById(1);
                            if (differenceSeconds <= (Integer.parseInt(config.getValue()) * 60)) {
                                //小于5分钟 不允许入场
                                code = -1;
                            }
                        } else if (continuityEnterCount == 1) {
                            //有过一次连续进场 间隔20分钟
                            Config config = configService.findById(2);
                            if (differenceSeconds <= (Integer.parseInt(config.getValue()) * 60)) {
                                //小于20分钟 不允许入场
                                code = -1;
                            }
                        } else {
                            //超过两次连续入场 入场失败
                            code = -1;
                        }
                    }
                }
                MemberDto memberMessageDto = new MemberDto();
                memberMessageDto.setName(member.getNickname());
                memberMessageDto.setAvatar(member.getAvatar());
                memberMessageDto.setMobile(member.getMobile());

                memberMessageDto.setPlaceName("");
                member = memberService.findByFaceId(response.getPerson().getId());
                memberMessageDto.setAmount(member.getMoney());
                if (code > 0) {
                    EnterResult result = venueService.enterVenue(response.getPerson().getId(), device.getId());
                    if (result.getStatus() >= 0) {
                        memberMessageDto.setCode(1);
                        if (result.getStatus() == 1) {
                            memberMessageDto.setCardName("会员卡");
                        }
                        if (result.getStatus() == 2) {
                            memberMessageDto.setPlacePrice(result.getMoney());
                            memberMessageDto.setFirst(true);
                            logger.info(member.getId() + "入场成功:" + member.getMoney().toString());
                            member = memberService.findByFaceId(response.getPerson().getId());
                            memberMessageDto.setAmount(member.getMoney());
                            //5秒后开门
                            scheduledExecutorService.schedule(new Runnable() {
                                @Override
                                public void run() {
                                    MessageDto messageDto = new MessageDto();
                                    messageDto.setCmdId(Cmd.OPEN_DOOR.id);
                                    messageDto.setDoor(1);
                                    sendOpenMessage(messageDto, 1);
                                }
                            }, 5, TimeUnit.SECONDS);
                        } else {
                            //开门
                            MessageDto messageDto = new MessageDto();
                            messageDto.setCmdId(Cmd.OPEN_DOOR.id);
                            messageDto.setDoor(1);
                            sendOpenMessage(messageDto, 1);
                            logger.info(member.getId() + "入场成功:" + member.getMoney().toString());

                        }
                        memberMessageDto.setMessage("门禁已开，请入门");
                        venueService.addNumber(venue.getId(), 1, member.getId());
                        venue = venueService.findById(venue.getId());
                        sendNumberChange(venue.getNumber());
                    } else {
                        logger.info(member.getId() + "入场失败:" + member.getMoney().toString());
                        memberMessageDto.setCode(-1);
                        memberMessageDto.setMessage(member.getName() + "您好，您的余额不足，请扫描门禁上张贴的小程序二维码充值");
                    }
                } else {
                    logger.info(member.getId() + "入场失败:连续入场");
                    memberMessageDto.setCode(0);
                    Config config = configService.findById(1);
                    memberMessageDto.setMessage("无出门记录连续入场，请" + config.getValue() + "分钟之后再试");
                }
                sendMessage(memberMessageDto, device.getId());

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void out(Device device, FaceRecognizeResponse response, Venue venue) {
        Member member = memberService.findByFaceId(response.getPerson().getId());
        if (member != null) {
            //出场 不用判断直接出
            MemberEnterVenueLog memberEnterVenueLog = new MemberEnterVenueLog();
            memberEnterVenueLog.setOrderSn("");
            memberEnterVenueLog.setType(1);
            memberEnterVenueLog.setMemberId(member.getId());
            memberEnterVenueLog.setVeneuType(device.getVenueType());
            memberEnterVenueLog.setVenueId(device.getVenueId());
            memberEnterVenueLog.setPlatformId(member.getPlatformId());
            memberEnterVenueLogService.save(memberEnterVenueLog);
            logger.info("用户" + member.getNickname() + "出场");
            MemberDto memberMessageDto = new MemberDto();
            memberMessageDto.setAmount(member.getMoney());
            memberMessageDto.setName(member.getNickname());
            memberMessageDto.setAvatar(member.getAvatar());
            memberMessageDto.setMobile(member.getMobile());
            memberMessageDto.setPlaceName("");
            memberMessageDto.setMessage("欢迎下次再来" + venue.getName());
            memberMessageDto.setCode(2);
            MessageDto messageDto = new MessageDto();
            messageDto.setCmdId(Cmd.OPEN_DOOR.id);
            messageDto.setDoor(2);
            sendOpenMessage(messageDto, device.getId());
            sendMessage(memberMessageDto, device.getId());
            venueService.addNumber(venue.getId(), -1, member.getId());
            venue = venueService.findById(venue.getId());
            sendNumberChange(venue.getNumber());
        }
    }

    public void sendNumberChange(Integer number) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("LingtekID", "5d14229fcb1f5c1a9046f429");
        param.add("Number", number.toString());
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(param, headers);
        ResponseEntity<Result> result = restTemplate.exchange("http://lingtek.jalasmart.com/api/v1/lingtek/number", HttpMethod.PUT, request, Result.class);
        logger.info("灯光结果" + JsonMapper.nonDefaultMapper().toJson(result));
    }


    @Override
    public void destory(Channel channel) {
        channels.remove(channel);
    }

    @Override
    public void doOffLine(Integer memberId, String nickname, String machineSn) {

    }

    @Override
    public void sendMessage(MemberDto memberDto) {

    }


    protected Cmd resolveCmd(String message) {
        BaseDto baseDto = JsonMapper.nonEmptyMapper().fromJson(message, BaseDto.class);
        return Cmd.getCmd(baseDto.getCmdId());
    }


}
