package com.wteam.controller.hardware;

/**
 * @author doncic
 * @date 2023/9/27 19:13
 */

import cn.hutool.core.util.RandomUtil;
import com.wteam.framework.common.enums.OrderStatusEnum;
import com.wteam.framework.common.enums.ResultCode;
import com.wteam.framework.common.enums.ResultUtil;
import com.wteam.framework.common.enums.orderTypeEnum;
import com.wteam.framework.common.security.AuthUser;
import com.wteam.framework.common.security.context.UserContext;
import com.wteam.framework.common.vo.ResultMessage;
import com.wteam.framework.modules.book.entity.Card;
import com.wteam.framework.modules.book.entity.OrderInfo;
import com.wteam.framework.modules.book.entity.vo.TimeCodeVo;
import com.wteam.framework.modules.book.mapper.CardMapper;
import com.wteam.framework.modules.book.mapper.OrderInfoMapper;
import com.wteam.framework.modules.book.service.OrderInfoService;
import com.wteam.framework.modules.hardware.QrCodeRegistrationUtil;
import com.wteam.framework.modules.user.entity.User;
import com.wteam.framework.modules.user.mapper.UserMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Tag(name = "硬件相关接口管理")
@RestController
@RequestMapping("/v1/hardware")
public class HardwareController {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private OrderInfoService orderInfoService;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private CardMapper cardMapper;

    private static String lastRequestTime = null; // 用来记录上一次请求的时间
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static SimpleDateFormat sdfTimer = new SimpleDateFormat("HH:mm");

    /**
     * 这个时间判断是为了避免存在：因为扫码头会有多次扫到同一个码的情况
     *
     * @return
     */
    private static boolean isTimeElapsed() {
        String currentTime = sdf.format(new Date()); // 获取当前时间的字符串表示
        if (lastRequestTime == null) {
            // 如果是第一次请求，记录当前时间并返回true
            lastRequestTime = currentTime;
            return true;
        } else {
            try {
                Date currentDateTime = sdf.parse(currentTime);
                Date lastRequestDateTime = sdf.parse(lastRequestTime);
                long timeElapsed = currentDateTime.getTime() - lastRequestDateTime.getTime();
                return timeElapsed >= 5000; // 5秒等于5000毫秒
            } catch (Exception e) {
                e.printStackTrace();
                return true; // 如果出现异常，默认返回true以执行程序
            }
        }
    }


    /**
     * 这里理一下思路
     * 这里是反扫码设置，先前已经使用hutool接口进行第三方的url回调设置了
     * 根据传入的信息解析可以获得  扫码的时间、二维码信息、devmac指的是生命周期
     * 这里根据二维码信息可以获得对应id
     * 如果是订场订单生成的code ---》userId和orderId
     * 如果是计时订单生成的code ---》userId和orderId
     * 如果是月卡或者次卡生成的code ---》userId和有效天数
     * 根据orderId获取到对应的订单信息
     * 如果是计时订单
     * 先将时间存入starttime
     * 第二次进行时间校验 -》第二次的时间必须要是
     * 然后进行业务处理
     */
    @Operation(summary = "反扫码设备第三方二维码核销说明")
    @RequestMapping(value = "/verification", method = RequestMethod.GET)
    public ResultMessage hardware(@RequestParam(value = "data") String data) throws ParseException {
        log.info("此时回调的data数据为：{}", data);

        // 使用正则表示判断传入的回调数据是否正确
        // 定义正则表达式来匹配格式
        String pattern1 = "\\d{15}_pxo_\\d{8}_\\d{5}_\\d{9}_\\d{2}";

        if (!data.matches(pattern1)) {
            return ResultUtil.success(ResultCode.HARD_CALL_BACK_DATA_ERROR);
        }

        String orderId = null;

        // 获取当前时间
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(now); // 格式化当前时间为字符串
        log.info("此时扫码的时间为：{}", time);

        boolean timeElapsed = isTimeElapsed();
        log.info("此时的间隔是否大于5秒：{}", timeElapsed);

        if (!timeElapsed) {
            return ResultUtil.success(ResultCode.SAOMA_TIME_TAI_DUAN);
        }

        // 定义正则表达式来匹配数字
        String pattern2 = "\\d{9}"; // 匹配9位数字

        Pattern r = Pattern.compile(pattern2);
        Matcher m = r.matcher(data);

        String result = null;

        while (m.find()) {
            result = m.group(); // 获取匹配的子字符串
        }

        if (result != null) {
            // 将result的值赋给orderId
            orderId = result;
        } else {
            log.info("此时获得错误的orderId为：{}", result);
        }

        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        log.info("此时的订单信息为{}", orderInfo);

        //这里先判断是什么订单
        String orderType = orderInfo.getOrderType();

        //根据订场类型不同实现不同的扫码逻辑
        switch (orderType) {
            case "RENTAL":
                // 处理租场订单类型的二维码注销逻辑
                return RentalOrder(orderId, time);
            case "TICKET":
                // 处理门票订单类型的二维码注销逻辑
                return TicketOrder(orderId, time);
            case "TIMER":
                // 处理计时订单类型的二维码注销逻辑
                // 将时间转化为HH:mm 形式
                return TimerOrder(orderId, time);
            case "MONTHLY":
                // 处理月卡订单类型的二维码注销逻辑
                return MonthlyOrder(orderId, time);
            default:
                // 处理未知订单类型的情况
                return null;
        }
    }


