package com.czg.service.account.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.czg.config.RedisCst;
import com.czg.constants.ParamCodeCst;
import com.czg.exception.CzgException;
import com.czg.resp.CzgResult;
import com.czg.service.RedisService;
import com.czg.system.dto.SysParamsDTO;
import com.czg.system.service.SysParamsService;
import com.google.gson.JsonObject;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Administrator
 */
@Slf4j
@Component
public class WechatMiniMsgUtil {
    @DubboReference(check = false)
    private SysParamsService sysParamsService;
    @Resource
    private RedisService redisService;
    @Resource
    private AliOssUtil aliOssUtil;

    private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    private static final String QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode";


    static LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();

    static {

        linkedHashMap.put("40001", "获取 access_token 时 AppSecret 错误，或者 access_token 无效。请开发者认真比对 AppSecret 的正确性，或查看是否正在为恰当的公众号调用接口");
        linkedHashMap.put("40003", "不合法的 OpenID ，请开发者确认 OpenID （该用户）是否已关注公众号，或是否是其他公众号的 OpenID");
        linkedHashMap.put("40014", "不合法的 access_token ，请开发者认真比对 access_token 的有效性（如是否过期），或查看是否正在为恰当的公众号调用接口");
        linkedHashMap.put("40037", "不合法的 template_id");
        linkedHashMap.put("43101", "用户未订阅消息");
        linkedHashMap.put("43107", "订阅消息能力封禁");
        linkedHashMap.put("43108", "并发下发消息给同一个粉丝");
        linkedHashMap.put("45168", "命中敏感词");
        linkedHashMap.put("47003", "参数错误");

    }

    public JSONObject sendTempMsg(String tempId, String toUserOpenId, Map<String, Object> data, String note) {
        log.info("开始发送" + note + "模板消息， 接收用户openId: {}, 消息数据: {}", toUserOpenId, data);
        String token = getAccessToken();

        JSONObject object1 = new JSONObject();

        object1.put("template_id", tempId);
        object1.put("touser", toUserOpenId);
        object1.put("data", data);

        object1.put("miniprogram_state", "trial");
        object1.put("lang", "zh_CN");

        String response = HttpRequest.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=".concat(token)).body(object1.toString()).execute().body();
        log.info("微信模板消息发送成功，相应内容:{}", response);
        JSONObject resObj = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(resObj) && ObjectUtil.isNotNull(resObj) && "0".equals(resObj.get("errcode") + "")) {
            return resObj;
        }

