package com.foxx.androidcast.crash;

/*********************************************************************
 * CrashReporter.java
 * Created at: Wed 20 May 2026 14:31:55 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class CrashReporter
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 89 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	5254fee83476d4a80db718169cbc13b61b18697148a8625baab8df0833b1d78f
**********************************************************************/
import android.app.Application;
import android.content.Context;
import android.util.Log;

import com.foxx.androidcast.stats.SessionStatsContext;

import org.json.JSONObject;

import java.io.File;

/** Main-process entry: write pending crash JSON and wake the watcher process. */
public final class CrashReporter {
    private static final String TAG = "CrashReporter";

    private CrashReporter() {}

    public static void install(Context context) {
        Context app = context.getApplicationContext();
        if (!isMainProcess(app)) {
            return;
        }
        if (app instanceof android.app.Application) {
            CrashRuntimeContext.init((android.app.Application) app);
        }
        CrashNativeBridge.init(app);
        final Thread.UncaughtExceptionHandler previous =
                Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            try {
                SessionStatsContext.flushActiveOnUncaughtException(app);
            } catch (Throwable ignored) {
            }
            try {
                recordJavaCrash(app, thread, throwable);
            } catch (Throwable t) {
                Log.e(TAG, "recordJavaCrash failed", t);
            }
            if (previous != null) {
                previous.uncaughtException(thread, throwable);
            }
        });
        CrashWatcherService.scheduleUploadSweep(app);
    }

    public static void recordJavaCrash(Context context, Thread thread, Throwable throwable) {
        if (!CrashSettingsStore.load(context).enabled) {
            return;
        }
        JSONObject report = CrashReportBuilder.buildJavaCrash(context, thread, throwable);
        persistAndWake(context, report);
    }

    public static void recordNativeCrash(Context context, String signalName, String backtrace) {
        if (!CrashSettingsStore.load(context).enabled) {
            return;
        }
        JSONObject report = CrashReportBuilder.buildNativeCrash(context, signalName, backtrace);
        persistAndWake(context, report);
    }

    static void persistAndWake(Context context, JSONObject report) {
        if (report == null) {
            return;
        }
        try {
            long generatedAt = report.optLong("generated_at_epoch_ms", System.currentTimeMillis());
            File file = CrashReportStore.newPendingFile(context.getFilesDir(), generatedAt);
            report.put("report_id", file.getName().substring(
                    CrashReportStore.PREFIX.length(),
                    file.getName().length() - ".json".length()));
            report.put("report_file", file.getName());
            CrashReportStore.writeJson(file, report);
            CrashReportStore.pruneOldestPending(context.getFilesDir(),
                    CrashSettingsStore.load(context).maxPendingFiles);
            wakeWatcher(context);
        } catch (Exception e) {
            Log.e(TAG, "persist failed", e);
        }
    }

    private static void wakeWatcher(Context context) {
        CrashWatcherService.requestImmediateSweep(context, true);
    }

    private static boolean isMainProcess(Context context) {
        return context.getPackageName().equals(Application.getProcessName());
    }
}
