package com.gxwebsoft.common.system.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gxwebsoft.cms.entity.CmsWebsite;
import com.gxwebsoft.cms.service.CmsWebsiteService;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.security.JwtSubject;
import com.gxwebsoft.common.core.security.JwtUtil;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.RequestUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.*;
import com.gxwebsoft.common.system.param.UserParam;
import com.gxwebsoft.common.system.result.LoginResult;
import com.gxwebsoft.common.system.service.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import okhttp3.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;

import static com.gxwebsoft.common.core.constants.PlatformConstants.MP_WEIXIN;
import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KEY;
import static com.gxwebsoft.common.core.constants.RedisConstants.MP_WX_KEY;

@RestController
@RequestMapping("/api/wx-login")
@Tag(name = "微信小程序登录API")
public class WxLoginController extends BaseController {
  private final StringRedisTemplate redisTemplate;
  private final OkHttpClient http = new OkHttpClient();
  private final ObjectMapper om;
  private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒
  @Resource
  private SettingService settingService;
  @Resource
  private UserService userService;
  @Resource
  private ConfigProperties configProperties;
  @Resource
  private UserRoleService userRoleService;
  @Resource
  private LoginRecordService loginRecordService;
  @Resource
  private RoleService roleService;
  @Resource
  private RedisUtil redisUtil;
  @Resource
  private RequestUtil requestUtil;
  @Resource
  private ConfigProperties config;
  @Resource
  private UserRefereeService userRefereeService;
  @Resource
  private CmsWebsiteService cmsWebsiteService;


  public WxLoginController(StringRedisTemplate redisTemplate, ObjectMapper objectMapper) {
    this.redisTemplate = redisTemplate;
    this.om = objectMapper;
  }

  @Operation(summary = "获取微信AccessToken")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/getAccessToken")
  public ApiResult<String> getMpAccessToken() {
    return success("操作成功", getAccessToken());
  }

  @Operation(summary = "获取微信openId")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/getOpenId")
  public ApiResult<LoginResult> getOpenId(@RequestBody UserParam userParam, HttpServletRequest request) {
    // 1.获取openid
    JSONObject result = getOpenIdByCode(userParam);
    String openid = result.getString("openid");
    String unionid = result.getString("unionid");
    if (openid == null) {
      return fail("获取openid失败", null);
    }
    // 2.通过openid查询用户是否已存在
    User user = userService.getByOauthId(userParam);
    // 3.存在则签发token并返回登录成功,不存在则注册新用户
    if (user == null) {
      user = addUser(userParam);
    }
    // 4.签发token
    loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_LOGIN, null, user.getTenantId(), request);
    String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
      configProperties.getTokenExpireTime(), configProperties.getTokenKey());
    return success("登录成功", new LoginResult(access_token, user));
  }

  @Operation(summary = "微信授权手机号码并登录")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/loginByMpWxPhone")
  public ApiResult<LoginResult> loginByMpWxPhone(@RequestBody UserParam userParam, HttpServletRequest request) {
    // 获取手机号码
    String phone = getPhoneByCode(userParam);
    if (phone == null) {
      String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
      redisTemplate.delete(key);
      throw new BusinessException("授权失败，请重试");
    }
    // 查询是否存在
    User user = userService.getByPhone(phone);
    // 不存在则注册
    if (user == null) {
      if ((userParam.getOpenid() == null || userParam.getOpenid().isEmpty()) && userParam.getAuthCode() != null) {
        UserParam userParam2 = new UserParam();
        userParam2.setCode(userParam.getAuthCode());
        JSONObject result = getOpenIdByCode(userParam2);
        String openid = result.getString("openid");
//                String unionid = result.getString("unionid");
        userParam.setOpenid(openid);
      }
      userParam.setPhone(phone);
      user = addUser(userParam);
      user.setRecommend(1);
    } else {
      // 存在则检查绑定上级
      if (userParam.getSceneType() != null && userParam.getSceneType().equals("save_referee") && userParam.getRefereeId() != null && userParam.getRefereeId() != 0) {
        UserReferee check = userRefereeService.check(user.getUserId(), userParam.getRefereeId());
        if (check == null) {
          UserReferee userReferee = new UserReferee();
          userReferee.setDealerId(userParam.getRefereeId());
          userReferee.setUserId(user.getUserId());
          userRefereeService.save(userReferee);
        }
      }
    }
    // 签发token
    String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
      configProperties.getTokenExpireTime(), configProperties.getTokenKey());
    loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REGISTER, null, user.getTenantId(), request);
    // 附加体育中心项目用户信息