        throw new RuntimeException(linkedHashMap.getOrDefault(resObj.get("errcode") + "", "未知错误"));
    }

    public void sendCurrentOrNearCallMsg(String shopName, String state, String callNum, String currentNum, String note, String openId, boolean isNear) {
        CzgResult<SysParamsDTO> callNear = sysParamsService.getParamsByCode(ParamCodeCst.System.WX_MINI_MSG_CALL_NEAR);
        CzgResult<SysParamsDTO> callCurrent = sysParamsService.getParamsByCode(ParamCodeCst.System.WX_MINI_MSG_CALL_CURRENT);

        Map<String, Object> data = new HashMap<>() {{
            put("thing1", new HashMap<String, Object>() {{
                put("value", shopName);
            }});
            put("phrase2", new HashMap<String, Object>() {{
                put("value", state);
            }});
            put("character_string3", new HashMap<String, Object>() {{
                put("value", callNum);
            }});
            put("character_string4", new HashMap<String, Object>() {{
                put("value", currentNum);
            }});
            put("thing5", new HashMap<String, Object>() {{
                put("value", note);
            }});
        }};
        try {
            sendTempMsg(isNear ? callNear.getData().getParamValue() : callCurrent.getData().getParamValue(), openId, data, "排队到号");
        } catch (Exception e) {
            log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
        }
    }

    public void sendPassCallMsg(String shopName, String state, String callNum, String currentNum, String note, String openId) {
        CzgResult<SysParamsDTO> callPass = sysParamsService.getParamsByCode(ParamCodeCst.System.WX_MINI_MSG_CALL_PASS);

        Map<String, Object> data = new HashMap<>() {{
            put("thing1", new HashMap<String, Object>() {{
                put("value", shopName);
            }});
            put("character_string2", new HashMap<String, Object>() {{
                put("value", callNum);
            }});
            put("character_string3", new HashMap<String, Object>() {{
                put("value", currentNum);
            }});
            put("phrase4", new HashMap<String, Object>() {{
                put("value", state);
            }});
            put("thing5", new HashMap<String, Object>() {{
                put("value", note);
            }});
        }};
        try {
            sendTempMsg(callPass.getData().getParamValue(), openId, data, "过号");
        } catch (Exception e) {
            log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
        }
    }

    public String getAccountOpenId(String code) {
        Map<String, String> shopAc = sysParamsService.getParamsByMap("shop_ac_key_set", ParamCodeCst.SHOP_AC_KEY_SET);
        String accountAppId = shopAc.get(ParamCodeCst.Wechat.Ac.SHOP_WX_AC_APP_ID);
        String accountSecrete = shopAc.get(ParamCodeCst.Wechat.Ac.SHOP_WX_AC_SECRETE);
        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?";
        Map<String, Object> requestUrlParam = new HashMap<>();
        // https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
        //小程序appId
        requestUrlParam.put("appid", accountAppId);
        //小程序secret
        requestUrlParam.put("secret", accountSecrete);
        //小程序端返回的code
        requestUrlParam.put("code", code);
        //默认参数
        requestUrlParam.put("grant_type", "authorization_code");
        log.info("微信获取openid请求报文：{}", requestUrlParam);
        //发送post请求读取调用微信接口获取openid用户唯一标识
        String resp = HttpUtil.post(requestUrl, requestUrlParam);
        log.info("响应报文{}", resp);
        return JSONObject.parseObject(resp).getString("openid");
    }

    //获取小程序token
    private String getAccessToken() {
        // 商户小程序参数
        Map<String, String> shopMiniKeyMap = sysParamsService.getParamsByMap("shop_mini_key_set", ParamCodeCst.SHOP_MINI_KEY_SET);
        String appId = shopMiniKeyMap.get(ParamCodeCst.Wechat.Mini.SHOP_WX_APP_ID);
        String secrete = shopMiniKeyMap.get(ParamCodeCst.Wechat.Mini.SHOP_WX_SECRETE);

        String resp = HttpUtil.get(StrUtil.format("https://access-token.sxczgkj.com/accessToken?appId={}&appSecret={}", appId, secrete));
        if (StrUtil.isNotBlank(resp)) {
            JSONObject respInfo = JSONObject.parseObject(resp);
            return respInfo.getString("accessToken");
        } else {
            String response = HttpUtil.get(String.format("%s?grant_type=client_credential&appid=%s&secret=%s", TOKEN_URL, appId, secrete));
            JSONObject jsonResponse = JSONObject.parseObject(response);
            if (!jsonResponse.containsKey("access_token")) {
                throw new CzgException("零点八零商户端 获取access_token失败: " + response);
            }
            return jsonResponse.getString("access_token");
        }
    }

    /**
     * 生成 小程序码 跳转对应页面
     *
     */
    public String getFetchQrCode(Map<String, Object> params) throws Exception {
        String url = aliOssUtil.upload(fetchQrCode(params), aliOssUtil.getPath("shopVip", "png"));
        redisService.set(RedisCst.SHOP_VIP_CODE + params.get("shopId"), url);
        return url;
    }

    //生成页面地址
    private InputStream fetchQrCode(Map<String, Object> params) {
        JsonObject jsonObject = new JsonObject();
        //路径
        jsonObject.addProperty("path", sysParamsService.getSysParamValue(ParamCodeCst.System.WX_MINI_VIP_URL) + "?shopId=" + params.get("shopId"));
        //是否需要透明底色，为 true 时，生成透明底色的小程序码
        jsonObject.addProperty("is_hyaline", true);
        //正式版为 release，体验版为 trial，开发版为 develop
        if (params.containsKey("env_version")) {
            jsonObject.addProperty("env_version", "trial");
        }
        String accessToken = getAccessToken();
        String url = String.format("%s?access_token=%s", QR_CODE_URL, accessToken);
        return HttpUtil.createPost(url)
                .body(jsonObject.toString(), "application/json")
                .execute()
                .bodyStream();
    }


    public JSONObject getSession(String code) {
        Map<String, String> userMini = sysParamsService.getParamsByMap("user_mini_key_set", ParamCodeCst.USER_MINI_KEY_SET);
        // 用户小程序参数
        String appId = userMini.get(ParamCodeCst.Wechat.Mini.USER_WX_APP_ID);
        String secrete = userMini.get(ParamCodeCst.Wechat.Mini.USER_WX_SECRETE);
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
        Map<String, Object> requestUrlParam = new HashMap<>();
        // https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
        //小程序appId
        requestUrlParam.put("appid", appId);
        //小程序secret
        requestUrlParam.put("secret", secrete);
        //小程序端返回的code
        requestUrlParam.put("js_code", code);
        //默认参数
        requestUrlParam.put("grant_type", "authorization_code");
        //发送post请求读取调用微信接口获取openid用户唯一标识
        String resp = HttpUtil.post(requestUrl, requestUrlParam);
        JSONObject jsonObject = JSON.parseObject(resp);
        log.info("微信获取openid响应报文：{}", resp);
        return jsonObject;
    }

    public String getSessionKey(String code, String key) {
        JSONObject session = getSession(code);
        String info = session.getString(key);
        if (StrUtil.isBlank(info)) {
            throw new RuntimeException(key + "获取失败");
        }
        return info;
    }

    public String getSessionKeyOrOpenId(String code, boolean isAccount) {
        return getSessionKey(code, "openid");
    }

    public static String decrypt(String sessionKey, @NotBlank(message = "数据不能为空") String encryptedData, String iv) {
        // Base64 解码
        byte[] keyBytes = Base64.decode(sessionKey);
        byte[] encryptedBytes = Base64.decode(encryptedData);
        byte[] ivBytes = Base64.decode(iv);

        // 使用 Hutool 进行 AES-CBC 解密
        AES aes = new AES("CBC", "PKCS5Padding", keyBytes, ivBytes);
        byte[] decryptedBytes = aes.decrypt(encryptedBytes);

        return new String(decryptedBytes, java.nio.charset.StandardCharsets.UTF_8);
    }
}
