package com.zhongshu.card.server.core.util.wx;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.github.microservice.core.util.net.HttpClient;
import com.github.microservice.core.util.net.apache.HttpClientUtil;
import com.github.microservice.core.util.net.apache.HttpModel;
import com.github.microservice.core.util.net.apache.MethodType;
import com.github.microservice.core.util.net.apache.ResponseModel;
import com.zhongshu.card.client.model.wechat.AccessTokenVo;
import com.zhongshu.card.client.model.wechat.GenerateSchemeVo;
import com.zhongshu.card.client.model.wechat.MiniAppUserInfoVo;
import com.zhongshu.card.client.model.wechat.WechatPhoneNumber;
import com.github.microservice.net.ResultContent;
import com.github.microservice.net.ResultState;
import com.zhongshu.card.server.core.service.base.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 *
 */
@Component
@Slf4j
public class WechatCUtil {

    private final static String ACCESS_TOKEN_KEY = "com:zswl:cloud:bdb:wechat:accessToken:";

    private final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token?grant_type=client_credential&appid=%s&secret=%s";

    private final static String WECHAT_MINI_APP_AUTH_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=%s";

    private final static String PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";

    private final static String GENERATE_SCHEME = "https://api.weixin.qq.com/wxa/generatescheme?access_token=%s";

    private final static String UNLIMITED_QRCODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";

    @Resource
    private WechatCAuthConfig wechatAuthConfig;

    @Autowired
    private RedisService redisService;

    //    @PostConstruct
    public void init() throws Exception {
        log.info("init微信Token... ");
        AccessTokenVo accessTokenVo = getAccess_token();
        if (Objects.nonNull(accessTokenVo) && StringUtils.isNotEmpty(accessTokenVo.getAccessToken())) {
            log.info("accessTokenVo: {}", accessTokenVo);
            redisService.setValueSecond(ACCESS_TOKEN_KEY, accessTokenVo.getAccessToken(), 300);
            log.info("微信登录初始化成功");
        } else {
            log.error("*******fail *********");
            log.error("WechatUtil init fail");
            log.error("*******fail *********");
        }
    }

    public AccessTokenVo genAccessToken() throws Exception {
        String url = String.format(ACCESS_TOKEN_URL, wechatAuthConfig.getAppid().trim(), wechatAuthConfig.getSecret().trim());
        ResponseModel request = HttpClientUtil.request(HttpModel.builder().method(MethodType.Get).url(url).build());
        if (200 == request.getCode()) {
            return BeanUtil.copyProperties(request.getBody(), AccessTokenVo.class);
        }
        return null;
    }

    public synchronized AccessTokenVo getAccess_token() throws IOException {
        String url = String.format(ACCESS_TOKEN_URL, wechatAuthConfig.getAppid(), wechatAuthConfig.getSecret());
        Map<String, Object> map = new HashMap<>();
        map.put("grant_type", "client_credential");
        map.put("appid", wechatAuthConfig.getAppid().trim());
        map.put("secret", wechatAuthConfig.getSecret().trim());
        String json = JSONUtil.toJsonStr(map);
        ResponseModel request = HttpClientUtil.request(HttpModel.builder()
                .url(url).method(MethodType.Post).body(json).build());
        if (200 == request.getCode()) {
            return BeanUtil.copyProperties(request.getBody(), AccessTokenVo.class);
        }
        return null;
    }

    public String getAccessToken() throws Exception {
        String value = redisService.getValue(ACCESS_TOKEN_KEY);
        if (Objects.isNull(value)) {
            init();
            value = redisService.getValue(ACCESS_TOKEN_KEY);
        }
        return value;
    }

    public WechatPhoneNumber getPhoneNumber(String code) throws Exception {
        String url = String.format(PHONE_NUMBER_URL, getAccessToken());
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        String json = JSONUtil.toJsonStr(map);
        ResponseModel request = HttpClientUtil.request(HttpModel.builder().method(MethodType.Post)
                .url(url).body(json).build());
        if (200 == request.getCode()) {
            try {
                LinkedHashMap map1 = (LinkedHashMap) request.getBody();
                if (map1.containsKey("errcode")) {
                    log.error("getPhoneNumber error: {} {} {}", map1.get("errcode"), map1.get("errmsg"), code);
                    String errcode = map1.get("errcode").toString();
                    if (errcode.equals("40001")) {
                        clear();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            WechatPhoneNumber wechatPhoneNumber = BeanUtil.copyProperties(((LinkedHashMap) request.getBody()).get("phone_info"), WechatPhoneNumber.class);
            log.info("wechatPhoneNumber: {}", wechatPhoneNumber);
            return wechatPhoneNumber;
        }
        return null;
    }

    public MiniAppUserInfoVo getMiniAppUserInfo(String jsCode) throws Exception {
        String url = String.format(WECHAT_MINI_APP_AUTH_URL, wechatAuthConfig.getAppid(), wechatAuthConfig.getSecret(), jsCode, "authorization_code");
        ResponseModel request = HttpClientUtil.request(HttpModel.builder().url(url).build());
        if (200 == request.getCode()) {
            MiniAppUserInfoVo miniAppUserInfoVo = BeanUtil.copyProperties(
                    JSONUtil.parseObj(request.getBody().toString()),
                    MiniAppUserInfoVo.class);
            if (Objects.nonNull(miniAppUserInfoVo.getOpenid())) {
                return miniAppUserInfoVo;
            }
        } else {
            MiniAppUserInfoVo vo = new MiniAppUserInfoVo();
            vo.setMsg(request.toString());
            return vo;
        }
        return null;
    }

    public ResultContent generateScheme() throws Exception {
        String accessToken = getAccessToken();
        String url = String.format(GENERATE_SCHEME, accessToken);
        ResponseModel request = HttpClientUtil.request(HttpModel.builder().method(MethodType.Post).url(url).build());
        String msg = "";
        if (200 == request.getCode()) {
            LinkedHashMap body = (LinkedHashMap) request.getBody();
            Integer errcode = (Integer) body.get("errcode");
            msg = body.get("errmsg").toString();
            String openlink = (String) body.get("openlink");
            GenerateSchemeVo vo = new GenerateSchemeVo(errcode, msg, openlink);
            if (Objects.nonNull(vo.getOpenLink()))
                return ResultContent.buildContent(vo.getOpenLink());
            msg = vo.getErrMsg();
        }
        return ResultContent.build(ResultState.Exception, msg);
    }

    /**
     * 生成微信小程序 页面二维码
     *
     * @param scene
     * @param page
     * @return
     * @throws Exception
     */
    public byte[] getUnlimitedQRCodeToByte(String scene, String page) throws Exception {
        String url = String.format(UNLIMITED_QRCODE_URL, getAccessToken());
        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", "image/jpeg");
        Map<String, Object> map = new HashMap<>();
        map.put("scene", scene);
        if (StringUtils.isNotEmpty(page)) {
            map.put("page", page);
        }
        map.put("env_version", "trial");
        HttpClient.ResultBean resultBean = new HttpClient().ReadDocuments(url, true,
                JSONUtil.toJsonStr(map).getBytes(), header);
        if (200 == resultBean.getStat()) {
            return resultBean.getData();
        }
        return null;
    }

    public void clear() {
        redisService.removeValue(ACCESS_TOKEN_KEY);
    }
}
