package com.ruoyi.web.controller.common;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.erp.request.IERPAccount;
import com.ruoyi.jarvis.service.IErpGoofishOrderService;
import com.ruoyi.jarvis.service.erp.ErpAccountResolver;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 闲管家开放平台推送回调（请在开放平台填写真实 URL）
 * 订单：POST .../open/callback/order/receive?appid=&timestamp=&sign=
 * <p>
 * 成功/失败体须与《订单推送通知》OpenAPI 一致：{@code result=success|fail} + {@code msg}；
 * 仅当 {@code result} 为 success 时平台认为接收成功（失败最多重试 3 次；建议业务异步处理、快速返回）。
 */
@Anonymous
@RestController
@RequestMapping("/open/callback")
public class OpenCallbackController {

    @Resource
    private ErpAccountResolver erpAccountResolver;

    @Resource
    private IErpGoofishOrderService erpGoofishOrderService;

    @PostMapping("/product/receive")
    public JSONObject receiveProductCallback(
            @RequestParam("appid") String appid,
            @RequestParam(value = "timestamp", required = false) Long timestamp,
            @RequestParam(value = "seller_id", required = false) Long sellerId,
            @RequestParam("sign") String sign,
            @RequestBody(required = false) String rawBody
    ) {
        String normalizedBody = normalizeJsonBody(rawBody);
        IERPAccount account = erpAccountResolver.resolveStrict(appid);
        if (!verifyGoofishSign(account, timestamp, sellerId, sign, normalizedBody)) {
            return failCallback("签名失败");
        }
        return successCallback();
    }

    @PostMapping("/order/receive")
    public JSONObject receiveOrderCallback(
            @RequestParam("appid") String appid,
            @RequestParam(value = "timestamp", required = false) Long timestamp,
            @RequestParam(value = "seller_id", required = false) Long sellerId,
            @RequestParam("sign") String sign,
            @RequestBody(required = false) String rawBody
    ) {
        String normalizedBody = normalizeJsonBody(rawBody);
        IERPAccount account = erpAccountResolver.resolveStrict(appid);
        if (account == null) {
            return failCallback("未找到启用的 AppKey 配置");
        }
        if (!verifyGoofishSign(account, timestamp, sellerId, sign, normalizedBody)) {
            return failCallback("签名失败");
        }
        JSONObject body;
        try {
            body = "{}".equals(normalizedBody) ? new JSONObject() : JSON.parseObject(normalizedBody);
        } catch (Exception e) {
            return failCallback("请求体不是合法JSON");
        }
        try {
            erpGoofishOrderService.publishOrProcessNotify(appid, timestamp, body);
        } catch (Exception e) {
            return failCallback("入队异常");
        }
        return successCallback();
    }

    /**
     * 与开放平台示例一致：签名字符串 = md5(appKey + "," + md5(body原文) + "," + timestamp + [,sellerId] + "," + appSecret)
     * body 须与推送原文完全一致后做 MD5，不能用解析后再 toJSONString（字段顺序变化会导致验签失败）。
     */
    private boolean verifyGoofishSign(IERPAccount account, Long timestamp, Long sellerId, String sign, String bodyExactForMd5) {
        if (account == null || StringUtils.isEmpty(sign)) {
            return false;
        }
        String jsonForMd5 = bodyExactForMd5 == null || bodyExactForMd5.isEmpty() ? "{}" : bodyExactForMd5;
        String bodyMd5 = md5Hex(jsonForMd5);
        long ts = timestamp == null ? 0L : timestamp;
        String data;
        if (sellerId != null) {
            data = account.getApiKey() + "," + bodyMd5 + "," + ts + "," + sellerId + "," + account.getApiKeySecret();
        } else {
            data = account.getApiKey() + "," + bodyMd5 + "," + ts + "," + account.getApiKeySecret();
        }
        String local = md5Hex(data);
        return StringUtils.equalsIgnoreCase(local, sign.trim());
    }

    private static String normalizeJsonBody(String rawBody) {
        if (rawBody == null) {
            return "{}";
        }
        String t = rawBody.trim();
        return t.isEmpty() ? "{}" : t;
    }

    /** 与《订单推送通知》notify_resp_ok 一致：result=success 时平台才停止重试 */
    private static JSONObject successCallback() {
        JSONObject ok = new JSONObject();
        ok.put("result", "success");
        ok.put("msg", "接收成功");
        return ok;
    }

    /** 与 notify_resp_fail 一致：result=fail */
    private static JSONObject failCallback(String msg) {
        JSONObject j = new JSONObject();
        j.put("result", "fail");
        j.put("msg", msg == null || msg.isEmpty() ? "处理失败" : msg);
        return j;
    }

    private String md5Hex(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(str.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b & 0xff));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}
