package shop.alien.store.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;
import shop.alien.store.config.BaseRedisService;
import shop.alien.store.config.WeChatMiniProgramConfig;
import shop.alien.store.service.WeChatMiniProgramQrCodeService;
import shop.alien.util.ali.AliOSSUtil;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序二维码服务实现类
 *
 * @author system
 * @since 2025-01-XX
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatMiniProgramQrCodeServiceImpl implements WeChatMiniProgramQrCodeService {

    private final WeChatMiniProgramConfig config;
    private final BaseRedisService redisService;
    private final AliOSSUtil aliOSSUtil;

    /**
     * Redis缓存Key前缀
     */
    private static final String ACCESS_TOKEN_CACHE_KEY = "wechat:miniprogram:access_token";

    /**
     * Access Token缓存时间（秒），比微信的7200秒稍短
     */
    private static final long ACCESS_TOKEN_CACHE_SECONDS = 7000L;

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

    /**
     * 微信生成小程序码接口（无数量限制）
     */
    private static final String QR_CODE_UNLIMITED_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";

    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    @Override
    public String getAccessToken() {
        // 先从Redis缓存获取
        String cachedToken = redisService.getString(ACCESS_TOKEN_CACHE_KEY);
        if (cachedToken != null && !cachedToken.isEmpty()) {
            log.debug("从Redis缓存获取Access Token成功");
            return cachedToken;
        }

        // 缓存不存在，调用微信API获取
        String url = String.format(ACCESS_TOKEN_URL, config.getAppId(), config.getAppSecret());
        
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                log.error("获取Access Token失败，HTTP状态码: {}", response.code());
                throw new RuntimeException("获取Access Token失败，HTTP状态码: " + response.code());
            }

            String responseBody = response.body() != null ? response.body().string() : "";
            JSONObject jsonObject = JSON.parseObject(responseBody);

            if (jsonObject.containsKey("errcode") && jsonObject.getIntValue("errcode") != 0) {
                log.error("获取Access Token失败，错误码: {}, 错误信息: {}", 
                        jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg"));
                throw new RuntimeException("获取Access Token失败: " + jsonObject.getString("errmsg"));
            }

            String accessToken = jsonObject.getString("access_token");
            if (accessToken == null || accessToken.isEmpty()) {
                log.error("获取Access Token失败，返回数据异常: {}", responseBody);
                throw new RuntimeException("获取Access Token失败，返回数据异常");
            }

            // 缓存到Redis
            redisService.setString(ACCESS_TOKEN_CACHE_KEY, accessToken, ACCESS_TOKEN_CACHE_SECONDS);
            log.info("获取Access Token成功并缓存到Redis");

            return accessToken;
        } catch (IOException e) {
            log.error("获取Access Token网络请求失败: {}", e.getMessage(), e);
            throw new RuntimeException("获取Access Token网络请求失败: " + e.getMessage(), e);
        }
    }

    @Override
    public byte[] generateQrCode(String scene, String page, Integer width) {
        log.info("开始生成小程序二维码, scene={}, page={}, width={}", scene, page, width);

        // 参数校验
        if (scene == null || scene.isEmpty()) {
            throw new IllegalArgumentException("scene参数不能为空");
        }
        if (scene.length() > 32) {
            log.warn("scene参数超过32字符，将被截断: {}", scene);
            scene = scene.substring(0, 32);
        }

        // 获取Access Token
        String accessToken = getAccessToken();
        String url = String.format(QR_CODE_UNLIMITED_URL, accessToken);

        // 构建请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("scene", scene);
        params.put("env_version", config.getEnvVersion()); // develop-开发版
        
        // 页面路径
        String pagePath = (page != null && !page.isEmpty()) ? page : config.getPagePath();
        if (pagePath != null && !pagePath.isEmpty()) {
            params.put("page", pagePath);
        }
        
        // 二维码宽度
        if (width != null && width >= 280 && width <= 1280) {
            params.put("width", width);
        } else {
            params.put("width", 430); // 默认宽度
        }

        // 自动配置线条颜色
        params.put("auto_color", true);
        // 是否需要透明底色
        params.put("is_hyaline", false);
        // 开发版或体验版时，跳过页面路径校验
        if ("develop".equals(config.getEnvVersion()) || "trial".equals(config.getEnvVersion())) {
            params.put("check_path", false);
        }

        String jsonBody = JSON.toJSONString(params);
        log.info("请求微信小程序码接口, URL={}, params={}", url, jsonBody);

        RequestBody requestBody = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"), jsonBody);

        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                log.error("生成小程序二维码失败，HTTP状态码: {}", response.code());
                throw new RuntimeException("生成小程序二维码失败，HTTP状态码: " + response.code());
            }

            // 获取Content-Type
            String contentType = response.header("Content-Type");
            byte[] bodyBytes = response.body() != null ? response.body().bytes() : new byte[0];

            // 判断返回类型
            if (contentType != null && contentType.contains("application/json")) {
                // 返回JSON表示出错了
                String errorJson = new String(bodyBytes);
                JSONObject jsonObject = JSON.parseObject(errorJson);
                log.error("生成小程序二维码失败，错误码: {}, 错误信息: {}", 
                        jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg"));
                
                // 如果是access_token过期，清除缓存并重试一次
                if (jsonObject.getIntValue("errcode") == 40001 || 
                    jsonObject.getIntValue("errcode") == 42001) {
                    log.info("Access Token已过期，清除缓存并重试");
                    redisService.delete(ACCESS_TOKEN_CACHE_KEY);
                    return retryGenerateQrCode(scene, pagePath, width);
                }
                
                throw new RuntimeException("生成小程序二维码失败: " + jsonObject.getString("errmsg"));
            }

            // 返回图片数据
            if (bodyBytes.length == 0) {
                throw new RuntimeException("生成小程序二维码失败: 返回数据为空");
            }

            log.info("生成小程序二维码成功, 图片大小: {} bytes", bodyBytes.length);
            return bodyBytes;
        } catch (IOException e) {
            log.error("生成小程序二维码网络请求失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成小程序二维码网络请求失败: " + e.getMessage(), e);
        }
    }

    /**
     * 重试生成二维码（用于access_token过期的情况）
     */
    private byte[] retryGenerateQrCode(String scene, String page, Integer width) {
        String accessToken = getAccessToken();
        String url = String.format(QR_CODE_UNLIMITED_URL, accessToken);

        Map<String, Object> params = new HashMap<>();
        params.put("scene", scene);
        params.put("env_version", config.getEnvVersion());
        if (page != null && !page.isEmpty()) {
            params.put("page", page);
        }
        params.put("width", width != null ? width : 430);
        params.put("auto_color", true);
        params.put("is_hyaline", false);
        // 开发版或体验版时，跳过页面路径校验
        if ("develop".equals(config.getEnvVersion()) || "trial".equals(config.getEnvVersion())) {
            params.put("check_path", false);
        }

        RequestBody requestBody = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(params));

        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("重试生成小程序二维码失败，HTTP状态码: " + response.code());
            }

            String contentType = response.header("Content-Type");
            byte[] bodyBytes = response.body() != null ? response.body().bytes() : new byte[0];

            if (contentType != null && contentType.contains("application/json")) {
                String errorJson = new String(bodyBytes);
                JSONObject jsonObject = JSON.parseObject(errorJson);
                throw new RuntimeException("重试生成小程序二维码失败: " + jsonObject.getString("errmsg"));
            }

            return bodyBytes;
        } catch (IOException e) {
            throw new RuntimeException("重试生成小程序二维码网络请求失败: " + e.getMessage(), e);
        }
    }

    @Override
    public String generateQrCodeAndUpload(String scene, String page, Integer width, String ossPath) {
        log.info("生成小程序二维码并上传到OSS, scene={}, page={}, width={}, ossPath={}", 
                scene, page, width, ossPath);

        // 生成二维码
        byte[] qrCodeBytes = generateQrCode(scene, page, width);

        // 上传到OSS
        ByteArrayInputStream inputStream = new ByteArrayInputStream(qrCodeBytes);
        String qrCodeUrl = aliOSSUtil.uploadFile(inputStream, ossPath);

        if (qrCodeUrl == null || qrCodeUrl.isEmpty()) {
            log.error("上传二维码到OSS失败, ossPath={}", ossPath);
            throw new RuntimeException("上传二维码到OSS失败");
        }

        log.info("上传二维码到OSS成功, qrCodeUrl={}", qrCodeUrl);
        return qrCodeUrl;
    }

    @Override
    public String generateTableQrCode(Integer tableId, Integer storeId) {
        log.info("为桌号生成小程序二维码, tableId={}, storeId={}", tableId, storeId);

        if (tableId == null || storeId == null) {
            throw new IllegalArgumentException("tableId和storeId不能为空");
        }

        // 构建scene参数（格式：t=tableId&s=storeId，使用简写确保不超过32字符）
        String scene = String.format("t=%d&s=%d", tableId, storeId);
        
        // 如果scene超过32字符，使用更简洁的格式
        if (scene.length() > 32) {
            scene = String.format("%d-%d", tableId, storeId);
        }

        // 构建OSS存储路径
        String ossPath = String.format("qrcode/table/%d_%d_%d.png", 
                storeId, tableId, System.currentTimeMillis());

        // 生成二维码并上传，扫码后打开指定页面
        return generateQrCodeAndUpload(scene, "pages/launch/index", 430, ossPath);
    }
}
