package com.yeejoin.amos.boot.module.hygf.biz.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.yeejoin.amos.boot.biz.common.utils.HttpUtils;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.module.hygf.api.dto.AccessTokenDto;
import com.yeejoin.amos.boot.module.hygf.api.dto.WechatQrCodeDTO;
import com.yeejoin.amos.boot.module.hygf.api.service.IWxService;
import com.yeejoin.amos.boot.module.hygf.api.util.HttpUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;

/**
 * 农户微信小程序
 *
 * @author Provence
 * @version v1.0
 * @date 2023/8/21 16:21
 */
@Slf4j
@Component
public class WxServiceImpl implements IWxService {

    private final String SMALL_PRO_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?";

    private final String SMALL_PRO_TOKEN = "wxToken";

    private final String SMALL_PRO_QRCODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?";

    @Autowired
    private RedisUtils redisUtil;

    private static final String grant_type = "client_credential";

    private static final String smallPrograName = "amos\\u5E73\\u53F0";
    /**
     * 公众号appid
     */

    private static final String publicAppId = "wxf6f295ce82aa4aab";
    /**
     * 公众号secret
     */

    private static final String publicSecret = "8df0d4c5968d0d65cba2a398eedfd1e8";
    @Value ("${hygfProgram.appid}")
    private String appId;

    @Value ("${hygfProgram.secret}")
    private String secret;

    public String getSessionKey(String code) {
        JSONObject jsonObject = getCode2Session(code);
        String sessionKey = null;
        if (jsonObject != null) {
            sessionKey = jsonObject.getString("session_key");
        }
        return sessionKey;
    }

    @Override
    public String getAccessToken() {
        // 坑，redis取出来的值 微信说失效了 现在解决方案 是改小失效时间
        String redisKey = SMALL_PRO_TOKEN + ":" + appId;
        if (redisUtil.hasKey(redisKey)) {
            return redisUtil.get(redisKey).toString();
        }
        AccessTokenDto accessToken = buildTokenParam(SMALL_PRO_TOKEN_URL, grant_type, appId, secret);
        if (accessToken.getErrcode() == null) {
            redisUtil.set(redisKey, accessToken.getAccess_token(), accessToken.getExpires_in() - 5);
        }
        return accessToken.getAccess_token();
    }

    @Override
    public String refreshAccessToken() {
        // 坑，redis取出来的值 微信说失效了 现在解决方案 是改小失效时间
        String redisKey = SMALL_PRO_TOKEN + ":" + appId;
        AccessTokenDto accessToken = buildTokenParam(SMALL_PRO_TOKEN_URL, grant_type, appId, secret);
        if (accessToken.getErrcode() == null) {
            redisUtil.set(redisKey, accessToken.getAccess_token(), accessToken.getExpires_in() - 5);
        }
        return accessToken.getAccess_token();
    }

