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=
 * 响应须与平台「强校验」一致：一般 {@code {"code":0,"msg":"OK","data":{}}} ，勿使用 {@code result} 等非标准字段。
 */
@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(1, "签名失败");
        }
        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(1, "未找到启用的 AppKey 配置");
        }
        if (!verifyGoofishSign(account, timestamp, sellerId, sign, normalizedBody)) {
            return failCallback(1, "签名失败");
        }
        JSONObject body;
        try {
            body = "{}".equals(normalizedBody) ? new JSONObject() : JSON.parseObject(normalizedBody);
        } catch (Exception e) {
            return failCallback(2, "请求体不是合法JSON");
        }
        try {
            erpGoofishOrderService.publishOrProcessNotify(appid, timestamp, body);
        } catch (Exception e) {
            return failCallback(3, "入队异常");
        }
        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;
    }

    /** 与平台开放接口成功响应字段类型对齐：code 为数值、msg 为字符串、data 为对象 */
    private static JSONObject successCallback() {
        JSONObject ok = new JSONObject();
        ok.put("code", 0);
        ok.put("msg", "OK");
        ok.put("data", new JSONObject());
        return ok;
    }

    private static JSONObject failCallback(int code, String msg) {
        JSONObject j = new JSONObject();
        j.put("code", code);
        j.put("msg", msg == null ? "fail" : msg);
        j.put("data", new JSONObject());
        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);
        }
    }
}
