package com.ruoyi.cms.util;

import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class WechatUtil {
    /**
     * 生成signature
     **/
    /*private static String appid = "wx9d1cbc11c8c40ba7";
    private static String secret = "38e87cf6251945446e8ac091a0ba9ab2";*/

    // 令牌自定义标识
    @Value("${wx.appid}")
    private String appid;

    // 令牌秘钥
    @Value("${wx.secret}")
    private String secret;

    public AppWechatEntity sign(String url) {
        Map<String, String> ret = new HashMap();
        String nonceStr = create_nonce_str();
        String timestamp = Long.toString(create_timestamp());
        String string1;
        String signature = "";
        //获取jsapi_ticket和过期时间
        AppWechatEntity appWechatEntity = getJsapiTicket();
        String jsapiTicket = appWechatEntity.getJsapi_ticket();
        Long expireTime = appWechatEntity.getExpire_time();
        //注意这里参数名必须全部小写，且必须有序
        string1 = "jsapi_ticket=" + jsapiTicket +
                "&noncestr=" + nonceStr +
                "&timestamp=" + timestamp +
                "&url=" + url;
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes(StandardCharsets.UTF_8));
            signature = byteToHex(crypt.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }

        appWechatEntity.setAppId(appid);
        appWechatEntity.setExpire_time(expireTime);
        appWechatEntity.setNonceStr(nonceStr);
        appWechatEntity.setTimestamp(timestamp);
        appWechatEntity.setSignature(signature);
        return appWechatEntity;
    }


    /**
     * 获取jsapi_ticket
     **/
    public AppWechatEntity getJsapiTicket() {
        //logger.debug("--------------开始执行getJsapiTicket方法--------------");
        //定义过期时间
        AppWechatEntity appWechatEntity = new AppWechatEntity();
        String accessTokenString = "";
        String jsapiTicketString = "";
        String jsapi_ticket = "";
        String access_token = "";
        jsapiTicketString = readWechatTokenFile(getJsapiTicketFilePath());
        if (!StringUtils.isEmpty(jsapiTicketString)) {
            appWechatEntity = JSONObject.parseObject(jsapiTicketString, AppWechatEntity.class);
            long expireTime = appWechatEntity.getExpire_time();
            long curTime = create_timestamp();
            if (expireTime >= curTime && StrUtil.isNotEmpty(appWechatEntity.getJsapi_ticket())) {
                //logger.debug("已有的jsapi_ticket=" + jsapi_ticket);
                return appWechatEntity;
            }
        }

        long timestamp = create_timestamp() + 7000;//过期时间是2小时(7200s)
        access_token =
                getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid+ "&secret=" + secret);
        Map accessTokenMap = new HashMap();
        accessTokenMap.put("expire_time", timestamp);
        accessTokenMap.put("access_token", access_token);
        accessTokenString = JSONObject.toJSONString(accessTokenMap);

        jsapi_ticket = getJsapiTicketData("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi");
        Map jsapiTicketMap = new HashMap();
        jsapiTicketMap.put("expire_time", timestamp);
        jsapiTicketMap.put("jsapi_ticket", jsapi_ticket);
        jsapiTicketString = JSONObject.toJSONString(jsapiTicketMap);

        // 写文件
        try {
            FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8);
            FileUtils.writeStringToFile(new File(getJsapiTicketFilePath()), jsapiTicketString, CharsetUtil.CHARSET_UTF_8);
            //logger.debug("写入文件成功");
        } catch (IOException e) {
            log.debug("写文件异常：" + e.getMessage());
            e.printStackTrace();
        }

        appWechatEntity.setJsapi_ticket(jsapi_ticket);
        appWechatEntity.setExpire_time(timestamp);
        appWechatEntity.setAccess_token(access_token);
        //logger.debug("--------------结束执行getJsapiTicket方法--------------");
        return appWechatEntity;
    }

    public String getAccessToken() {
        //logger.debug("--------------开始执行getAccessToken方法--------------");
        String access_token = "";
        String accessTokenString = "";
        AppWechatEntity appWechatEntity = new AppWechatEntity();
        accessTokenString = readWechatTokenFile(getAccessTokenFilePath());
        if (StringUtils.isNotEmpty(accessTokenString)) {
            appWechatEntity = JSONObject.parseObject(accessTokenString, AppWechatEntity.class);
            access_token = appWechatEntity.getAccess_token();
            long expireTime = appWechatEntity.getExpire_time();
            long curTime = create_timestamp();
            if (expireTime >= curTime) {
                //logger.debug("已有的access_token=" + access_token);
                return access_token;
            }
        }

        long timestamp = create_timestamp() + 6000;//过期时间是2小时，但是可以提前进行更新，防止前端正好过期
        access_token =
                getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid  + "&secret=" + secret);
        Map accessTokenMap = new HashMap();
        accessTokenMap.put("expire_time", timestamp);
        accessTokenMap.put("access_token", access_token);
        accessTokenString = JSONObject.toJSONString(accessTokenMap);

        // 写文件
        try {
            FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8);
        } catch (IOException e) {
            log.debug("写文件异常：" + e.getMessage());
            e.printStackTrace();
        }
        //logger.debug("新的access_token=" + access_token);
        //logger.debug("--------------结束执行getAccessToken方法--------------");
        return access_token;
    }

    private String readWechatTokenFile(String filePath) {
        String content = "";
        try {
            if (new File(filePath).exists()) {
                FileReader fileReader = new FileReader(filePath, CharsetUtil.CHARSET_UTF_8);
                content = fileReader.readString();
            } else {
                new File(filePath).createNewFile();
            }
        } catch (IOException e) {
            log.error("读文件异常：" + e.getMessage());
            e.printStackTrace();
        }
        return content;
    }

    private String getAccessTokenData(String url) {
        String str = "";
        String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
        System.out.println("result=============="+result);
        if (StringUtils.isEmpty(result))
            return str;
        str = parseData("access_token", "expires_in", result);
        return str;
    }

    private String getJsapiTicketData(String url) {
        String str = "";
        String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
        if (StringUtils.isEmpty(result))
            return str;
        str = parseData("ticket", "expires_in", result);
        return str;
    }

    private String parseData(String tokenName, String expiresInName, String data) {
        String tokenConent = "";
        JSONObject jsonObject = JSONObject.parseObject(data);
        try {
            tokenConent = jsonObject.get(tokenName).toString();
            if (StringUtils.isEmpty(tokenConent)) {
                log.error("token获取失败,获取结果" + data);
                return tokenConent;
            }
        } catch (Exception e) {
            log.error("token 结果解析失败，token参数名称: " + tokenName + "有效期参数名称:" + expiresInName + "token请求结果:" + data);
            e.printStackTrace();
            return tokenConent;
        }
        return tokenConent;
    }

    /**
     * 获取appid和session_key
     * @param url
     * @return
     */
    private String getAccessData(String url) {
        String str = "";
        String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
        System.out.println("result=============="+result);
        if (StringUtils.isEmpty(result))
            return str;
        return result;
    }

    private String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 通过code获取微信用户的openid和session_key
     *
     * @param code      登录凭证code
     * @return 包含openid、session_key、unionid的JSON对象
     */
    public JSONObject code2Session(String code) {
        try {
            System.out.println("appid==============="+appid);
            System.out.println("secret================"+secret);
            String response = getAccessData(AliyunNlsUtils.getWXoppenidUrl()+"?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code");
            JSONObject result = JSONObject.parseObject(response);
            // 微信返回错误码处理
            if (result.containsKey("errcode") && result.getInteger("errcode") != 0) {
                throw new RuntimeException("微信授权失败：" + result.getString("errmsg"));
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException("调用微信接口失败：" + e.getMessage());
        }
    }

    /**
     * 解密微信用户手机号（用户通过 getPhoneNumber 组件授权后返回的加密数据）
     * @param encryptedData 微信返回的加密手机号数据
     * @param sessionKey 从 code2Session 接口获取的会话密钥
     * @param iv 微信返回的加密向量（与 encryptedData 配套）
     * @return 解密后的 JSON 对象（包含 phoneNumber、purePhoneNumber 等字段）
     * @throws RuntimeException 解密失败时抛出
     */
    public JSONObject decryptPhoneNumber(String encryptedData, String sessionKey, String iv) {
        try {
            // 1. Base64 解码（encryptedData、sessionKey、iv 均为 Base64 编码）
            byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
            byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
            byte[] ivBytes = Base64.getDecoder().decode(iv);

            // 2. 验证session_key长度（AES-128要求密钥长度为16字节）
            if (sessionKeyBytes.length != 16) {
                throw new RuntimeException("session_key长度错误，应为16字节");
            }
            // 验证iv长度（CBC模式下iv长度必须与块大小一致，AES为16字节）
            if (ivBytes.length != 16) {
                throw new RuntimeException("iv长度错误，应为16字节");
            }

            // 2. 初始化 AES-128-CBC 解密器（使用PKCS5Padding替换PKCS7Padding，两者在AES中效果一致）
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            // 3. 执行解密并转换为字符串
            byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
            String decryptedStr = new String(decryptedBytes, StandardCharsets.UTF_8);

            // 4. 解析为 JSON 并返回（包含手机号等信息）
            return JSONObject.parseObject(decryptedStr);
        } catch (Exception e) {
            throw new RuntimeException("解密用户手机号失败：" + e.getMessage(), e);
        }
    }

    private String create_nonce_str() {
        return IdUtil.simpleUUID();
    }

    private Long create_timestamp() {
        return System.currentTimeMillis() / 1000;
    }


    private String getJsapiTicketFilePath() {
        return "/data/wechat" + "/" +appid + "_jsapiTicket.txt";
    }

    private String getAccessTokenFilePath() {
        return "/data/wechat" + "/" + appid + "_accessToken.txt";
    }
}
