package com.xinelu.common.utils;

import com.alibaba.fastjson2.JSON;
import com.xinelu.common.config.AppletChatConfig;
import com.xinelu.common.config.XinELuConfig;
import com.xinelu.common.constant.Constants;
import com.xinelu.common.entity.AppletAccessToken;
import com.xinelu.common.entity.AppletLoginVO;
import com.xinelu.common.entity.AppletPhoneVO;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description 微信小程序工具类
 * @Author 纪寒
 * @Date 2022-08-17 16:11:10
 * @Version 1.0
 */
@Slf4j
@Component
public class AppletChatUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private AppletChatConfig appletChatConfig;

    /**
     * 返回成功状态码
     */
    private static final int SUCCESS_CODE = 0;

    /**
     * 小程序登录url
     */
    private static final String APPLET_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";

    /**
     * 小程序accessToken的url
     */
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";

    /**
     * 小程序获取用户手机号的url
     */
    private static final String PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";

    /**
     * 获取小程序二维码接口地址
     */
    private static final String GET_APPLET_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";

    /**
     * JSON数据格式标识
     */
    private static final String JSON_DATA_FORMAT = "Content-Type: application/json; encoding=utf-8";

    /**
     * 生成小程序二维码图片失败标识
     */
    private static final String FAIL = "FAIL";

    /**
     * 根据登录编码获取微信用户信息
     *
     * @param appletId  小程序id
     * @param secret    小程序秘钥
     * @param code      登录凭证码
     * @param grantType 授权类型
     * @return 登录信息
     */
    public static AppletLoginVO getAppletLoginInfo(String appletId, String secret, String code, String grantType) {
        //请求地址
        String appletLoginUrl = APPLET_LOGIN_URL
                + "?appid=" + appletId
                + "&secret=" + secret
                + "&js_code=" + code
                + "&grant_type=" + grantType;
        //发送请求
        String result = HttpUtils.sendGet(appletLoginUrl);
        if (StringUtils.isBlank(result)) {
            throw new ServiceException("获取微信小程序用户信息失败", 201);
        }
        return JSON.parseObject(result, AppletLoginVO.class);
    }

    /**
     * 获取微信小程序的accessToken
     *
     * @param appletId 小程序id
     * @param secret   小程序秘钥
     * @return accessToken信息
     */
    public static AppletAccessToken getAppletAccessToken(String appletId, String secret) {
        //请求路径
        String accessTokenUrl = ACCESS_TOKEN_URL + "&appid=" + appletId + "&secret=" + secret;
        //发送请求
        String result = HttpUtils.sendGet(accessTokenUrl);
        if (StringUtils.isBlank(result)) {
            throw new ServiceException("获取微信小程序accessToken信息失败", 201);
        }
        return JSON.parseObject(result, AppletAccessToken.class);
    }

    /**
     * 获取微信小程序的手机号码
     *
     * @param code        登录凭证
     * @param accessToken 小程序accessToken
     * @return 手机信息
     */
    public static AppletPhoneVO getAppletPhoneInfo(String code, String accessToken) {
        //请求地址
        String phoneUrl = PHONE_NUMBER_URL + accessToken;
        //请求参数
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("code", code);
        String param = JSON.toJSONString(paramMap);
        //发送POST请求
        String result = HttpUtils.sendPostJson(phoneUrl, param);
        if (StringUtils.isBlank(result)) {
            throw new ServiceException("获取微信小程序手机号失败", 201);
        }
        return JSON.parseObject(result, AppletPhoneVO.class);
    }


    /**
     * 递归获取小程序二维码信息
     *
     * @param weChatCodeId          输入id
     * @param failFlag              调用失败标识
     * @param fileName              文件名称
     * @param appletPageUrl         二维码跳转路径
     * @param filePathWeChatCodeUrl 文件保存路径
     * @return java.lang.String 二维码地址
     */
    public String createAppletCode(Long weChatCodeId, boolean failFlag, String fileName, String appletPageUrl, String filePathWeChatCodeUrl) {
        if (BooleanUtils.isTrue(failFlag)) {
            //删除原有Redis中的key值
            String accessTokenKey = Constants.NURSE_STATION_APPLET_ACCESS_TOKEN + "accessToken";
            Object object = redisTemplate.opsForValue().get(accessTokenKey);
            if (Objects.nonNull(object)) {
                redisTemplate.delete(accessTokenKey);
            }
        }
        //删除原有Redis中缓存的AccessToken
        String appletAccessToken = getAppletAccessToken();
        if (StringUtils.isBlank(appletAccessToken)) {
            throw new ServiceException("获取小程序凭证信息失败，请联系管理员！");
        }
        Map<String, Object> params = new HashMap<>();
        params.put("scene", weChatCodeId);
        params.put("page", appletPageUrl);
        String body = JSON.toJSONString(params);
        String filePath = XinELuConfig.getProfile() + filePathWeChatCodeUrl + "/" + weChatCodeId;
        //调用微信接口获取小程序二维码并将其上传到服务器中
        return getAppletCodePicture(appletAccessToken, body, filePath, fileName);
    }

    /**
     * 根据小程序AccessToken获取小程序的二维码图片
     *
     * @param accessToken 微信小程序的accessToken
     * @param body        接口传输参数
     * @param filePath    文件路径
     * @param fileName    文件名称
     * @return 小程序的二维码图片
     */
    public static String getAppletCodePicture(String accessToken, String body, String filePath, String fileName) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(GET_APPLET_CODE_URL + accessToken);
        try {
            StringEntity entity = new StringEntity(body, "UTF-8");
            entity.setContentType("image/png");
            httpPost.setEntity(entity);
            HttpResponse response = httpClient.execute(httpPost);
            String contentType = response.getEntity().getContentType().toString();
            if (StringUtils.equals(contentType, JSON_DATA_FORMAT)) {
                return FAIL;
            }
            String absolutePath = getAbsoluteFile(filePath, fileName).getAbsolutePath();
            try (InputStream inputStream = response.getEntity().getContent(); FileOutputStream out = new FileOutputStream(absolutePath)) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                out.flush();
            }
            return absolutePath;
        } catch (Exception e) {
            log.error("获取小程序二维码接口调用失败，失败原因为：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 创建文件路径
     *
     * @param uploadDir 路径名称
     * @param fileName  文件名称
     * @return File 文件目录
     */
    public static File getAbsoluteFile(String uploadDir, String fileName) {
        File file = new File(uploadDir + File.separator + fileName);
        if (!file.exists()) {
            if (!file.getParentFile().exists()) {
                boolean mkdirs = file.getParentFile().mkdirs();
                if (BooleanUtils.isFalse(mkdirs)) {
                    throw new ServiceException("文件路径创建失败，请联系管理员！");
                }
            }
        }
        return file;
    }


    /**
     * 获取小程序AccessToken方法
     *
     * @return 小程序的AccessToken
     */
    public String getAppletAccessToken() {
        String accessToken;
        String accessTokenKey = Constants.NURSE_STATION_APPLET_ACCESS_TOKEN + "accessToken";
        //从Redis中取出accessToken
        Object object = redisTemplate.opsForValue().get(accessTokenKey);
        if (Objects.isNull(object)) {
            //没有，获取accessToken
            AppletAccessToken appletAccessToken = AppletChatUtil.getAppletAccessToken(appletChatConfig.getAppletId(), appletChatConfig.getSecret());
            if (Objects.isNull(appletAccessToken)) {
                throw new ServiceException("获取微信小程序accessToken信息失败");
            }
            if (Objects.nonNull(appletAccessToken.getErrcode()) && appletAccessToken.getErrcode() != SUCCESS_CODE) {
                throw new ServiceException("获取微信小程序accessToken信息失败，失败信息为：" + appletAccessToken.getErrmsg(), 201);
            }
            if (StringUtils.isBlank(appletAccessToken.getAccessToken())) {
                throw new ServiceException("accessToken信息为空");
            }
            //存入Redis中
            redisTemplate.opsForValue().set(accessTokenKey, appletAccessToken.getAccessToken(), 3600, TimeUnit.SECONDS);
            accessToken = appletAccessToken.getAccessToken();
        } else {
            accessToken = (String) object;
        }
        return accessToken;
    }
}
