package com.wteam.framework.modules.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.hutool.core.text.CharSequenceUtil;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wteam.framework.common.cache.Cache;
import com.wteam.framework.common.cache.CachePrefix;
import com.wteam.framework.common.enums.*;
import com.wteam.framework.common.exception.ServiceException;
import com.wteam.framework.common.security.AuthUser;
import com.wteam.framework.common.security.context.UserContext;
import com.wteam.framework.common.security.enums.SecurityEnum;
import com.wteam.framework.common.security.enums.UserEnums;
import com.wteam.framework.common.security.token.Token;
import com.wteam.framework.common.security.token.TokenUtils;
import com.wteam.framework.common.utils.AesCbcUtil;
import com.wteam.framework.common.utils.BeanUtil;
import com.wteam.framework.common.vo.ResultMessage;
import com.wteam.framework.modules.book.entity.Card;
import com.wteam.framework.modules.book.entity.CouponUser;
import com.wteam.framework.modules.book.entity.OrderInfo;
import com.wteam.framework.modules.book.entity.Venue;
import com.wteam.framework.modules.book.entity.vo.OrderInfoVo;
import com.wteam.framework.modules.book.entity.vo.OrderTicketVo;
import com.wteam.framework.modules.book.entity.vo.VenueVo;
import com.wteam.framework.modules.book.mapper.CardMapper;
import com.wteam.framework.modules.book.mapper.CouponUserMapper;
import com.wteam.framework.modules.book.mapper.OrderInfoMapper;
import com.wteam.framework.modules.book.mapper.VenueMapper;
import com.wteam.framework.modules.book.service.OrderInfoService;
import com.wteam.framework.modules.user.entity.User;
import com.wteam.framework.modules.user.entity.dto.WechatMPLoginParams;
import com.wteam.framework.modules.user.mapper.UserMapper;
import com.wteam.framework.modules.user.service.UserService;
import com.wteam.framework.modules.user.token.UserTokenGenerate;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.wteam.framework.common.enums.orderTypeEnum.TICKET;
import static com.wteam.framework.common.utils.DateRuoYiUtils.parseDate;

/**
 * (User)表服务实现类
 *
 * @author doncic
 * @since 2023-09-14 19:28:44
 */
