package com.zhongshu.card.server.core.service.wxQRCode;

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.github.microservice.net.ResultContent;
import com.github.microservice.net.ResultState;
import com.zhongshu.card.client.model.pay.UnionApplySignParam;
import com.zhongshu.card.client.model.projectAbout.ProjectWeChatInfoModel;
import com.zhongshu.card.client.model.qrCode.QRCodeSceneModel;
import com.zhongshu.card.client.model.wechat.AccessTokenVo;
import com.zhongshu.card.client.model.wechat.UnlimitedQRCodeParam;
import com.zhongshu.card.server.core.controller.projectAbout.ProjectWeChatInfoController;
import com.zhongshu.card.server.core.dao.qrCode.QRCodeSceneDao;
import com.zhongshu.card.server.core.domain.qrCode.QRCodeScene;
import com.zhongshu.card.server.core.service.base.RedisService;
import com.zhongshu.card.server.core.service.projectAbout.ProjectWeChatInfoService;
import com.zhongshu.card.server.core.util.CommonUtil;
import com.zhongshu.card.server.core.util.wx.WechatCUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Cleanup;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
public class WeChatService {

    @Autowired
    RedisService redisService;

    @Autowired
    QRCodeSceneDao qrCodeSceneDao;

    @Autowired
    ProjectWeChatInfoService projectWeChatInfoService;

    private final static String ACCESS_TOKEN_KEY = "com:zswl:card: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 UNLIMITED_QRCODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";


    @SneakyThrows
    public void UnlimitedQRCode(UnlimitedQRCodeParam param, HttpServletResponse response) {

        ResultContent<ProjectWeChatInfoModel> weChatInfoRet = projectWeChatInfoService.getProjectWeChatInfo(param.getProjectId());
        if (!weChatInfoRet.getState().equals(ResultState.Success)){
            throw new RuntimeException("项目未配置微信小程序");
        }

        ProjectWeChatInfoModel weChatInfoModel = weChatInfoRet.getContent();

        if (StringUtils.isBlank(weChatInfoModel.getAppId()) || StringUtils.isBlank(weChatInfoModel.getAppSecret())){
            throw new RuntimeException("appId或者secret为空");
        }

        String accessToken = getAccessToken(weChatInfoModel.getAppId(), weChatInfoModel.getAppSecret());

        String sceneId = RandomStringUtils.randomAlphabetic(16);

        QRCodeScene qrCodeScene = new QRCodeScene();
        qrCodeScene.setSceneId(sceneId);
        qrCodeScene.setScene(param.getScene());
        qrCodeSceneDao.save(qrCodeScene);

        byte[] unlimitedQRCodeToByte = getUnlimitedQRCodeToByte(sceneId, param.getPage(), accessToken);
        response.addHeader("Content-Type","image/jpeg");
        @Cleanup OutputStream outputStream = response.getOutputStream();
        outputStream.write(unlimitedQRCodeToByte);
        outputStream.flush();
    }

    public ResultContent getScene(String sceneId){
        QRCodeScene qrCodeScene = qrCodeSceneDao.findTopBySceneId(sceneId);
        QRCodeSceneModel qrCodeSceneModel = new QRCodeSceneModel();
        if (qrCodeScene != null){
            BeanUtils.copyProperties(qrCodeScene, qrCodeSceneModel);
        }
        return ResultContent.buildContent(qrCodeSceneModel);
    }

    public String getAccessToken(String appId, String secret) throws Exception {
        String value = redisService.getValue(ACCESS_TOKEN_KEY + appId + ":");
        if (Objects.isNull(value)) {
            AccessTokenVo accessTokenVo = getAccess_token(appId, secret);

            value = accessTokenVo.getAccessToken();
            Integer expiresIn = accessTokenVo.getExpiresIn();

            redisService.setValueSecond(ACCESS_TOKEN_KEY, value, expiresIn);
        }
        return value;
    }

    private synchronized AccessTokenVo getAccess_token(String appId, String secret) throws IOException {
        String url = String.format(ACCESS_TOKEN_URL, appId, secret);
        Map<String, Object> map = new HashMap<>();
        map.put("grant_type", "client_credential");
        map.put("appid", appId.trim());
        map.put("secret", secret.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;
    }

    /**
     * 生成微信小程序 页面二维码
     *
     * @param scene
     * @param page
     * @return
     * @throws Exception
     */
    public byte[] getUnlimitedQRCodeToByte(String scene, String page, String accessToken) throws Exception {
        String url = String.format(UNLIMITED_QRCODE_URL, accessToken);
        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;
    }
}
