package beilv.applogin.service.impl;

import beilv.applogin.domain.vo.AppAuthLoginRespVO;
import beilv.applogin.domain.vo.AppAuthWeixinMiniAppLoginReqVO;
import beilv.applogin.service.IAppAuthService;
import beilv.common.enums.LoginTypeEnum;
import beilv.common.utils.rsa.RsaUtil;
import beilv.system.domain.SysMember;
import beilv.system.mapper.SysMemberMapper;
import beilv.vipCard.mapper.VipCardMapper;
import cn.binarywang.wx.miniapp.api.WxMaService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;


/**
 * 会员的认证 Service 接口
 *
 * @author yshop
 */
@Service
@Slf4j
public class AppAuthServiceImpl implements IAppAuthService {

    @Autowired
    private SysMemberMapper sysMemberMapper;
    @Value("${rsa.publicKey}")
    private String publicKey;
    @Value("${wx.pay.appId}")
    private String appId;
    @Value("${wx.pay.appSecret}")
    private String appSecret;

    @Resource
    private VipCardMapper vipCardMapper;
    @Override
    public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) throws Exception {
        SysMember memberUserDO;
        JSONObject responseMap = getSessionKeyOropenid(reqVO.getLoginCode());
        String openId = (String) responseMap.get("openid");
        String sessionKey = (String) responseMap.get("session_key");
        //获取用户信息
        SysMember sysMember = getUserInfo(sessionKey, reqVO.getEncryptedData(), reqVO.getIv());
        // 用户已经存在
        memberUserDO = sysMemberMapper.selectByMobile(sysMember.getOpenId());
        if (memberUserDO == null) {
            // 获得获得注册用户
            sysMemberMapper.createUserIfAbsent(sysMember);
            memberUserDO = sysMemberMapper.selectSysMemberById(sysMember.getId());
        }
        //加密
        String accessToken = RsaUtil.encrypt(memberUserDO.getId().toString(), publicKey);
        AppAuthLoginRespVO userInfo = new AppAuthLoginRespVO();
        userInfo.setAccessToken(accessToken);
        userInfo.setOpenId(openId);
        userInfo.setSessionKey(sessionKey);
        userInfo.setSysMember(memberUserDO);
        userInfo.setVipCard(vipCardMapper.selectVipCardByUserId(String.valueOf(memberUserDO.getId())));
        return userInfo;
    }

    /**
     * 获取用户信息
     *
     * @param sessionKey    sessionKey
     * @param encryptedData encryptedData
     * @param iv            iv
     * @return 手机号
     */
    public SysMember getUserInfo(String sessionKey, String encryptedData, String iv) {
        SysMember sysMember = new SysMember();
        Date date = new Date();
        try {
            // 1. 解密用户信息
            byte[] result = decrypt(encryptedData, sessionKey, iv);
            JSONObject json = JSONObject.parseObject(new String(result));
            // 2. 处理解密后的手机号信息
            sysMember.setOpenId(json.get("openId").toString());
            sysMember.setUsername(json.get("nickName").toString() + "_" + json.get("openId").toString());
            sysMember.setIntegral(BigDecimal.ZERO);
            sysMember.setCreateTime(date);
            return sysMember;
        } catch (Exception e) {
            e.printStackTrace();
            return null; // 处理异常
        }
    }

    /**
     * 获取用户手机号
     *
     * @param telephoneCode telephoneCode
     * @return 手机号
     */
    public String getUserPhone(String telephoneCode) {
        String accessToken = getAccessToken();

        try {
            // 构建 URL
            String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
            URL obj = new URL(url);
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();
            // 设置请求方法为 POST
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/json");
            // 发送 POST 请求
            con.setDoOutput(true);
            String jsonInputString = "{\"code\":\"" + telephoneCode + "\"}";
            try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
                wr.writeBytes(jsonInputString);
                wr.flush();
            }
            // 获取响应
            int responseCode = con.getResponseCode();
            BufferedReader in;
            if (responseCode == HttpURLConnection.HTTP_OK) {
                in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            } else {
                in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
            }
            String inputLine;
            StringBuilder response = new StringBuilder();
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
            JSONObject json = JSONObject.parseObject(response.toString());
            if (json.get("errcode").equals(0)) {
                JSONObject phoneObject = JSONObject.parseObject(json.get("phone_info").toString());
                return phoneObject.get("phoneNumber").toString();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null; // 处理异常
        }
    }


    /**
     * 获取微信小程序 session_key 和 openid
     * 用户端
     *
     * @param code 调用微信登陆返回的Code
     * @return
     * @author zhuping
     */
    public JSONObject getSessionKeyOropenid(String code) {
        //微信端登录code值
        String wxCode = code;
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";    //请求地址
        Map<String, String> requestUrlParam = new HashMap<String, String>();
        requestUrlParam.put("appid", appId);    //开发者设置中的appId
        requestUrlParam.put("secret", appSecret);    //开发者设置中的appSecret
        requestUrlParam.put("js_code", wxCode);    //小程序调用wx.login返回的code
        requestUrlParam.put("grant_type", "authorization_code");    //默认参数
        //发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
        JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam, null));
        return jsonObject;
    }


    /**
     * 获取微信小程序access_token
     * 用户端
     *
     * @return
     * @author zhuping
     */
    public String getAccessToken() {
        //微信端登录code值
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/token";    //请求地址
        Map<String, String> requestUrlParam = new HashMap<String, String>();
        requestUrlParam.put("appid", appId);    //开发者设置中的appId
        requestUrlParam.put("secret", appSecret);    //开发者设置中的appSecret
        requestUrlParam.put("grant_type", "client_credential");    //小程序调用wx.login返回的code
        //发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
        JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam, null));
        System.out.println("----------:" + jsonObject.getString("access_token"));
        return jsonObject.getString("access_token");
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, Map<String, ?> paramMap, String headerToken) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        String param = "";
        Iterator<String> it = paramMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            param += key + "=" + paramMap.get(key) + "&";
        }
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            if (headerToken != null) {
                conn.setRequestProperty("access_token", headerToken);
            }
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            //log.error(e.getMessage(), e);
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return result;
    }


    /**
     * 解密用户信息
     */
    private byte[] decrypt(String encryptedData, String sessionKey, String iv) throws Exception {
        // Base64解码
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
        byte[] ivByte = Base64.getDecoder().decode(iv);

        // 如果密钥不足16位，就补足为16位
        byte[] key = new byte[16];
        System.arraycopy(keyByte, 0, key, 0, Math.min(keyByte.length, key.length));

        // 解密
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivByte);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(dataByte);
    }

}
