package com.foxx.androidcast.stats;

import android.content.Context;
import android.os.Build;

import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.BuildConfig;
import com.foxx.androidcast.DeviceInfo;
import com.foxx.androidcast.crash.CrashSettingsStore;
import com.foxx.androidcast.crash.CrashUploadClient;
import com.foxx.androidcast.network.BackendEndpoints;

import org.json.JSONObject;

import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** POST anonymous session aggregates to the graphs ingest API ({@code graph_upload.php}). */
public final class GraphSessionUploader {
    private static final ExecutorService EXEC = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r, "graph-session-upload");
        t.setDaemon(true);
        return t;
    });

    private GraphSessionUploader() {}

    public static void uploadAsync(Context context, JSONObject sessionRoot) {
        if (context == null || sessionRoot == null || !AppPreferences.isGrabSessionStats(context)) {
            return;
        }
        final Context app = context.getApplicationContext();
        EXEC.execute(() -> upload(app, sessionRoot));
    }

    static boolean upload(Context context, JSONObject sessionRoot) {
        try {
            JSONObject payload = toGraphPayload(context, sessionRoot);
            if (payload == null) {
                return false;
            }
            String url = BackendEndpoints.graphUploadUrlFromCrashesUrl(
                    CrashSettingsStore.load(context).uploadUrl);
            return CrashUploadClient.upload(url, payload);
        } catch (Exception ignored) {
            return false;
        }
    }

    static JSONObject toGraphPayload(Context context, JSONObject root) throws Exception {
        long started = root.optLong("started_at_epoch_ms", 0L);
        long ended = root.optLong("ended_at_epoch_ms", 0L);
        if (started <= 0 || ended <= 0 || ended < started) {
            return null;
        }
        String direction = root.optString("direction", "send");
        String ingestDir = "recv".equalsIgnoreCase(direction) ? "receiver" : "sender";
        String transport = root.optString("transport", "wifi").toLowerCase(Locale.US);
        if (transport.isEmpty()) {
            transport = "wifi";
        }

        JSONObject payload = new JSONObject();
        payload.put("schema_version", 1);
        payload.put("ingest_kind", "graph_session");
        payload.put("session_id", sessionId(started, direction));
        payload.put("device_id", DeviceInfo.getDeviceUuid(context));
        payload.put("app_version", BuildConfig.VERSION_NAME);
        payload.put("sdk_int", Build.VERSION.SDK_INT);
        payload.put("started_at_epoch_ms", started);
        payload.put("ended_at_epoch_ms", ended);
        payload.put("duration_s", Math.max(0, (int) ((ended - started) / 1000L)));
        payload.put("direction", ingestDir);
        payload.put("transport", transport);
        payload.put("install_source", "direct");
        payload.put("ntp_source", AppPreferences.isDevBackendNtpSyncEnabled(context) ? "backend" : "device");

        if (root.has("avg_video_bitrate_kbps")) {
            payload.put("avg_kbps", Math.max(0, (int) Math.round(root.optDouble("avg_video_bitrate_kbps", 0))));
        }
        if (root.has("avg_loss_ratio")) {
            payload.put("packet_loss_pct", Math.max(0.0, root.optDouble("avg_loss_ratio", 0) * 100.0));
        }

        JSONObject globals = root.optJSONObject("globals");
        String reason = globals != null ? globals.optString("reason", "") : "";
        payload.put("completed", reason.isEmpty() || !"crash".equalsIgnoreCase(reason));

        JSONObject meta = new JSONObject();
        meta.put("capture_mode", root.optString("capture_mode", ""));
        meta.put("video_codec", root.optString("video_codec", ""));
        meta.put("role", root.optString("role", ""));
        if (root.has("avg_encode_fps")) {
            meta.put("avg_encode_fps", root.optDouble("avg_encode_fps", 0));
        }
        payload.put("meta", meta);
        return payload;
    }

    private static String sessionId(long startedMs, String direction) {
        String seed = startedMs + ":" + direction + ":" + UUID.randomUUID();
        return "mob-" + Integer.toHexString(seed.hashCode()) + "-" + startedMs;
    }
}