@Service("UserService")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private Cache cache;
    @Autowired
    private UserTokenGenerate UserTokenGenerate;
    @Autowired
    private UserService userService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private CardMapper cardMapper;
    @Autowired
    private CouponUserMapper couponUserMapper;
    @Autowired
    private VenueMapper venueMapper;
    @Autowired
    private OrderInfoService orderInfoService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> miniProgramLogin(WechatMPLoginParams params) {
        //获取相关信息
        String encryptedData = params.getEncryptedData();
        String iv = params.getIv();
        String code = params.getCode();
        //日志打印一下配置信息
        log.info(JSONUtil.toJsonStr(params));
        //校验前端调微信官方登录接口是否成功 是否拿到code
        if (StringUtils.isBlank(code)) {
            throw new ServiceException(ResultCode.WECHAT_CODE_NOT_EXIST);
        }
        try {
            //获取微信登录相关配置
            Map<String, String> map = new HashMap<>(2);
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
            String sessionKey = session.getSessionKey();
            log.info("此时解析出的key为：{}", sessionKey);
            String openId = session.getOpenid();
            log.info("此时解析出的openid为：{}", openId);
            map.put("sessionKey", sessionKey);
            //todo 在这里可以调节时间以方便进行登录状态下调试
            cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + openId, map, 60 * 60 * 24L);
            //比对openId信息
            User user = this.query().eq("open_id", openId).one();
            //先前没有登录过
            Map<String, Object> result = new HashMap<>(3);
            result.put("openId", openId);
            if (!ObjectUtil.isNull(user)) {
                //-----------------------
                result.put("user", user);
            }
            if (ObjectUtil.isNull(user)) {
                // 解密用户信息
                //FIXME maybe NPE报错
//                WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
//                log.info("test" + userInfo.toString());
//                String jsonStr = WeChatUtil.getUserInfo(encryptedData, sessionKey, iv);
//                // 使用 hutool 解析 JSON 数据
//                JSONObject jsonObject = new JSONObject(jsonStr);
//
//                // 获取特定字段的值
//                String openIds = jsonObject.getStr("openId");
//                String unionId = jsonObject.getStr("unionId");
//                String nickName = jsonObject.getStr("nickName");
//                int gender = jsonObject.getInt("gender");
//                String language = jsonObject.getStr("language");
//                String city = jsonObject.getStr("city");
//                String province = jsonObject.getStr("province");
//                String country = jsonObject.getStr("country");
//                String avatarUrl = jsonObject.getStr("avatarUrl");
//                WxMaUserInfo userInfo = this.wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
//                log.info("此时的userinfo为{}", userInfo.toString());

                String jsonStr = decryptUserInfo(sessionKey, encryptedData, iv);
                log.info("此时解析出来的json数据为：{}", jsonStr);

                // 使用Hutool解析JSON字符串
                JSONObject jsonObject = JSONUtil.parseObj(jsonStr);

                // 获取phoneNumber字段的值
                String phoneNumber = jsonObject.getStr("phoneNumber");
                log.info("此时的电话为：{}", phoneNumber);
                User newUser = new User();
                newUser.setOpenId(openId);
//                log.info("此时的昵称为：{}", userInfo.getNickName());
//                newUser.setNickName(userInfo.getNickName());
//                newUser.setAvatar(userInfo.getAvatarUrl());
//                newUser.setSex(gender);
//                newUser.setNickName(nickName);
                newUser.setPhoneNumber(phoneNumber);
                /**
                 * 这里可以增加邀请码功能
                 */
//                log.info("邀请码{}", params.getInvitedCode());
//                if (null != params.getInvitedCode()) {
//                    //设置邀请用户。增加邀请人数，发放优惠券
//                    sentInvited(params.getInvitedCode(), newUser);
//                }
                userService.save(newUser);
//                this.save(newUser);
                //清理ThreadLocal
                WxMaConfigHolder.remove();
                Token token = UserTokenGenerate.createToken(newUser, true);
                result.put("token", token);
                //---------------------
                result.put("user", newUser);
                return result;
            }
            Token token = UserTokenGenerate.createToken(user, true);
            result.put("token", token);
            //把生成的Token返回给前端
            return result;
        } catch (WxErrorException e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(ResultCode.WECHAT_LOGIN_ERROR);
        } finally {
            //清理ThreadLocal
            WxMaConfigHolder.remove();
        }
    }

    public static String decryptUserInfo(String sessionKey, String encryptedData, String iv) {
        try {
            byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
            byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
            byte[] ivBytes = Base64.getDecoder().decode(iv);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] decrypted = cipher.doFinal(encryptedDataBytes);

            return new String(decrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    @Override
    public User getUserInfo() {
        AuthUser tokenUser = UserContext.getCurrentUser();
        if (tokenUser != null) {
            return this.findById(tokenUser.getId());
        }
        throw new ServiceException(ResultCode.USER_NOT_LOGIN);
    }

    private User findById(String id) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return this.baseMapper.selectOne(queryWrapper);
    }

    @Override
    public void getPhoneInfo(WechatMPLoginParams params) {
        // 对加密信息进行解密
        try {
            Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getOpenId());
            Map<String, String> map = (Map<String, String>) cacheData;
            log.info(map.get("sessionKey"));
            WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(map.get("sessionKey"), params.getEncryptedData(), params.getIv());
            log.info(phoneNoInfo.toString());
            //存入当前用户
            //这里有两个获取手机号码的方法 到时候日志打印出来看看
//            String phoneNumber1 = phoneNoInfo.getPhoneNumber();
            String phoneNumber = phoneNoInfo.getPurePhoneNumber();
            this.update(new UpdateWrapper<User>().eq("open_id", params.getOpenId()).set("phone_number", phoneNumber));
            //清理ThreadLocal
            WxMaConfigHolder.remove();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw new ServiceException(ResultCode.USER_PHONE_NOT_EXIST);
        }
    }

    @Override
    public void logout(UserEnums userEnums) {
        String currentUserToken = UserContext.getCurrentUserToken();
        if (CharSequenceUtil.isNotEmpty(currentUserToken)) {
            cache.remove(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + currentUserToken);
        }
    }

    @Override
    public ResultMessage showOrderType(String accessToken, orderTypeEnum orderTypeEnum) {
        // 根据传入的token获取用户信息
        AuthUser authUser = UserContext.getAuthUser(accessToken);
        if (authUser == null) {
            return ResultUtil.error(ResultCode.USER_NOT_FOUND);
        }
        Long userId = Long.valueOf(authUser.getId());
        //构造查询条件
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getUserId, userId).eq(OrderInfo::getOrderType, orderTypeEnum);
        // 获取用户的订单信息
        List<OrderInfo> orderInfos = orderInfoMapper.selectList(queryWrapper);
        //校验
        if (orderInfos.isEmpty()) {
            return ResultUtil.success();
        }
        // 返回成功结果
        return ResultUtil.data(orderInfos);
    }

    @Override
    public ResultMessage showMyCard(Long id, CardTypeEnum cardTypeEnum) {
        //构造查询条件
        LambdaQueryWrapper<Card> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Card::getUserId, id).eq(Card::getCardType, cardTypeEnum);
        // 获取用户的该类型卡的信息
        List<Card> cardsInfos = cardMapper.selectList(queryWrapper);
        //校验
        if (cardsInfos.isEmpty()) {
            return ResultUtil.success();
        }
        // 返回成功结果
        return ResultUtil.data(cardsInfos);
    }

    @Override
    public ResultMessage showMyCoupon(@NotNull String accessToken) {
        // 根据传入的token获取用户信息
        AuthUser authUser = UserContext.getAuthUser(accessToken);
        if (authUser == null) {
            return ResultUtil.error(ResultCode.USER_NOT_FOUND);
        }
        Long userId = Long.valueOf(authUser.getId());
        //查询信息
        CouponUser couponUser = couponUserMapper.selectById(userId);
        return ResultUtil.data(couponUser);
    }

    @Override
    public ResultMessage showOrder(@NotNull String id) {
        OrderInfo orderInfo = orderInfoMapper.selectById(id);
        //先判断订单状态
        if (orderInfo == null) {
            return ResultUtil.error(ResultCode.ORDER_STATE_ERROR);
        }
        log.info("此时获得的订单信息为:{}", orderInfo);
        String orderType = orderInfo.getOrderType();
        String qrCode = orderInfo.getQrCode();
        //如果是次卡订单或者月卡订单,就直接弹出二维码返回即可
        if ("TICKET".equals(orderType) || "MONTHLY".equals(orderType)) {
            return ResultUtil.data(qrCode);
        }
        Long venueId = orderInfo.getVenueId();
        log.info("此时获得的场地id为:{}", venueId);
        Venue venue = venueMapper.selectById(venueId);
        String venueName = venue.getVenueName();
        String venueType = venue.getVenueType();
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        BeanUtil.copyProperties(orderInfo, orderInfoVo);
        orderInfoVo.setVenueName(venueName);
        orderInfoVo.setVenueType(venueType);
        if (ObjectUtil.isNull(orderInfoVo)) {
            return ResultUtil.success();
        }
        return ResultUtil.data(orderInfoVo);
    }


    @Override
    public ResultMessage showOrderByType(@NotNull String id, OrderTypeEnums orderTypeEnum) {
        //先获取到这个用户的所有订单
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OrderInfo::getOrderType, orderTypeEnum)
                .eq(OrderInfo::getUserId, id);
        List<OrderInfo> orderInfos = orderInfoMapper.selectList(lambdaQueryWrapper);
        List<OrderInfo> collect = orderInfos.stream()
                .filter(orderInfo -> orderInfo.getOrderState() != null)
                .filter(orderInfo -> orderInfo.getQrCode() != null)
                .collect(Collectors.toList());
        log.info("过滤之后的订单信息为:{}", collect);

        List<OrderInfo> orderInfoList = new ArrayList<>();
        // 获取当前时间
        Date currentTime = new Date();
        for (OrderInfo orderInfo : collect) {
            // 自定义方法，将订单结束时间字符串解析为Date对象
            Date orderEndTime = parseDate(orderInfo.getOrderEd());
            // 假设订单已经过期的阈值为5分钟
            long threshold = 5 * 60 * 1000;
            if (orderEndTime != null && orderEndTime.getTime() + threshold < currentTime.getTime()) {
                // 订单已经过期，将订单状态设置为已经过期
                orderInfo.setOrderState(String.valueOf(OrderStatusEnum.EXPIRED));
                orderInfoList.add(orderInfo);
                // 更新订单状态到数据库（具体的数据库更新操作需要根据您的项目需求来实现）
                orderInfoService.updateById(orderInfo);
            }
            orderInfoList.add(orderInfo);
        }

        List<OrderInfoVo> orderInfoVos = new ArrayList<>();
        List<OrderTicketVo> order = new ArrayList<>();
        //增强for循环遍历
        for (OrderInfo orderInfo : orderInfoList) {
            String orderType = orderInfo.getOrderType();
            log.info("此时的订单类型为:{}", orderType);
            if ("TICKET".equals(orderType)) {
                log.info("此时的orderInfo信息为:{}", orderInfo);
                Integer cardId = orderInfo.getCardId();
                log.info("此时的卡id为为:{}", cardId);
                OrderTicketVo orderTicketVo = new OrderTicketVo();
                Card card = cardMapper.selectById(cardId);
                log.info("此时的卡为:{}", card);
                Integer days = card.getDays();
                if (days != null && days == 0) {
                    return ResultUtil.success(ResultCode.CARD_DAYS_IS_NULl);
                }
                String endDate = card.getEndDate();
                String startDate = card.getStartDate();
                BeanUtil.copyProperties(orderInfo, orderTicketVo);
                orderTicketVo.setDays(days);
                orderTicketVo.setEndDate(endDate);
                orderTicketVo.setStartDate(startDate);
                order.add(orderTicketVo);
                return ResultUtil.data(order);
            }
            OrderInfoVo orderInfoVo = new OrderInfoVo();
            Long venueId = orderInfo.getVenueId();
            log.info("此时的场地id为:{}", venueId);
            if (venueId == null) {
                return ResultUtil.data(null);
            }
            Venue venue = venueMapper.selectById(venueId);
            log.info("此时的场地为:{}", venue);
            String venueName = venue.getVenueName();
            log.info("此时的场地名称为:{}", venueName);
            String venueType = venue.getVenueType();
            log.info("此时的场地类型为:{}", venueType);
            BeanUtil.copyProperties(orderInfo, orderInfoVo);
            orderInfoVo.setVenueType(venueType);
            orderInfoVo.setVenueName(venueName);
            log.info("此时的返回数据为:{}", orderInfoVo);
            orderInfoVos.add(orderInfoVo);
        }
        if (ObjectUtil.isNull(orderInfoVos)) {
            return ResultUtil.success();
        }
        return ResultUtil.data(orderInfoVos);
    }


    @Override
    public ResultMessage exit(HttpServletRequest request) {
        //获取token
        String accessToken = request.getHeader(SecurityEnum.HEADER_TOKEN.getValue());
        //移除redis的token
        Boolean remove = cache.remove(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.USER) + accessToken);
        return Boolean.TRUE.equals(remove) ? ResultUtil.success() : ResultUtil.error(ResultCode.ERROR);
    }
}