    /**
     * 门票的扫码逻辑
     * 在二维码注册的时候就已经限制的门票只能核销两次
     *
     * @param orderId
     * @return
     */
    private ResultMessage TicketOrder(String orderId, String time) throws ParseException {
        //获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        //处理时间格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date currentTime = dateFormat.parse(time);

        //处理业务代码
        //先处理订场订单的情况
        if (orderTypeEnum.TICKET.toString().equals(orderInfo.getOrderType())) {

            if (orderInfo.getStartTime() == null) {

                // 第一次调用接口，设置startTime
                orderInfo.setStartTime(currentTime);
                log.info("使用次卡二维码第一次进入的时间为：{}", currentTime);
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.IN_USE));
                orderInfoMapper.updateById(orderInfo);

            } else {

                if (orderInfo.getEndTime() == null) {
                    // 第二次调用接口，设置endTime，并计算费用
                    orderInfo.setEndTime(currentTime);
                    log.info("使用次卡二维码出门的时间为：{}", currentTime);
                    orderInfo.setOrderState(String.valueOf(OrderStatusEnum.USED));
                    orderInfoMapper.updateById(orderInfo);

                }
            }
        }
        return ResultUtil.error(ResultCode.TICKET_HARD_ERROR);
    }

    /**
     * 月卡的核销业务逻辑处理
     * 月卡的二维码注册的时候有效期直到月卡失效
     * 先检测二维码是否存在
     * 再判断此时二维码是否失效
     * 只要未失效
     * 不限时间不限次数
     *
     * @param orderId
     * @param time
     * @return
     */
    private ResultMessage MonthlyOrder(String orderId, String time) throws ParseException {
        //获取order信息
        //获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //获得卡信息
        Integer cardId = orderInfo.getCardId();
        Card card = cardMapper.selectById(cardId);
        String endDate = card.getEndDate();
        // 假设时间格式为 "yyyy-MM-dd HH:mm:ss"
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date timeDate = sdf.parse(time);
        Date endDateDate = sdf.parse(endDate);
        //处理扫码业务
        if (timeDate.before(endDateDate)) {
            return ResultUtil.success();
        }
        return ResultUtil.error(ResultCode.MONTHLY_HARD_ERROR);
    }


    /**
     * 计时卡逻辑
     * 根据第一次传入的时间设置为用户进场时间
     * 根据第一次进场时间判断时间段，根据时间段的不同，设置订单的价格不同
     * 根据第二次传入的时间设置为用户离场时间
     * 根据第一次和第二次的差值计算价格
     */
    @SneakyThrows
    private ResultMessage TimerOrder(String orderId, String time) {
        //获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        // 处理时间格式
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
        // 获取当前日期和时间
        Date currentDate = new Date();
        String format = sdf1.format(currentDate);

        Date date = sdf.parse(time);
        log.info("此刻的传入的时间为：{}", date);

        // 设置不同时间段的价格
        Date startTime1 = sdf.parse(format + " 09:30:00");
        Date endTime1 = sdf.parse(format + " 18:00:00");
        Date startTime2 = sdf.parse(format + " 18:30:00");
        log.info("此时格式化的三个时间段为：{}，{}，{}", startTime1, endTime1, startTime2);

        //先处理订场订单的情况
        if (orderInfo.getStartTime() == null) {

            // 第一次调用接口，设置startTime
            orderInfo.setStartTime(date);
            orderInfo.setOrderState(String.valueOf(OrderStatusEnum.IN_USE));
            log.info("此时第一次调用接口设置的计时订单的进场时间为:{}", date);
            orderInfoMapper.updateById(orderInfo);

            if (date.after(startTime1) && date.before(endTime1)) {

                // 当前时间在 09:30 - 18:00 之间，价格为 8 元/小时
                double initialPrice = 8.0;
                orderInfo.setOrderPrice(initialPrice);
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.WAITING_TO_PAY));
                log.info("09:30 - 18:00这个时间段的订单信息为：{}", orderInfo);
                orderInfoService.updateById(orderInfo);

            } else if (date.after(startTime2)) {

                // 当前时间在 18:30 之后，价格为 10 元/小时
                double initialPrice = 10.0;
                orderInfo.setOrderPrice(initialPrice);
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.WAITING_TO_PAY));
                log.info("18 ：30 之后的这个时间段的订单信息为：{}", orderInfo);
                orderInfoService.updateById(orderInfo);

            }

        } else {

            if (orderInfo.getEndTime() == null) {

                // 第二次调用接口，设置endTime，并计算费用
                orderInfo.setEndTime(date);
                log.info("此时第二次调用接口设置的计时订单的进场时间为:{}", date);

                // 计算时间差（以分钟为单位）
                long timeDifferenceMillis = orderInfo.getEndTime().getTime() - orderInfo.getStartTime().getTime();
                long minutesDifference = TimeUnit.MILLISECONDS.toMinutes(timeDifferenceMillis);

                // 计算超出一分钟的时间
                long extraMinutes = Math.max(0, minutesDifference - 60);
                log.info("计时订单超出一分钟的时间为：{}", extraMinutes);

                // 考虑超时的情况
                if (extraMinutes > 0) {

                    // 计算超时分钟数
                    long l = extraMinutes % 60;

                    // 根据超出的分钟计算费用
                    // 8元/60分钟
                    double pricePerMinute = 8.0 / 60.0;
                    double totalPrice = pricePerMinute * l;
                    orderInfo.setOverPrice(totalPrice);
                    log.info("计时订单超时金额为：{}", totalPrice);
                    orderInfo.setOrderState(String.valueOf(OrderStatusEnum.WAITING_SUPPLEMENTS));

                    orderInfoService.updateById(orderInfo);
                }

                //如果没有超时 该计时订单完成
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.USED));
                log.info("使用完成的计时订单信息为：{}", orderInfo);
                orderInfoService.updateById(orderInfo);

            }
        }
        return ResultUtil.error(ResultCode.TIMER_HARD_ERROR);
    }


    /**
     * 租场订单实现逻辑
     * 先获取第一次时间设置为用户进场时间 第二次时间先判断是否在订场时间段之内 如果在就直接放行
     * 超过订场时间段，则计算超出订场时间
     *
     * @param orderId
     * @return
     */
    private ResultMessage RentalOrder(String orderId, String time) throws ParseException {
        //获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        log.info("传入的时间为：{}", time);

        //处理时间格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date currentTime = dateFormat.parse(time);
        log.info("此刻的时间为：{}", currentTime);


        //处理可能会出现提前扫码的情况
        // 1-->保证在订场开始时间的前30分钟扫码才有效
        // 获取原始时间
        Date orderSt = orderInfo.getOrderSt();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(orderSt);

        // 减去30分钟
        calendar.add(Calendar.MINUTE, -30);
        Date newTime = calendar.getTime();
        log.info("该订单开始时间减去30分钟之后的时间为：{}", newTime);

        // 判断时间
        if (currentTime.after(newTime)) {
            return ResultUtil.success(ResultCode.TIME_BU_DAO);
        }

        //先处理订场订单的情况
        if (orderTypeEnum.RENTAL.toString().equals(orderInfo.getOrderType())) {

            if (orderInfo.getStartTime() == null && orderInfo.getOrderState().equals(OrderStatusEnum.WAITING_TO_USE)) {

                // 第一次调用接口，设置startTime
                orderInfo.setStartTime(currentTime);
                log.info("使用租场二维码的进场时间为：{}", currentTime);
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.IN_USE));
                orderInfoMapper.updateById(orderInfo);

            } else {

                if (orderInfo.getEndTime() == null) {

                    // 第二次调用接口，设置endTime，并计算费用
                    orderInfo.setEndTime(currentTime);
                    log.info("使用租场二维码的出场时间为：{}", currentTime);
                    orderInfoMapper.updateById(orderInfo);

                    // 校验当前时间是否在ordered时间之前
                    if (currentTime.before(orderInfo.getOrderEd())) {

                        // 在ordered时间之前，返回状态码200
                        orderInfo.setOrderState(String.valueOf(OrderStatusEnum.USED));
                        orderInfoMapper.updateById(orderInfo);

                    } else {

                        // 超过ordered时间，计算超过的分钟数
                        long minutesDifference = calculateMinutesDifference(orderInfo.getOrderEd(), currentTime);

                        //  计算超时需要付钱金额
                        double v = calculateAmount(minutesDifference);

                        //获取订单支付金额
                        Double orderPrice = orderInfo.getOrderPrice();

                        //需要支付的金额
                        double v1 = v * orderPrice;
                        log.info("租场超时金额为：{}", v1);
                        orderInfo.setOverPrice(v1);
                        orderInfoService.updateById(orderInfo);

                    }
                }
            }
        }
        return ResultUtil.error(ResultCode.RENTAL_HARD_ERROR);
    }


    // 计算分钟差
    private long calculateMinutesDifference(Date startTime, Date endTime) {
        Duration duration = Duration.between((Temporal) startTime, (Temporal) endTime);
        return duration.toMinutes();
    }

    // 定义一个方法来根据时间差计算费用
    private double calculateAmount(long timeDifferenceMinutes) {
        // 实现计费计算逻辑
        // 根据时间差计算费用
        double billingAmount = 0.0;
        if (timeDifferenceMinutes >= 45) {
            billingAmount = 0.75; // 按小时费率的3/4
        } else if (timeDifferenceMinutes >= 30) {
            billingAmount = 0.5; // 按小时费率的1/2
        } else if (timeDifferenceMinutes >= 15) {
            billingAmount = 0.25; // 按小时费率的1/4
        }
        return billingAmount;
    }

