package com.gzzm.lobster.api;

import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.identity.UserContextHolder;
import com.gzzm.platform.commons.Tools;
import net.cyan.arachne.PageInterceptor;
import net.cyan.arachne.RequestContext;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * LobsterApiPageInterceptor —— 大龙虾 API 异常 → 结构化响应 /
 * Translates uncaught exceptions on lobster API endpoints into a uniform
 * {@code {code, message, data}} response body + appropriate HTTP status.
 *
 * <p><b>动机</b>：zmeg_new 默认全局 {@code SystemExceptionHandler} 把所有未知异常的消息
 * 吞成 "操作失败"，前端调试时根本看不到真正的根因。本拦截器只对 {@code com.gzzm.lobster.*}
 * 包下的 controller 生效，把 {@link LobsterException} / {@link IllegalArgumentException} /
 * {@link IllegalStateException}（含 UserContext 缺失）等常见异常映射成可读的 JSON：
 *
 * <pre>
 * { "code": "auth.required",
 *   "message": "未登录或会话已过期",
 *   "data": null }
 * </pre>
 *
 * <p>同时设置 4xx/5xx HTTP 状态码，让前端 {@code requestJson} 的 {@code resp.ok} 判定
 * 自动走错误分支，无需每个 caller 自己识别 "操作失败" 字符串.
 *
 * <p>注册：{@code web/WEB-INF/arachne/lobster.xml}（arachne 主配置 {@code <config path="arachne"/>}
 * 自动扫加载）.
 */
public class LobsterApiPageInterceptor implements PageInterceptor {

    @Override
    public boolean accept(RequestContext context) {
        Class<?> pageClass = context.getPageClass();
        if (pageClass == null) return false;
        // 仅本工程 controller —— zmeg_new 内部 service 不接管
        return pageClass.getName().startsWith("com.gzzm.lobster.");
    }

    @Override
    public Object before(RequestContext context) {
        if (context != null) {
            UserContextHolder.bridgeFromRequest(context.getRequest(), context.getResponse());
        }
        return null;
    }

    @Override
    public Object after(RequestContext context, Object result) { return null; }

    @Override
    public Object catchHandle(RequestContext context, Throwable ex) {
        Throwable root = unwrap(ex);
        String code;
        String message;
        int httpStatus;

        if (root instanceof LobsterException) {
            LobsterException le = (LobsterException) root;
            code = (le.getCode() != null && !le.getCode().isEmpty()) ? le.getCode() : "lobster.error";
            message = nonEmpty(le.getMessage(), code);
            httpStatus = mapLobsterCodeToHttp(code);
        } else if (root instanceof IllegalArgumentException) {
            code = "bad_request";
            message = nonEmpty(root.getMessage(), "参数错误");
            httpStatus = 400;
        } else if (root instanceof IllegalStateException) {
            // 典型："No authenticated UserContext on current thread"
            String msg = root.getMessage();
            if (msg != null && msg.contains("UserContext")) {
                code = "auth.required";
                message = "未登录或会话已过期，请重新登录";
                httpStatus = 401;
            } else {
                code = "illegal_state";
                message = nonEmpty(msg, root.getClass().getSimpleName());
                httpStatus = 500;
            }
        } else if (root instanceof SecurityException) {
            code = "auth.forbidden";
            message = nonEmpty(root.getMessage(), "无权访问");
            httpStatus = 403;
        } else {
            code = "internal_error";
            message = nonEmpty(root.getMessage(), root.getClass().getSimpleName());
            httpStatus = 500;
        }

        // 设置 HTTP 状态码，让前端 resp.ok 自动走错误分支
        try {
            HttpServletResponse resp = context.getResponse();
            if (resp != null) resp.setStatus(httpStatus);
        } catch (Throwable ignore) { /* 状态码失败不影响响应体 */ }

        // 始终落 server 端日志（含完整堆栈），便于运维排查；客户端只看到清洗后的 message
        try {
            String uri = (context.getRequest() != null) ? context.getRequest().getRequestURI() : "<unknown>";
            Tools.log("[lobster api] " + uri + " threw " + ex.getClass().getSimpleName()
                    + " → " + code + " (" + httpStatus + ")", ex);
        } catch (Throwable ignore) { /* ignore */ }

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("code", code);
        body.put("message", message);
        body.put("data", null);
        return body;
    }

    @Override
    public void finallyHandle(RequestContext context) {
        UserContextHolder.clear();
    }

    @Override
    public void logResult(RequestContext context, Object result) {}

    // ============================================================
    // helpers
    // ============================================================

    /**
     * 解开常见的反射 wrapper —— 反射调用失败会抛 InvocationTargetException 把真实异常套进 cause，
     * 此处剥到第一层"业务异常"再做分类.
     */
    private static Throwable unwrap(Throwable t) {
        if (t == null) return null;
        if (t instanceof InvocationTargetException && t.getCause() != null) {
            return t.getCause();
        }
        return t;
    }

    private static String nonEmpty(String s, String fallback) {
        return (s != null && !s.isEmpty()) ? s : fallback;
    }

    /**
     * LobsterException.code 到 HTTP 状态的约定映射。LobsterException 的 code 是符号串
     * （"kb.forbidden" / "kb.not_found" / "llm.exhausted" 等），这里按前缀粗分类：
     * <ul>
     *   <li>{@code auth.*} → 401</li>
     *   <li>{@code *.forbidden} → 403</li>
     *   <li>{@code *.not_found} → 404</li>
     *   <li>{@code *.invalid} / {@code bad_*} → 400</li>
     *   <li>其它默认 500（业务侧异常一般是上游/状态错误，不算客户端错）</li>
     * </ul>
     */
    private static int mapLobsterCodeToHttp(String code) {
        if (code == null) return 500;
        if (code.startsWith("auth.")) return 401;
        if (code.endsWith(".forbidden")) return 403;
        if (code.endsWith(".not_found")) return 404;
        if (code.endsWith(".invalid") || code.startsWith("bad_") || code.endsWith(".bad_request")) return 400;
        return 500;
    }
}