    private AccessTokenDto buildTokenParam(String url, String grant_type, String appid, String secret) {
        StringBuffer buildUrl = new StringBuffer();
        buildUrl.append(url).append("grant_type=" + grant_type).append("&appid=" + appid).append("&secret=" + secret);
        try {
            log.info("获取access_token入参 => {}", buildUrl);
            String resultStr = HttpUtils.doGet(buildUrl.toString());
            log.info("获取access_token返回值 => {}", resultStr);
            AccessTokenDto accessToken = JSON.parseObject(resultStr, AccessTokenDto.class);
            if (accessToken.getErrcode() != null && 0 != accessToken.getErrcode()) {
                throw new RuntimeException(accessToken.getErrmsg());
            }
            return accessToken;
        } catch (Exception e) {
            log.error("获取access_token is error url:{}", buildUrl.toString());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * @param url
     * @param access_token 接口调用凭证
     * @param scene        最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，其它字符请自行编码为合法字符（因不支持%，中文无法使用
     *                     urlencode 处理，请使用其他编码方式）
     * @param page         必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index, 根路径前不要填加
     *                     /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
     * @param width        二维码的宽度，单位 px，最小 280px，最大 1280px
     * @param auto_color   自动配置线条颜色，如果颜色依然是黑色，则说明不建议配置主色调，默认 false
     * @param line_color   auto_color 为 false 时生效，使用 rgb 设置颜色 例如
     *                     {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
     * @param is_hyaline   是否需要透明底色，为 true 时，生成透明底色的小程序
     * @return
     */
    private WechatQrCodeDTO buildQrParam(String url, String access_token, String scene, String page, Long width,
                                  Boolean auto_color, JSONObject line_color, Boolean is_hyaline) {
        log.info("生成二维码入参, url=>{}, access_token=>{}, scene=>{}, page=>{},", url, access_token, scene, page);
        WechatQrCodeDTO smallProQrCodeVo = new WechatQrCodeDTO();
        StringBuffer buildUrl = new StringBuffer();
        buildUrl.append(url).append("access_token=" + access_token);
        JSONObject param = new JSONObject();
        param.put("scene", scene);
        param.put("page", page);
        param.put("width", width);
        param.put("auto_color", auto_color);
        param.put("line_color", line_color);
        param.put("is_hyaline", is_hyaline);
        try {
            RestTemplate rest = new RestTemplate();
            log.info("request wxQrCode start.....");
            ResponseEntity<Resource> entity = rest.postForEntity(buildUrl.toString(), param.toJSONString(),
                    Resource.class);
            log.info("get wxQrCode entity:{}", entity.toString());
            InputStream in = entity.getBody().getInputStream();
            smallProQrCodeVo.setBuffer(inputStream2byte(in));
        } catch (Exception e) {
            log.error("getSmallProQrCode is error url:{} param:{}", buildUrl.toString(), JSON.toJSON(param));
            throw new BadRequest(e.getMessage());
        }
        return smallProQrCodeVo;
    }

    /**
     * 功能描述:
     *
     * @param inputStream 输入流
     * @return byte[] 数组
     * @author sqy
     * @date 2019/3/28 16:03
     * @version 1.0
     */
    public static byte[] inputStream2byte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = inputStream.read(buff, 0, 100)) > 0) {
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public String getSmallProQrCode(String access_token, String scene, String page, Long width, Boolean auto_color, JSONObject line_color, Boolean is_hyaline) {
        WechatQrCodeDTO smallProQrCodeVo = buildQrParam(SMALL_PRO_QRCODE_URL, access_token, scene, page, width, auto_color,
                line_color, is_hyaline);
        // 小程序二维码
        byte[] buffer = smallProQrCodeVo.getBuffer();
        return JSON.toJSONString(buffer);
    }

    @Override
    public byte[] getSmallProQrCodeByte(String access_token, String scene, String page, Long width, Boolean auto_color, JSONObject line_color, Boolean is_hyaline) {
        WechatQrCodeDTO smallProQrCodeVo = buildQrParam(SMALL_PRO_QRCODE_URL, access_token, scene, page, width, auto_color,
                line_color, is_hyaline);
        // 小程序二维码 byte
        return smallProQrCodeVo.getBuffer();
    }

    @Override
    public String getSmallProQrCodeResponse(String access_token, String scene, String page, Long width, Boolean auto_color, JSONObject line_color, Boolean is_hyaline, HttpServletResponse response, String fileName, String fileType) {
        WechatQrCodeDTO smallProQrCodeVo = buildQrParam(SMALL_PRO_QRCODE_URL, access_token, scene, page, width,
                auto_color, line_color, is_hyaline);
        byte[] smallProQrCode = smallProQrCodeVo.getBuffer();
        response.setContentType("image/" + fileType);
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + "." + fileType);
        response.addHeader("Access-Control-Allow-Headers", "Content-Disposition");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");

        String smallProQrCodeStr = new String(smallProQrCode);
        if (StringUtils.isNotBlank(smallProQrCodeStr)) {
            try {
                JSONObject resultJson = JSONObject.parseObject(smallProQrCodeStr);
                log.info("生成二维码access_token错误, result => {}, 尝试重新获取access_token", resultJson);
                if (resultJson != null && "40001".equals(resultJson.getString("errcode"))) {
                    access_token = refreshAccessToken();
                    smallProQrCodeVo = buildQrParam(SMALL_PRO_QRCODE_URL, access_token, scene, page, width,
                            auto_color, line_color, is_hyaline);
                    smallProQrCode = smallProQrCodeVo.getBuffer();
                }
            } catch (JSONException e) {
                // 如果json解析异常则说明正常的获取到图片流
            }
        }
        String picStr = Base64.getEncoder().encodeToString(smallProQrCode);
        return  picStr;
        /*OutputStream os = null;
        try {
            os = response.getOutputStream();
            os.write(smallProQrCode);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) os.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null) os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
    }

    @Override
    public JSONObject getCode2Session(String code) {
        String url = buildOpenIdUrl(appId, secret, code);
        log.info("微信 code2Session, code =>{}, 请求 => {}", code, url);
        String resultStr = HttpUtil.sendHttpGet(url);
        // resultStr => {"session_key":"Mj5xbDhcZU73DtUduI1xKg==","openid":"oRraY5aYJkxkDJiG4rBaaw4MSmPA"}
        log.info("微信 code2Session, code =>{}, 结果 => {}", code, resultStr);
        JSONObject jsonObject = JSONObject.parseObject(resultStr);
        if (jsonObject == null || jsonObject.getIntValue("errcode") != 0) {
            throw new RuntimeException("微信授权失败, 请重新授权");
        }
        return jsonObject;
    }

    private String buildOpenIdUrl(String appId, String secret, String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code="
                + code + "&grant_type=authorization_code";
        return url;
    }

    @Data
    public static class ResponeVo {
        int code;
        CloseableHttpResponse response;
        String content;
        byte[] inStream;
        InputStream inputStream;
    }

    @Override
    public String getOpenId(String code) {
        JSONObject jsonObject = getCode2Session(code);
        String openId = null;
        if (jsonObject != null) {
            openId = jsonObject.getString("openid");
        }
        return openId;
    }

    @Override
    public String getPhoneNumber(String session_key, String encryptedData, String iv) {
        return null;
    }

    @Override
    public String getName() {
        return null;
    }
}
