package org.xyzh.common.wechat.service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.xyzh.api.system.service.SysConfigService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.wechat.config.WeChatMiniProgramConfig;
import org.xyzh.common.wechat.pojo.WeChatPhoneResult;
import org.xyzh.common.wechat.pojo.WeChatSessionResult;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

/**
 * 微信小程序服务
 * @author cascade
 * @since 2026-01-09
 */
@Lazy
@Service
public class WeChatMiniProgramService {
    
    private static final Logger logger = LoggerFactory.getLogger(WeChatMiniProgramService.class);
    
    /** 微信小程序配置缓存 */
    private volatile WeChatMiniProgramConfig config;
    
    /** code2Session接口地址 */
    private static final String CODE2SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";
    
    /** 获取access_token接口地址 */
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    
    /** 获取手机号接口地址（新版API） */
    private static final String GET_PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
    
    @Lazy
    @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0)
    private SysConfigService sysConfigService;
    
    /**
     * 获取微信小程序配置
     */
    public WeChatMiniProgramConfig getConfig() {
        if (config == null) {
            loadConfig();
        }
        return config;
    }
    
    /**
     * 从系统配置加载微信小程序配置
     */
    private synchronized void loadConfig() {
        if (config != null) {
            return;
        }
        
        config = new WeChatMiniProgramConfig();
        
        try {
            // 从系统配置获取小程序AppID
            String appIdResult = sysConfigService.getStringConfig("wechat.miniprogram.appid");
            if (appIdResult != null && !appIdResult.trim().isEmpty()) {
                config.setAppId(appIdResult);
            }
            
            // 从系统配置获取小程序AppSecret
            String appSecretResult = sysConfigService.getStringConfig("wechat.miniprogram.appsecret");
            if (appSecretResult != null && !appSecretResult.trim().isEmpty()) {
                config.setAppSecret(appSecretResult);
            }
            
            logger.info("微信小程序配置加载成功, appId: {}", config.getAppId());
        } catch (Exception e) {
            logger.error("加载微信小程序配置失败", e);
        }
    }
    
    /**
     * 刷新配置
     */
    public void refreshConfig() {
        config = null;
        loadConfig();
    }
    
    /**
     * 通过code获取session信息（openid和session_key）
     * @param code wx.login返回的code
     * @return session信息
     */
    public ResultDomain<WeChatSessionResult> code2Session(String code) {
        WeChatMiniProgramConfig cfg = getConfig();
        if (cfg == null || cfg.getAppId() == null || cfg.getAppSecret() == null) {
            return ResultDomain.failure("微信小程序配置未初始化");
        }
        
        try {
            String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                    CODE2SESSION_URL, cfg.getAppId(), cfg.getAppSecret(), code);
            
            String response = httpGet(url);
            logger.debug("code2Session响应: {}", response);
            
            WeChatSessionResult result = JSON.parseObject(response, WeChatSessionResult.class);
            
            if (result.getErrcode() != null && result.getErrcode() != 0) {
                logger.error("code2Session失败: {} - {}", result.getErrcode(), result.getErrmsg());
                return ResultDomain.failure("获取session失败: " + result.getErrmsg());
            }
            
            return ResultDomain.success("获取session成功", result);
        } catch (Exception e) {
            logger.error("code2Session异常", e);
            return ResultDomain.failure("获取session异常: " + e.getMessage());
        }
    }
    
    /**
     * 获取access_token
     * @return access_token
     */
    public String getAccessToken() {
        WeChatMiniProgramConfig cfg = getConfig();
        if (cfg == null) {
            return null;
        }
        
        // 检查缓存的token是否有效
        if (cfg.getAccessToken() != null && cfg.getAccessTokenExpireTime() != null 
                && System.currentTimeMillis() < cfg.getAccessTokenExpireTime()) {
            return cfg.getAccessToken();
        }
        
        // 重新获取token
        try {
            String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
                    ACCESS_TOKEN_URL, cfg.getAppId(), cfg.getAppSecret());
            
            String response = httpGet(url);
            JSONObject json = JSON.parseObject(response);
            
            if (json.containsKey("access_token")) {
                String accessToken = json.getString("access_token");
                int expiresIn = json.getIntValue("expires_in");
                
                cfg.setAccessToken(accessToken);
                // 提前5分钟过期
                cfg.setAccessTokenExpireTime(System.currentTimeMillis() + (expiresIn - 300) * 1000L);
                
                logger.info("获取access_token成功, 有效期: {}秒", expiresIn);
                return accessToken;
            } else {
                logger.error("获取access_token失败: {}", response);
                return null;
            }
        } catch (Exception e) {
            logger.error("获取access_token异常", e);
            return null;
        }
    }
    
    /**
     * 通过phoneCode获取手机号（新版API，推荐使用）
     * @param phoneCode getPhoneNumber返回的code
     * @return 手机号信息
     */
    public ResultDomain<WeChatPhoneResult> getPhoneNumber(String phoneCode) {
        String accessToken = getAccessToken();
        if (accessToken == null) {
            return ResultDomain.failure("获取access_token失败");
        }
        
        try {
            String url = GET_PHONE_URL + "?access_token=" + accessToken;
            
            JSONObject requestBody = new JSONObject();
            requestBody.put("code", phoneCode);
            
            String response = httpPost(url, requestBody.toJSONString());
            logger.debug("getPhoneNumber响应: {}", response);
            
            WeChatPhoneResult result = JSON.parseObject(response, WeChatPhoneResult.class);
            
            if (result.getErrcode() != null && result.getErrcode() != 0) {
                logger.error("getPhoneNumber失败: {} - {}", result.getErrcode(), result.getErrmsg());
                return ResultDomain.failure("获取手机号失败: " + result.getErrmsg());
            }
            
            return ResultDomain.success("获取手机号成功", result);
        } catch (Exception e) {
            logger.error("getPhoneNumber异常", e);
            return ResultDomain.failure("获取手机号异常: " + e.getMessage());
        }
    }
    
    /**
     * 通过encryptedData和iv解密手机号（旧版API）
     * @param sessionKey 会话密钥
     * @param encryptedData 加密数据
     * @param iv 解密向量
     * @return 解密后的手机号
     */
    public ResultDomain<String> decryptPhoneNumber(String sessionKey, String encryptedData, String iv) {
        try {
            byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
            byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            
            byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
            String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8);
            
            logger.debug("解密数据: {}", decryptedData);
            
            JSONObject json = JSON.parseObject(decryptedData);
            String phoneNumber = json.getString("phoneNumber");
            
            if (phoneNumber == null || phoneNumber.isEmpty()) {
                phoneNumber = json.getString("purePhoneNumber");
            }
            
            return ResultDomain.success("解密成功", phoneNumber);
        } catch (Exception e) {
            logger.error("解密手机号失败", e);
            return ResultDomain.failure("解密手机号失败: " + e.getMessage());
        }
    }
    
    /**
     * HTTP GET请求
     */
    private String httpGet(String urlStr) throws Exception {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        } finally {
            conn.disconnect();
        }
    }
    
    /**
     * HTTP POST请求
     */
    private String httpPost(String urlStr, String body) throws Exception {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json");
        
        try (OutputStream os = conn.getOutputStream()) {
            os.write(body.getBytes(StandardCharsets.UTF_8));
        }
        
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        } finally {
            conn.disconnect();
        }
    }
    
}
