package com.fs.ad.controller;

import com.fs.baidu.domain.BdApi;
import com.fs.baidu.service.IBdApiService;
import com.fs.baidu.utils.SignService;
import com.fs.huifuPay.sdk.opps.core.exception.BasePayException;
import com.fs.huifuPay.sdk.opps.core.utils.HttpClientUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
@RestController
@RequestMapping("/baiduBack")
public class MockAppController {
    private static final String APPID = "appId";
    private static final String AUTH_CODE = "authCode";
    private static final String USERID = "userId";
    private static final String TIMESTAMP = "timestamp";
    private static final String SIGNATURE = "signature";
    private static final String STATE = "state";
    private static final String ACCESSTOKEN_URL = "https://u.baidu.com/oauth/accessToken";

    @Autowired
    private IBdApiService bdApiService; // 开发者自定义
    @Autowired
    private SignService signService; // 开发者可自定义

    @GetMapping("/callback")
    public String mockApp(HttpServletRequest request) throws BasePayException {

        // 获取请求参数
        Map<String, String> params = new HashMap<>();
        this.fillParams(params, request);
        log.info("callback: params = {}", JSONObject.valueToString(params));
        int userIdInt = 0;
        try {
            userIdInt = Integer.parseInt(params.get(USERID));
        } catch (Exception e) {
            return this.getResponseJson(600011, "参数错误", new Object());
        }
        // 对签名内容进行判空
        if (StringUtils.isBlank(params.get(SIGNATURE))) {
            return this.getResponseJson(600011, "参数错误", new Object());
        }

        // 获取应用对应的密钥
        BdApi app = bdApiService.getAppByAppId(params.get(APPID));
        String sk = app.getAppSecretKey();
        // 开发者进行验签
        // 检查状态码
//        if (!this.checkState(params.get(APPID), app.getAppUserId(), params.get(STATE))) {
//            log.info("callback: state check fail");
//            return this.getResponseJson(600011, "状态码错误", new Object());
//        }

        // 签名验证
        if (!this.checkSignature(params, sk)) {
            log.info("callback: signature check fail");
            return this.getResponseJson(600011, "签名错误", new Object());
        }

        // 调用接口换取授权令牌
        String response = this.getAccessToken(params, sk, userIdInt);
        log.info("callback:getAccesstoken, response={}", response);
        String accessToken = null;
        String refreshToken = null;
        int uid;

        if (response.length() > 0) {
            JSONObject res = new JSONObject(response);
            int code = (int) res.get("code");
            if (code == 0) {
                JSONObject data = res.getJSONObject("data");
                accessToken = (String) data.get("accessToken");
                // 注释部分为 accessToken 其他信息，各应用开发者酌情使用即可
                 refreshToken = (String) data.get("refreshToken");
                String openId = data.getString("openId");
                int expiresIn = data.getInt("expiresIn");
                int refreshExpiresIn = data.getInt("refreshExpiresIn");
                app.setAccessToken(accessToken);
                app.setRefreshToken(refreshToken);
                app.setExpiresIn(expiresIn);
                app.setOpenId(openId);
                app.setRefreshExpiresIn(refreshExpiresIn);
                app.setAuthCode(params.get(AUTH_CODE));
                bdApiService.updateById(app);

                // TODO 相关信息落库处理
            } else {
                return this.getResponseJson(600011, "未获取到 access_token", new Object());
            }
        }
        Map<String, String> data = new HashMap<>();
        data.put("accessToken", accessToken);
        return this.getResponseJson(0, "success", data);
    }

    /**
     * 填充请求参数，开发者可用实体代替map
     *
     * @param params
     * @param request
     */
    private void fillParams(Map<String, String> params, HttpServletRequest request) {
        params.put(APPID, request.getParameter(APPID));
        params.put(AUTH_CODE, request.getParameter(AUTH_CODE));
        params.put(USERID, request.getParameter(USERID));
        params.put(TIMESTAMP, request.getParameter(TIMESTAMP));
        params.put(SIGNATURE, request.getParameter(SIGNATURE));
        params.put(STATE, request.getParameter(STATE));
    }

    /**
     * 校验状态码是否符合预期
     *
     * @param appId  应用ID
     * @param userId 创建应用的ID
     * @param state  请求参数中的状态码
     * @return
     */
    private boolean checkState(String appId, Long userId, String state) {
        // md5util 开发者自行开发即可
        String newState = md5(appId.concat("_").concat(String.valueOf(userId)));
        return newState.equals(state);
    }
    public static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 签名验证方法
     *
     * @param params
     * @param sk
     * @return
     */
    private boolean checkSignature(Map<String, String> params, String sk) {
        // 获取验签字符串:按key自然排序，拼接成 json 字符串，
        // 示例：str1 = {"appId": xxxxx, "authCode": xxx, "state": xxx,"timestamp": xxx}
        TreeMap<String, Object> map = new TreeMap<>();
        map.put(APPID, params.get(APPID));
        map.put(AUTH_CODE, params.get(AUTH_CODE));
        map.put(USERID, params.get(USERID));
        map.put(STATE, params.get(STATE));
        map.put(TIMESTAMP, params.get(TIMESTAMP));
        // 根据上述签名算法对接收到的参数签名
        String sign = signService.paramsSign(sk, map);
        log.info("callback: signature = {}", sign);
        // 判断签名和URL传参签名是否一致
        return params.get(SIGNATURE).equals(sign);
    }

    /**
     * 换取授权令牌
     *
     * @param params
     * @param sk
     * @param userIdInt 授权账户ID
     * @return
     */
    private String getAccessToken(Map<String, String> params, String sk, int userIdInt) throws BasePayException {
        Map<String, Object> requestMap = new HashMap<>();
        requestMap.put(APPID, params.get(APPID));
        requestMap.put("secretKey", sk);
        requestMap.put(AUTH_CODE, params.get(AUTH_CODE));
        requestMap.put("grantType", "access_token");
        requestMap.put("userId", userIdInt);

        String paramsJson = JSONObject.valueToString(requestMap);
        // 开发者自行增加异常判断
        return HttpClientUtils.httpPostJson(ACCESSTOKEN_URL, new HashMap<>(), paramsJson);
    }

    /**
     * 封装返回json串
     *
     * @param code
     * @param message
     * @param data
     * @return
     */
    private String getResponseJson(int code, String message, Object data) {
        // 封装返回字段的map
        Map<String, Object> responseMap = new HashMap<>();
        responseMap.put("code", code);
        responseMap.put("message", message);
        responseMap.put("data", data);
        return JSONObject.valueToString(responseMap);
    }
}