//    user.setBookingUser();
    return success("登录成功", new LoginResult(access_token, user));
  }

  @Operation(summary = "微信授权手机号码并更新")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/updatePhoneByMpWx")
  public ApiResult<?> updatePhoneByMpWx(@RequestBody UserParam userParam) {
    // 获取微信授权手机号
    String phone = getPhoneByCode(userParam);
    // 查询当前用户
    User user = userService.getById(userParam.getUserId());
    if (user != null && phone != null) {
      user.setPhone(phone);
      userService.updateUser(user);
      return success("更新成功", phone);
    }
    return fail("更新失败");
  }

  /**
   * 新用户注册
   */
  private User addUser(UserParam userParam) {
    User addUser = new User();
    // 注册用户
    addUser.setStatus(0);
    addUser.setUsername(createUsername("wx_"));
    addUser.setNickname("微信用户");
    addUser.setPlatform(MP_WEIXIN);
    addUser.setGradeId(2);
    if (userParam.getGradeId() != null) {
      addUser.setGradeId(userParam.getGradeId());
    }
    if (userParam.getPhone() != null) {
      addUser.setPhone(userParam.getPhone());
    }
    if (StrUtil.isNotBlank(userParam.getOpenid())) {
      addUser.setOpenid(userParam.getOpenid());
    }
    if (StrUtil.isNotBlank(userParam.getUnionid())) {
      addUser.setUnionid(userParam.getUnionid());
    }
    addUser.setPassword(userService.encodePassword(CommonUtil.randomUUID16()));
    addUser.setTenantId(getTenantId());
    addUser.setRecommend(1);
    Role role = roleService.getOne(new QueryWrapper<Role>().eq("role_code", "user"), false);
    addUser.setRoleId(role.getRoleId());
    if (userService.saveUser(addUser)) {
      // 添加用户角色
      final UserRole userRole = new UserRole();
      userRole.setUserId(addUser.getUserId());
      userRole.setTenantId(addUser.getTenantId());
      userRole.setRoleId(addUser.getRoleId());
      userRoleService.save(userRole);
    }
    // 绑定关系
    if (userParam.getSceneType() != null && userParam.getSceneType().equals("save_referee") && userParam.getRefereeId() != null && userParam.getRefereeId() != 0) {
      UserReferee check = userRefereeService.check(addUser.getUserId(), userParam.getRefereeId());
      if (check == null) {
        UserReferee userReferee = new UserReferee();
        userReferee.setDealerId(userParam.getRefereeId());
        userReferee.setUserId(addUser.getUserId());
        userRefereeService.save(userReferee);
      }
    }
    return addUser;
  }

  // 获取openid
  private JSONObject getOpenIdByCode(UserParam userParam) {
    // 从缓存获取微信小程序配置信息
    JSONObject setting = getWxConfigFromCache(getTenantId());
    // 获取openId
    String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code";
    // 执行get请求
    String result = HttpUtil.get(apiUrl);
    // 解析access_token
    return JSON.parseObject(result);
  }

  /**
   * 获取微信手机号码
   *
   * @param userParam 需要传微信凭证code
   */
  private String getPhoneByCode(UserParam userParam) {
    // 获取手机号码
    String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken();
    HashMap<String, Object> paramMap = new HashMap<>();
    if (StrUtil.isBlank(userParam.getCode())) {
      throw new BusinessException("code不能为空");
    }
    paramMap.put("code", userParam.getCode());
    // 执行post请求
    String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap));
    JSONObject json = JSON.parseObject(post);
    if (json.get("errcode").equals(0)) {
      JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info"));
      // 微信用户的手机号码
      final String phoneNumber = phoneInfo.getString("phoneNumber");
      // 验证手机号码
//            if (userParam.getNotVerifyPhone() == null && !Validator.isMobile(phoneNumber)) {
//                String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
//                redisTemplate.delete(key);
//                throw new BusinessException("手机号码格式不正确");
//            }
      return phoneNumber;
    }
    return null;
  }

  /**
   * 生成随机账号
   *
   * @return username
   */
  private String createUsername(String type) {
    return type.concat(RandomUtil.randomString(12));
  }

  /**
   * 获取接口调用凭据AccessToken
   * <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html">...</a>
   */
  public String getAccessToken() {
    Integer tenantId = getTenantId();
    String key = ACCESS_TOKEN_KEY.concat(":").concat(tenantId.toString());

    // 从缓存获取微信小程序配置信息
    JSONObject setting = getWxConfigFromCache(tenantId);
    if (setting == null) {
      throw new BusinessException("请先配置小程序");
    }

    // 从缓存获取access_token
    String value = redisTemplate.opsForValue().get(key);
    if (value != null) {
      // 解析access_token
      JSONObject response = JSON.parseObject(value);
      String accessToken = response.getString("access_token");
      if (accessToken != null) {
        return accessToken;
      }
    }

    // 微信获取凭证接口
    String apiUrl = "https://api.weixin.qq.com/cgi-bin/token";
    // 组装url参数
    String url = apiUrl.concat("?grant_type=client_credential")
      .concat("&appid=").concat(setting.getString("appId"))
      .concat("&secret=").concat(setting.getString("appSecret"));

    // 执行get请求
    String result = HttpUtil.get(url);
    // 解析access_token
    JSONObject response = JSON.parseObject(result);
    if (response.getString("access_token") != null) {
      // 存入缓存
      redisTemplate.opsForValue().set(key, result, 7000L, TimeUnit.SECONDS);
      return response.getString("access_token");
    }
    throw new BusinessException("小程序配置不正确");
  }

  @Operation(summary = "获取微信openId并更新")
  @PostMapping("/getWxOpenId")
  public ApiResult<?> getWxOpenId(@RequestBody UserParam userParam) {
    final User loginUser = getLoginUser();
    if (loginUser == null) {
      return fail("请先登录");
    }
    // 已存在直接返回
    if (StrUtil.isNotBlank(loginUser.getOpenid())) {
      return success(loginUser);
    }
    // 请求微信接口获取openid
    String apiUrl = "https://api.weixin.qq.com/sns/jscode2session";
    final HashMap<String, Object> map = new HashMap<>();
    final JSONObject setting = getWxConfigFromCache(getTenantId());
    final String appId = setting.getString("appId");
    final String appSecret = setting.getString("appSecret");
    map.put("appid", appId);
    map.put("secret", appSecret);
    map.put("js_code", userParam.getCode());
    map.put("grant_type", "authorization_code");
    final String response = HttpUtil.get(apiUrl, map);
    final JSONObject jsonObject = JSONObject.parseObject(response);
    String openid = jsonObject.getString("openid");
    String sessionKey = jsonObject.getString("session_key");
    String unionid = jsonObject.getString("unionid");
    // 保存openID
    if (loginUser.getOpenid() == null || StrUtil.isBlank(loginUser.getOpenid())) {
      loginUser.setOpenid(openid);
      loginUser.setUnionid(unionid);
      requestUtil.updateUser(loginUser);
//            userService.updateById(loginUser);
    }
    return success("获取成功", jsonObject);
  }

  @Operation(summary = "仅获取微信openId")
  @PostMapping("/getWxOpenIdOnly")
  public ApiResult<?> getWxOpenIdOnly(@RequestBody UserParam userParam) {

    String apiUrl = "https://api.weixin.qq.com/sns/jscode2session";
    final HashMap<String, Object> map = new HashMap<>();
    final JSONObject setting = getWxConfigFromCache(getTenantId());
    final String appId = setting.getString("appId");
    final String appSecret = setting.getString("appSecret");
    map.put("appid", appId);
    map.put("secret", appSecret);
    map.put("js_code", userParam.getCode());
    map.put("grant_type", "authorization_code");
    final String response = HttpUtil.get(apiUrl, map);
    final JSONObject jsonObject = JSONObject.parseObject(response);
    return success("获取成功", jsonObject);
  }

  @Operation(summary = "获取微信小程序码-用户ID")
  @GetMapping("/getUserQRCode")
  public ApiResult<?> getQRCode() {
    String apiUrl = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + getAccessToken();
    final HashMap<String, Object> map = new HashMap<>();
    map.put("path", "/package/user/qrcode?user_id=" + getLoginUserId());
//    map.put("env_version","trial");
    // 获取图片 Buffer
    byte[] qrCode = HttpRequest.post(apiUrl)
      .body(JSON.toJSONString(map))
      .execute().bodyBytes();

    // 保存的文件名称
    final String fileName = CommonUtil.randomUUID8().concat(".png");
    // 保存路径
    String filePath = getUploadDir().concat("qrcode/") + fileName;
    File file = FileUtil.writeBytes(qrCode, filePath);
    if (file != null) {
      return success(config.getFileServer().concat("/qrcode/").concat(fileName));
    }
    return fail("获取失败", null);
  }

  @Operation(summary = "获取微信小程序码-订单核销码")
  @GetMapping("/getOrderQRCode/{orderNo}")
  public ApiResult<?> getOrderQRCode(@PathVariable("orderNo") String orderNo) {
    String apiUrl = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + getAccessToken();
    final HashMap<String, Object> map = new HashMap<>();
    map.put("path", "/package/admin/order-scan?orderNo=".concat(orderNo));
    map.put("env_version", "release");
    // 获取图片 Buffer
    byte[] qrCode = HttpRequest.post(apiUrl)
      .body(JSON.toJSONString(map))
      .execute().bodyBytes();

    // 保存的文件名称
    final String fileName = CommonUtil.randomUUID8().concat(".png");
    // 保存路径
    String filePath = getUploadDir().concat("qrcode/") + fileName;
    File file = FileUtil.writeBytes(qrCode, filePath);
    if (file != null) {
      return success(config.getFileServer().concat("/qrcode/").concat(fileName));
    }
    return fail("获取失败", null);
  }

  @Operation(summary = "获取微信小程序码-订单核销码-数量极多的业务场景")
  @GetMapping("/getOrderQRCodeUnlimited/{scene}")
  public void getOrderQRCodeUnlimited(@PathVariable("scene") String scene, HttpServletResponse response) throws IOException {
    try {
      // 从scene参数中解析租户ID
      System.out.println("scene = " + scene);
      Integer tenantId = extractTenantIdFromScene(scene);
      System.out.println("从scene参数中解析租户ID = " + tenantId);
      if (tenantId == null) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().write("{\"error\":\"无法从scene参数中获取租户信息\"}");
        return;
      }

      // 使用指定租户ID获取 access_token
      String accessToken = getAccessTokenForTenant(tenantId);
      String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;

      final HashMap<String, Object> map = new HashMap<>();
      map.put("scene", scene);
      map.put("page", "pages/index/index");
      map.put("env_version", "release");

      // 判断应用运行状态
      final CmsWebsite website = cmsWebsiteService.getByTenantId(tenantId);
      if(website != null && Integer.valueOf(2).equals(website.getRunning())){
        map.put("check_path",false);
        map.put("env_version","trial");
      }

      String jsonBody = JSON.toJSONString(map);
      System.out.println("请求的 JSON body = " + jsonBody);

      // 获取微信 API 响应
      cn.hutool.http.HttpResponse httpResponse = HttpRequest.post(apiUrl)
        .body(jsonBody)
        .execute();

      byte[] responseBytes = httpResponse.bodyBytes();
      String contentType = httpResponse.header("Content-Type");

      // 检查响应内容类型，判断是否为错误响应
      if (contentType != null && contentType.contains("application/json")) {
        // 微信返回了错误信息（JSON格式）
        String errorResponse = new String(responseBytes, "UTF-8");
        System.err.println("微信 API 错误响应: " + errorResponse);

        // 返回错误信息给前端
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().write(errorResponse);
        return;
      }

      // 成功获取二维码图片
      response.setContentType("image/png");
      response.setHeader("Cache-Control", "no-cache");
      response.setHeader("Content-Disposition", "inline; filename=qrcode.png");

      // 输出图片
      response.getOutputStream().write(responseBytes);
      System.out.println("二维码生成成功，大小: " + responseBytes.length + " bytes");

    } catch (Exception e) {
      System.err.println("生成二维码失败: " + e.getMessage());
      e.printStackTrace();

      // 返回错误信息
      response.setContentType("application/json;charset=UTF-8");
      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.getWriter().write("{\"error\":\"生成二维码失败: " + e.getMessage() + "\"}");
    }
  }

  @Operation(summary = "获取微信小程序码-用户ID")
  @GetMapping("/getQRCodeText")
  public byte[] getQRCodeText(String scene, String page, Integer width,
                              Boolean isHyaline, String envVersion) throws IOException {
    HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/wxa/getwxacodeunlimit")
      .newBuilder()
      .addQueryParameter("access_token", getLocalAccessToken())
      .build();

    System.out.println("page = " + page);
    // 构造请求 JSON
    // 注意：scene 仅支持可见字符，长度上限 32，尽量 URL-safe（字母数字下划线等）
    // page 必须是已发布小程序内的路径（不带开头斜杠也可）
    var root = om.createObjectNode();
    root.put("scene", scene);
    if (page != null) root.put("page", page);
    if (width != null) root.put("width", width); // 默认 430，建议 280~1280
    if (isHyaline != null) root.put("is_hyaline", isHyaline);
    if (envVersion != null) root.put("env_version", envVersion); // release/trial/develop

    okhttp3.RequestBody reqBody = okhttp3.RequestBody.create(
      root.toString(), MediaType.parse("application/json; charset=utf-8"));
    Request req = new Request.Builder().url(url).post(reqBody).build();

    try (Response resp = http.newCall(req).execute()) {
      if (!resp.isSuccessful()) {
        throw new IOException("HTTP " + resp.code() + " calling getwxacodeunlimit");
      }
      MediaType ct = resp.body().contentType();
      byte[] bytes = resp.body().bytes();
      // 微信出错时返回 JSON，需要识别一下
      if (ct != null && ct.subtype() != null && ct.subtype().contains("json")) {
        String err = new String(bytes);
        throw new IOException("WeChat error: " + err);
      }
      return bytes; // 成功就是图片二进制（PNG）
    }
  }

  /**
   * 获取/刷新 access_token
   */
  public String getLocalAccessToken() throws IOException {
    long now = Instant.now().getEpochSecond();
    String key = "AccessToken:Local:" + getTenantId();

    // 从缓存获取access_token，使用JSON格式
    String value = redisUtil.get(key);
    if (value != null && now < tokenExpireEpoch - 60) {
      try {
        // 尝试解析为JSON格式
        JSONObject response = JSON.parseObject(value);
        String accessToken = response.getString("access_token");
        if (accessToken != null) {
          System.out.println("从缓存获取到access_token(Local): " + accessToken.substring(0, Math.min(10, accessToken.length())) + "...");
          return accessToken;
        }
      } catch (Exception e) {
        // 如果解析失败，可能是旧格式的纯字符串token
        System.out.println("本地缓存token格式异常，使用原值: " + e.getMessage());
        return value;
      }
    }

    // 从缓存获取微信小程序配置信息
    Integer tenantId = getTenantId();
    JSONObject setting = getWxConfigFromCache(tenantId);
    if (setting == null) {
      throw new IOException("请先配置小程序");
    }

    String appId = setting.getString("appId");
    String appSecret = setting.getString("appSecret");

    if (appId == null || appSecret == null) {
      throw new IOException("小程序配置不完整，缺少 appId 或 appSecret");
    }

    HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token")
      .newBuilder()
      .addQueryParameter("grant_type", "client_credential")
      .addQueryParameter("appid", appId)
      .addQueryParameter("secret", appSecret)
      .build();

    Request req = new Request.Builder().url(url).get().build();
    try (Response resp = http.newCall(req).execute()) {
      String body = resp.body().string();
      JsonNode json = om.readTree(body);
      if (json.has("access_token")) {
        String token = json.get("access_token").asText();
        long expiresIn = json.get("expires_in").asInt(7200);

        // 缓存完整的JSON响应，与其他方法保持一致
        redisUtil.set(key, body, expiresIn, TimeUnit.SECONDS);
        tokenExpireEpoch = now + expiresIn;
        System.out.println("获取新的access_token成功(Local)，租户ID: " + tenantId);
        return token;
      } else {
        throw new IOException("Get access_token failed: " + body);
      }
    }
  }

  /**
   * 文件上传位置(服务器)
   */
  private String getUploadDir() {
    return config.getUploadPath();
  }

  @Operation(summary = "调试：检查微信小程序配置")
  @GetMapping("/debug/checkWxConfig")
  public ApiResult<?> debugCheckWxConfig() {
    Integer tenantId = getTenantId();
    Map<String, Object> result = new HashMap<>();
    result.put("tenantId", tenantId);

    try {
      // 尝试从缓存获取配置
      JSONObject setting = getWxConfigFromCache(tenantId);
      result.put("hasConfig", true);
      result.put("config", setting);
      result.put("cacheKey", MP_WX_KEY + tenantId);
    } catch (Exception e) {
      result.put("hasConfig", false);
      result.put("error", e.getMessage());
      result.put("cacheKey", MP_WX_KEY + tenantId);

      // 提供创建配置的建议
      Map<String, Object> suggestion = new HashMap<>();
      suggestion.put("message", "请在Redis中创建微信小程序配置");
      suggestion.put("cacheKey", MP_WX_KEY + tenantId);
      suggestion.put("tenantId", tenantId);
      suggestion.put("sampleConfig", createSampleWxConfig());
      result.put("suggestion", suggestion);
    }

    return success("配置检查完成", result);
  }

  @Operation(summary = "调试：创建示例微信小程序配置")
  @PostMapping("/debug/createSampleWxConfig")
  public ApiResult<?> debugCreateSampleWxConfig(@RequestBody Map<String, String> params) {
    Integer tenantId = getTenantId();

    String appId = params.get("appId");
    String appSecret = params.get("appSecret");

    if (appId == null || appSecret == null) {
      return fail("请提供 appId 和 appSecret", null);
    }

    try {
      // 直接在Redis中创建配置
      String key = MP_WX_KEY + tenantId;

      // 创建配置内容
      Map<String, String> config = new HashMap<>();
      config.put("appId", appId);
      config.put("appSecret", appSecret);
      config.put("tenantId", tenantId.toString());
      config.put("settingKey", "mp-weixin");
      config.put("settingId", "301");

      // 保存到Redis缓存
      redisUtil.set(key, JSON.toJSONString(config));

      return success("微信小程序配置创建成功", config);
    } catch (Exception e) {
      return fail("创建配置失败: " + e.getMessage(), null);
    }
  }

  private Map<String, String> createSampleWxConfig() {
    Map<String, String> sample = new HashMap<>();
    sample.put("appId", "wx_your_app_id_here");
    sample.put("appSecret", "your_app_secret_here");
    return sample;
  }

  @Operation(summary = "调试：获取AccessToken")
  @GetMapping("/debug/getAccessToken")
  public ApiResult<?> debugGetAccessToken() {
    try {
      // 获取当前线程的租户ID
      Integer tenantId = getTenantId();
      if (tenantId == null) {
        tenantId = 10550; // 默认租户
      }

      System.out.println("=== 开始调试获取AccessToken，租户ID: " + tenantId + " ===");

      // 手动调用获取AccessToken
      String accessToken = getAccessTokenForTenant(tenantId);

      String result = "获取AccessToken成功: " + (accessToken != null ? accessToken.substring(0, Math.min(10, accessToken.length())) + "..." : "null");
      System.out.println("调试结果: " + result);

      return success(result);
    } catch (Exception e) {
      System.err.println("调试获取AccessToken异常: " + e.getMessage());
      e.printStackTrace();
      return fail("获取AccessToken失败: " + e.getMessage());
    }
  }

  /**
   * 从Redis缓存中获取微信小程序配置
   * @param tenantId 租户ID
   * @return 微信配置信息
   */
  private JSONObject getWxConfigFromCache(Integer tenantId) {
    String key = MP_WX_KEY + tenantId;
    String cacheValue = redisUtil.get(key);
    if (StrUtil.isBlank(cacheValue)) {
      throw new BusinessException("未找到微信小程序配置，请检查缓存key: " + key);
    }
    try {
      return JSON.parseObject(cacheValue);
    } catch (Exception e) {
      throw new BusinessException("微信小程序配置格式错误: " + e.getMessage());
    }
  }

  /**
   * 从scene参数中提取租户ID
   * scene格式: uid_userId_tenantId（优先）或 uid_userId（兼容旧格式）
   */
  private Integer extractTenantIdFromScene(String scene) {
    try {
      System.out.println("解析scene参数: " + scene);

      if (scene != null && scene.startsWith("uid_")) {
        String content = scene.substring(4); // 去掉"uid_"前缀

        // 优先解析 uid_userId_tenantId 格式
        String[] parts = content.split("_");
        if (parts.length >= 2) {
          try {
            Integer tenantId = Integer.parseInt(parts[1]);
            System.out.println("从scene直接解析到tenantId = " + tenantId);
            return tenantId;
          } catch (NumberFormatException e) {
            System.err.println("scene中tenantId格式异常: " + parts[1]);
          }
        }

        // 兼容旧格式 uid_userId：根据用户ID查询租户ID
        if (parts.length == 1) {
          Integer userId = Integer.parseInt(parts[0]);
          System.out.println("userId = " + userId);
          try {
            List<User> users = userService.listByIdIgnoreTenant(userId);
            System.out.println("查询到用户数量 = " + (users != null ? users.size() : 0));
            if (users != null && !users.isEmpty()) {
              System.out.println("从用户ID " + userId + " 获取到租户ID: " + users.get(0).getTenantId());
              return users.get(0).getTenantId();
            } else {
              System.err.println("未找到用户ID: " + userId);
            }
          } catch (Exception ex) {
            System.err.println("查询用户异常: " + ex.getMessage());
          }
        }
      }

      // 如果无法解析，默认使用租户10550
      System.out.println("无法解析scene参数，使用默认租户ID: 10550");
      return 10550;

    } catch (Exception e) {
      System.err.println("解析scene参数异常: " + e.getMessage());
      // 出现异常时，默认使用租户10550
      return 10550;
    }
  }

  /**
   * 为指定租户获取AccessToken
   */
  private String getAccessTokenForTenant(Integer tenantId) {
    try {
      String key = ACCESS_TOKEN_KEY.concat(":").concat(tenantId.toString());

      // 从缓存获取access_token
      String value = redisUtil.get(key);
      if (value != null) {
        try {
          // 尝试解析为JSON格式（与getAccessToken方法保持一致）
          JSONObject response = JSON.parseObject(value);
          String accessToken = response.getString("access_token");
          if (accessToken != null) {
            System.out.println("从缓存获取到access_token: " + accessToken.substring(0, Math.min(10, accessToken.length())) + "...");
            return accessToken;
          }
        } catch (Exception e) {
          // 如果解析失败，可能是旧格式的纯字符串token，直接返回
          System.out.println("缓存token格式异常，使用原值: " + e.getMessage());
          return value;
        }
      }

      // 缓存中没有，重新获取
      JSONObject wxConfig = getWxConfigFromCache(tenantId);
      String appId = wxConfig.getString("appId");
      String appSecret = wxConfig.getString("appSecret");

      String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
      System.out.println("调用微信API获取token - 租户ID: " + tenantId + ", AppID: " + (appId != null ? appId.substring(0, Math.min(8, appId.length())) + "..." : "null"));
      System.out.println("微信API请求URL: " + apiUrl.replaceAll("secret=[^&]*", "secret=***"));
      String result = HttpUtil.get(apiUrl);
      System.out.println("微信API响应: " + result);
      JSONObject json = JSON.parseObject(result);

      // 检查是否有错误
      if (json.containsKey("errcode")) {
        Integer errcode = json.getInteger("errcode");
        String errmsg = json.getString("errmsg");
        System.err.println("微信API错误 - errcode: " + errcode + ", errmsg: " + errmsg);

        if (errcode == 40125) {
          throw new RuntimeException("微信AppSecret配置错误，请检查并更新正确的AppSecret");
        } else if (errcode == 40013) {
          throw new RuntimeException("微信AppID配置错误，请检查并更新正确的AppID");
        } else {
          throw new RuntimeException("微信API调用失败: " + errmsg + " (errcode: " + errcode + ")");
        }
      }

      if (json.containsKey("access_token")) {
        String accessToken = json.getString("access_token");
        Integer expiresIn = json.getInteger("expires_in");

        // 缓存access_token，存储完整JSON响应（与getAccessToken方法保持一致）
        redisUtil.set(key, result, (long) (expiresIn - 300), TimeUnit.SECONDS);

        System.out.println("获取新的access_token成功，租户ID: " + tenantId);
        return accessToken;
      } else {
        throw new RuntimeException("获取access_token失败: " + result);
      }

    } catch (Exception e) {
      System.err.println("获取access_token异常，租户ID: " + tenantId + ", 错误: " + e.getMessage());
      throw new RuntimeException("获取access_token失败: " + e.getMessage());
    }
  }

}
