package cn.aqroid.business.auth.service.impl;

import cn.aqroid.business.auth.config.WxConfig;
import cn.aqroid.business.auth.domain.WxSession;
import cn.aqroid.business.auth.service.WxAuthService;
import cn.aqroid.common.exception.ServiceException;
import cn.aqroid.common.utils.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

/**
 * 微信认证服务实现
 *
 * @author aqroid
 * @date 2026-01-12
 */
@Service
public class WxAuthServiceImpl implements WxAuthService {

    private static final Logger log = LoggerFactory.getLogger(WxAuthServiceImpl.class);

    /** 微信登录接口URL */
    private static final String WX_CODE2SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";

    /** 微信获取access_token接口URL */
    private static final String WX_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    /** access_token缓存key */
    private static final String ACCESS_TOKEN_CACHE_KEY = "wx:access_token";

    @Autowired
    private WxConfig wxConfig;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    static {
        // 添加BouncyCastle加密提供者
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * 通过code获取微信会话信息
     */
    @Override
    public WxSession code2Session(String code) {
        if (StringUtils.isEmpty(code)) {
            throw new ServiceException("微信登录code不能为空");
        }

        String url = String.format(WX_CODE2SESSION_URL, 
                wxConfig.getAppId(), 
                wxConfig.getAppSecret(), 
                code);

        log.info("调用微信code2session接口, code: {}", code);

        try {
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            String body = response.getBody();
            
            log.info("微信code2session响应: {}", body);

            if (StringUtils.isEmpty(body)) {
                throw new ServiceException("微信登录失败，响应为空");
            }

            JSONObject json = JSON.parseObject(body);
            WxSession session = new WxSession();
            session.setOpenid(json.getString("openid"));
            session.setSessionKey(json.getString("session_key"));
            session.setUnionid(json.getString("unionid"));
            session.setErrcode(json.getInteger("errcode"));
            session.setErrmsg(json.getString("errmsg"));

            if (!session.isSuccess()) {
                log.error("微信登录失败: errcode={}, errmsg={}", session.getErrcode(), session.getErrmsg());
                throw new ServiceException("微信登录失败: " + session.getErrmsg());
            }

            return session;
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("调用微信code2session接口异常", e);
            throw new ServiceException("微信登录失败，请稍后重试");
        }
    }

    /**
     * 解密微信手机号
     */
    @Override
    public String decryptPhoneNumber(String sessionKey, String encryptedData, String iv) {
        if (StringUtils.isEmpty(sessionKey) || StringUtils.isEmpty(encryptedData) || StringUtils.isEmpty(iv)) {
            throw new ServiceException("解密参数不能为空");
        }

        try {
            byte[] keyBytes = Base64.getDecoder().decode(sessionKey);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);

            // 使用AES/CBC/PKCS7Padding解密
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
            params.init(new IvParameterSpec(ivBytes));
            cipher.init(Cipher.DECRYPT_MODE, keySpec, params);

            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8);

            log.info("解密后的数据: {}", decryptedData);

            // 解析JSON获取手机号
            JSONObject json = JSON.parseObject(decryptedData);
            String phoneNumber = json.getString("phoneNumber");

            if (StringUtils.isEmpty(phoneNumber)) {
                throw new ServiceException("获取手机号失败");
            }

            return phoneNumber;
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("解密微信手机号异常", e);
            throw new ServiceException("解密手机号失败，请重新授权");
        }
    }

    /**
     * 获取微信接口调用凭证
     */
    @Override
    public String getAccessToken() {
        // 先从缓存获取
        String accessToken = redisTemplate.opsForValue().get(ACCESS_TOKEN_CACHE_KEY);
        if (StringUtils.isNotEmpty(accessToken)) {
            return accessToken;
        }

        // 调用微信接口获取
        String url = String.format(WX_ACCESS_TOKEN_URL, 
                wxConfig.getAppId(), 
                wxConfig.getAppSecret());

        try {
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            String body = response.getBody();

            if (StringUtils.isEmpty(body)) {
                throw new ServiceException("获取access_token失败，响应为空");
            }

            JSONObject json = JSON.parseObject(body);
            accessToken = json.getString("access_token");
            Integer expiresIn = json.getInteger("expires_in");

            if (StringUtils.isEmpty(accessToken)) {
                String errmsg = json.getString("errmsg");
                throw new ServiceException("获取access_token失败: " + errmsg);
            }

            // 缓存access_token，提前5分钟过期
            redisTemplate.opsForValue().set(ACCESS_TOKEN_CACHE_KEY, accessToken, 
                    expiresIn - 300, TimeUnit.SECONDS);

            return accessToken;
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("获取微信access_token异常", e);
            throw new ServiceException("获取access_token失败，请稍后重试");
        }
    }
}
