package com.gxwebsoft.glt.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.service.GltSubscribeMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

import static com.gxwebsoft.common.core.constants.RedisConstants.*;

/**
 * 微信订阅消息服务实现
 *
 * <p>功能：
 * <ul>
 *   <li>新订单通知配送员</li>
 *   <li>订单状态变更通知</li>
 * </ul>
 * </p>
 */
@Slf4j
@Service
public class GltSubscribeMessageServiceImpl implements GltSubscribeMessageService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 订阅消息模板ID（需在微信公众平台配置）
     * 模板名称：订单配送通知
     * 关键词：订单编号、订单内容、配送地址、订单金额
     */
    private static final String SUBSCRIBE_TEMPLATE_ID = "YOUR_TEMPLATE_ID"; // TODO: 替换为实际模板ID

    /**
     * 发送新订单通知给配送员
     */
    @Override
    public boolean sendNewOrderNotice(GltTicketOrder order, String riderOpenId, Integer tenantId) {
        if (order == null || StrUtil.isBlank(riderOpenId) || tenantId == null) {
            log.warn("发送订阅消息参数不完整");
            return false;
        }

        try {
            String accessToken = getAccessToken(tenantId);
            if (StrUtil.isBlank(accessToken)) {
                log.warn("获取access_token失败");
                return false;
            }

            // 构建消息内容
            Map<String, Object> data = new HashMap<>();
            data.put("phrase1", Map.of("value", "待配送"));  // 订单状态
            data.put("character_string2", Map.of("value", String.valueOf(order.getId()))); // 订单编号
            data.put("thing3", Map.of("value", truncateStr(order.getAddress(), 20))); // 配送地址
            data.put("number4", Map.of("value", String.valueOf(order.getTotalNum()))); // 商品数量
            data.put("time5", Map.of("value", formatTime(order.getSendTime()))); // 期望送达时间

            // 发送订阅消息
            return sendSubscribeMessage(accessToken, riderOpenId, data);
        } catch (Exception e) {
            log.error("发送新订单订阅消息失败 - orderId={}, riderOpenId={}, error={}",
                    order.getId(), riderOpenId, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 发送订单状态变更通知
     */
    @Override
    public boolean sendOrderStatusNotice(GltTicketOrder order, String riderOpenId, String statusText, Integer tenantId) {
        if (order == null || StrUtil.isBlank(riderOpenId) || tenantId == null) {
            log.warn("发送订阅消息参数不完整");
            return false;
        }

        try {
            String accessToken = getAccessToken(tenantId);
            if (StrUtil.isBlank(accessToken)) {
                log.warn("获取access_token失败");
                return false;
            }

            // 构建消息内容
            Map<String, Object> data = new HashMap<>();
            data.put("phrase1", Map.of("value", truncateStr(statusText, 5))); // 状态描述
            data.put("character_string2", Map.of("value", String.valueOf(order.getId()))); // 订单编号
            data.put("time3", Map.of("value", formatTime(null))); // 通知时间

            // 发送订阅消息
            return sendSubscribeMessage(accessToken, riderOpenId, data);
        } catch (Exception e) {
            log.error("发送订单状态变更订阅消息失败 - orderId={}, riderOpenId={}, error={}",
                    order.getId(), riderOpenId, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取小程序的 access_token
     */
    private String getAccessToken(Integer tenantId) {
        if (tenantId == null) {
            throw new BusinessException("tenantId 不能为空");
        }

        final String tokenCacheKey = ACCESS_TOKEN_KEY + ":" + tenantId;

        // 1) 优先从缓存取
        String cachedValue = stringRedisTemplate.opsForValue().get(tokenCacheKey);
        if (StrUtil.isNotBlank(cachedValue)) {
            try {
                JSONObject cachedJson = JSON.parseObject(cachedValue);
                String accessToken = cachedJson.getString("access_token");
                if (StrUtil.isNotBlank(accessToken)) {
                    return accessToken;
                }
            } catch (Exception ignore) {
                // 旧格式：直接存 token
                return cachedValue;
            }
        }

        // 2) 缓存没有则从租户配置获取 appId/appSecret
        final String wxConfigKey = MP_WX_KEY + tenantId;
        final String wxConfigValue = stringRedisTemplate.opsForValue().get(wxConfigKey);
        if (StrUtil.isBlank(wxConfigValue)) {
            log.warn("未找到微信小程序配置，请检查缓存key: {}", wxConfigKey);
            return null;
        }

        JSONObject wxConfig;
        try {
            wxConfig = JSON.parseObject(wxConfigValue);
        } catch (Exception e) {
            log.error("微信小程序配置格式错误: {}", e.getMessage());
            return null;
        }

        final String appId = wxConfig.getString("appId");
        final String appSecret = wxConfig.getString("appSecret");
        if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
            log.error("微信小程序配置不完整(appId/appSecret)");
            return null;
        }

        // 3) 调用微信接口获取 token
        final String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
                + "&appid=" + appId + "&secret=" + appSecret;
        String result = HttpUtil.get(apiUrl);

        JSONObject json = JSON.parseObject(result);
        if (json.containsKey("errcode") && json.getIntValue("errcode") != 0) {
            log.error("获取小程序access_token失败: {}", json.getString("errmsg"));
            return null;
        }

        String accessToken = json.getString("access_token");
        Integer expiresIn = json.getInteger("expires_in");
        if (StrUtil.isBlank(accessToken)) {
            log.error("获取小程序access_token失败: access_token为空");
            return null;
        }

        // 4) 缓存，提前5分钟过期
        long ttlSeconds = 7000L;
        if (expiresIn != null && expiresIn > 300) {
            ttlSeconds = expiresIn - 300L;
        }
        stringRedisTemplate.opsForValue().set(tokenCacheKey, result, ttlSeconds, java.util.concurrent.TimeUnit.SECONDS);

        log.info("获取小程序access_token成功 - tenantId={}, ttlSeconds={}", tenantId, ttlSeconds);
        return accessToken;
    }

    /**
     * 发送订阅消息
     */
    private boolean sendSubscribeMessage(String accessToken, String openId, Map<String, Object> data) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;

        Map<String, Object> params = new HashMap<>();
        params.put("touser", openId);           // 用户 openid
        params.put("template_id", SUBSCRIBE_TEMPLATE_ID); // 模板ID
        params.put("page", "pages/rider/orders/index");    // 点击后跳转的页面
        params.put("data", data);

        String response = HttpUtil.createPost(url)
                .contentType("application/json")
                .body(JSON.toJSONString(params))
                .timeout(10000)
                .execute()
                .body();

        JSONObject result = JSON.parseObject(response);
        int errcode = result.getIntValue("errcode");

        if (errcode == 0) {
            log.info("订阅消息发送成功 - openId={}", openId);
            return true;
        } else {
            log.warn("订阅消息发送失败 - openId={}, errcode={}, errmsg={}",
                    openId, errcode, result.getString("errmsg"));
            return false;
        }
    }

    /**
     * 截断字符串
     */
    private String truncateStr(String str, int maxLen) {
        if (str == null) return "";
        return str.length() > maxLen ? str.substring(0, maxLen) : str;
    }

    /**
     * 格式化时间
     */
    private String formatTime(String timeStr) {
        if (StrUtil.isBlank(timeStr)) {
            return cn.hutool.core.date.DateUtil.now();
        }
        return timeStr;
    }
}