//        // 查询是否存在相同的二维码订单信息
//        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//        lambdaQueryWrapper.eq(OrderInfo::getQrCode, qcode);
//        List<OrderInfo> orderInfos = orderInfoMapper.selectList(lambdaQueryWrapper);
//        boolean empty = orderInfos.isEmpty();
//        if (empty) {
//            orderInfo.setOrderState(String.valueOf(OrderStatusEnum.IN_USE));
//            orderInfoService.save(orderInfo);
//            return ResultUtil.success(ResultCode.QRCODE);
//        }
//        return ResultUtil.error(ResultCode.QRCODE_ERROR);


    /**
     * 计时卡实现思路
     * 传入token校验解析 获得userId
     * 查出昵称
     * 生成二维码-》计时码注册到硬件只能使用两次 -》调用硬件接口注册-》处理响应数据-》成功-》返回-》 二维码包含userID和手机号信息
     * 如果二维码注册成功，就生成计时订单
     * 用户拿着计时码去扫码进场
     * 一扫码就先支付一个小时的价钱
     * 然后出来的时候也获得一个时间
     * 根据时间比对
     * 如果小于 就直接开闸离开
     * 如果大于一个小时就继续进行扣费
     *
     * @param accessToken
     * @return
     */
    @Operation(summary = "计时卡")
    @PostMapping("/timeCard/{accessToken}")
    public ResultMessage timeCard(@PathVariable("accessToken") String accessToken) {
        //解析校验token获得用户id
        AuthUser authUser = UserContext.getAuthUser(accessToken);
        assert authUser != null;
        String userId = authUser.getId();
        log.info("此时的用户id为:{}", userId);
        //根据userId查询user信息
        User user = userMapper.selectById(userId);
        // 处理nickName为null的情况
        String nickName = (user.getNickName() != null) ? user.getNickName() : "";
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        // 格式化日期为字符串
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String formattedDate = currentDate.format(formatter);
        /**
         * 这里存储计时订单信息
         */
        OrderInfo orderInfo = new OrderInfo();
        String orderId = RandomUtil.randomNumbers(9);
        orderInfo.setId(orderId);
        orderInfo.setUserId(userId);
        orderInfo.setCreateTime(new Date());
        orderInfo.setStoreId(9376L);
        orderInfo.setOrderType(String.valueOf(orderTypeEnum.TIMER));
        orderInfoService.save(orderInfo);

        //生成一个随机数字
        String numbers = RandomUtil.randomNumbers(5);

        String data = "pxo_" + formattedDate + "_" + numbers + "_" + orderInfo.getId() + "_00";

        Boolean flag = QrCodeRegistrationUtil.registerTimerQrCode(data);

        if (flag) {
            orderInfo.setQrCode(data);
            orderInfoService.updateById(orderInfo);
            //判断响应结果
            TimeCodeVo timeCodeVo = new TimeCodeVo();
            timeCodeVo.setCode(data);
            timeCodeVo.setNickName(nickName);
            return ResultUtil.data(timeCodeVo);
        }
        return ResultUtil.error(ResultCode.TIME_CODE_ERROR);
    }
}
