package com.miraclegarden.smsmessage.xposed.hook;

import java.util.concurrent.atomic.AtomicInteger;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * MariBank / SeaBank HTTP 全量抓包（仅 OkHttp 头 + 响应体）。
 * 加密明文 / attestation 由 {@link MariBankSdkUtilsHook} 负责，勿在此重复 Hook Gson/native。
 */
public final class MariBankFullCaptureHook {

    private static final String TAG = MariBankCaptureUtil.TAG;
    private static final AtomicInteger SEQ = new AtomicInteger();

    private static volatile boolean installed = false;

    private MariBankFullCaptureHook() {
    }

    public static void installLate(XC_LoadPackage.LoadPackageParam lpparam) {
        if (installed) {
            return;
        }
        installed = true;

        int n = hookOkHttpRealCall(lpparam);
        XposedBridge.log(TAG + " http capture hooks=" + n);
    }

    private static int hookOkHttpRealCall(XC_LoadPackage.LoadPackageParam lpparam) {
        XC_MethodHook hook = new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) {
                try {
                    Object request = XposedHelpers.getObjectField(param.thisObject, "originalRequest");
                    if (request == null) {
                        return;
                    }
                    captureRequest(request);
                } catch (Throwable t) {
                    XposedBridge.log(TAG + " req capture err: " + t.getMessage());
                }
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) {
                if (!"execute".equals(param.method.getName())) {
                    return;
                }
                try {
                    Object response = param.getResult();
                    if (response != null) {
                        captureResponse(response);
                    }
                } catch (Throwable t) {
                    XposedBridge.log(TAG + " resp capture err: " + t.getMessage());
                }
            }
        };
        int count = 0;
        for (String className : new String[]{
                "okhttp3.RealCall", "okhttp3.internal.connection.RealCall"
        }) {
            try {
                XposedHelpers.findAndHookMethod(className, lpparam.classLoader, "execute", hook);
                XposedHelpers.findAndHookMethod(className, lpparam.classLoader, "enqueue",
                        "okhttp3.Callback", hook);
                count += 2;
            } catch (Throwable ignored) {
            }
        }
        return count;
    }

    private static void captureRequest(Object request) {
        Object urlObj = XposedHelpers.callMethod(request, "url");
        String url = urlObj != null ? String.valueOf(urlObj) : "?";
        if (!MariBankCaptureUtil.isBankUrl(url)) {
            return;
        }
        int seq = SEQ.incrementAndGet();
        String method = (String) XposedHelpers.callMethod(request, "method");
        XposedBridge.log(TAG + " >>> #" + seq + " " + method + " " + url);
        logHeaders("reqHdr #" + seq, XposedHelpers.callMethod(request, "headers"));
    }

    private static void captureResponse(Object response) {
        try {
            Object request = XposedHelpers.callMethod(response, "request");
            Object urlObj = request != null ? XposedHelpers.callMethod(request, "url") : null;
            String url = urlObj != null ? String.valueOf(urlObj) : "?";
            if (!MariBankCaptureUtil.isBankUrl(url)) {
                return;
            }
            int code = (int) XposedHelpers.callMethod(response, "code");
            XposedBridge.log(TAG + " <<< HTTP " + code + " " + url);
            logHeaders("respHdr", XposedHelpers.callMethod(response, "headers"));
            try {
                Object peek = XposedHelpers.callMethod(response, "peekBody", 256L * 1024L);
                if (peek != null) {
                    Object ctObj = XposedHelpers.callMethod(peek, "contentType");
                    String contentType = ctObj != null ? String.valueOf(ctObj).toLowerCase() : "";
                    if (contentType.contains("pdf") || contentType.contains("octet-stream")) {
                        long size = 0L;
                        try {
                            size = (long) XposedHelpers.callMethod(peek, "contentLength");
                        } catch (Throwable ignored) {
                        }
                        XposedBridge.log(TAG + " <<< respBody skipped binary ct="
                                + contentType + " len=" + size + " " + url);
                    } else {
                        String body = (String) XposedHelpers.callMethod(peek, "string");
                        if (body != null && !body.isEmpty()) {
                            if (body.length() > 512
                                    && !MariBankCaptureUtil.isMostlyPrintable(body)) {
                                XposedBridge.log(TAG + " <<< respBody skipped non-text len="
                                        + body.length() + " " + url);
                            } else {
                                MariBankCaptureUtil.logText("respBody", body);
                            }
                        }
                    }
                }
            } catch (Throwable t) {
                XposedBridge.log(TAG + " peekBody err: " + t.getMessage());
            }
        } catch (Throwable t) {
            XposedBridge.log(TAG + " captureResponse err: " + t.getMessage());
        }
    }

    private static void logHeaders(String section, Object headers) {
        if (headers == null) {
            return;
        }
        try {
            int size = (int) XposedHelpers.callMethod(headers, "size");
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < size; i++) {
                String name = (String) XposedHelpers.callMethod(headers, "name", i);
                String value = (String) XposedHelpers.callMethod(headers, "value", i);
                sb.append(name).append(": ").append(value).append("\n");
            }
            MariBankCaptureUtil.logText(section, sb.toString().trim());
        } catch (Throwable t) {
            XposedBridge.log(TAG + " [" + section + "] headers err: " + t.getMessage());
        }
    }
}
