package com.foxx.androidcast.ota;

/*********************************************************************
 * OtaSettingsStore.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
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 195 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	ad00d10986e51e0c3056c351cfba2c39da457a45562cabf09387f6910676dc2e
**********************************************************************/
import android.content.Context;

import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.BuildConfig;
import com.foxx.androidcast.crash.CrashSettings;
import com.foxx.androidcast.util.IoUtils;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;

/** Reads app-private settings.json and composes effective OTA runtime settings. */
final class OtaSettingsStore {
    private static final String SETTINGS_FILE = "settings.json";

    private OtaSettingsStore() {}

    static OtaRuntimeSettings load(Context context) {
        ensureSettingsFile(context);
        JSONObject root = readSettingsJson(context);
        JSONObject ota = root.optJSONObject("ota");

        LinkedHashMap<String, Boolean> dedup = new LinkedHashMap<>();
        addSource(dedup, AppPreferences.getOtaChannelUrl(context));
        if (BuildConfig.OTA_CHANNEL_URL_DEFAULT != null) {
            addSource(dedup, BuildConfig.OTA_CHANNEL_URL_DEFAULT);
        }
        if (BuildConfig.OTA_MANIFEST_URL_DEFAULT != null) {
            addSource(dedup, BuildConfig.OTA_MANIFEST_URL_DEFAULT);
        }
        if (ota != null) {
            addSource(dedup, ota.optString("base_url", ""));
            JSONArray arr = ota.optJSONArray("base_urls");
            if (arr != null) {
                for (int i = 0; i < arr.length(); i++) {
                    addSource(dedup, arr.optString(i, ""));
                }
            }
        }

        List<String> urls = new ArrayList<>(dedup.keySet());
        long interval = OtaRuntimeSettings.DEFAULT_CHECK_INTERVAL_MS;
        if (ota != null) {
            interval = parseDurationMs(ota.optString("check_interval", "1m"), interval);
        }
        boolean trusted = ota != null && parseBoolean(ota.optString("trusted", "false"));
        return new OtaRuntimeSettings(urls, interval, trusted);
    }

    private static void addSource(LinkedHashMap<String, Boolean> dedup, String raw) {
        if (raw == null) {
            return;
        }
        String value = raw.trim();
        if (value.isEmpty()) {
            return;
        }
        if (value.endsWith(",")) {
            value = value.substring(0, value.length() - 1).trim();
        }
        if (value.contains(",")) {
            String[] split = value.split(",");
            for (String s : split) {
                addSource(dedup, s);
            }
            return;
        }
        dedup.put(value, Boolean.TRUE);
    }

    private static JSONObject readSettingsJson(Context context) {
        File f = new File(context.getFilesDir(), SETTINGS_FILE);
        try (FileInputStream fis = new FileInputStream(f)) {
            byte[] data = IoUtils.readAllBytes(fis);
            String text = new String(data, StandardCharsets.UTF_8);
            return new JSONObject(text);
        } catch (Exception ignored) {
            return new JSONObject();
        }
    }

    private static void ensureSettingsFile(Context context) {
        File f = new File(context.getFilesDir(), SETTINGS_FILE);
        if (f.exists()) {
            return;
        }
        JSONObject ota = new JSONObject();
        try {
            ota.put("base_url", defaultBaseUrl(context));
            ota.put("trusted", "false");
            ota.put("check_interval", "1m");
            JSONObject crash = new JSONObject();
            crash.put("enabled", "true");
            crash.put("upload_url", CrashSettings.DEFAULT_UPLOAD_URL);
            crash.put("prune_after_upload", "true");
            crash.put("upload_interval", "5m");
            crash.put("max_pending", 32);
            JSONObject root = new JSONObject();
            root.put("ota", ota);
            root.put("crash", crash);
            try (FileOutputStream fos = new FileOutputStream(f)) {
                fos.write(root.toString(2).getBytes(StandardCharsets.UTF_8));
            }
        } catch (Exception ignored) {
        }
    }

    private static String defaultBaseUrl(Context context) {
        String pref = AppPreferences.getOtaChannelUrl(context);
        if (!pref.isEmpty()) {
            return pref;
        }
        if (BuildConfig.OTA_CHANNEL_URL_DEFAULT != null && !BuildConfig.OTA_CHANNEL_URL_DEFAULT.trim().isEmpty()) {
            return BuildConfig.OTA_CHANNEL_URL_DEFAULT.trim();
        }
        if (BuildConfig.OTA_MANIFEST_URL_DEFAULT != null) {
            return BuildConfig.OTA_MANIFEST_URL_DEFAULT.trim();
        }
        return "https://foxx.org/v0/ota/channel/stable.json";
    }

    static long parseDurationMs(String raw, long fallback) {
        if (raw == null) {
            return fallback;
        }
        String text = raw.trim().toLowerCase(Locale.US);
        if (text.isEmpty()) {
            return fallback;
        }
        try {
            long total = 0L;
            int i = 0;
            while (i < text.length()) {
                while (i < text.length() && Character.isWhitespace(text.charAt(i))) {
                    i++;
                }
                int start = i;
                while (i < text.length()
                        && (Character.isDigit(text.charAt(i)) || text.charAt(i) == '.')) {
                    i++;
                }
                if (start == i) {
                    return fallback;
                }
                double value = Double.parseDouble(text.substring(start, i));
                int unitStart = i;
                while (i < text.length() && Character.isLetter(text.charAt(i))) {
                    i++;
                }
                String unit = text.substring(unitStart, i);
                long mult;
                if ("ms".equals(unit)) {
                    mult = 1L;
                } else if ("s".equals(unit)) {
                    mult = 1_000L;
                } else if ("m".equals(unit)) {
                    mult = 60_000L;
                } else if ("h".equals(unit)) {
                    mult = 3_600_000L;
                } else if ("d".equals(unit)) {
                    mult = 86_400_000L;
                } else {
                    return fallback;
                }
                total += Math.round(value * mult);
            }
            return total > 0L ? total : fallback;
        } catch (Exception e) {
            return fallback;
        }
    }

    private static boolean parseBoolean(String raw) {
        if (raw == null) {
            return false;
        }
        String v = raw.trim().toLowerCase(Locale.US);
        return "true".equals(v) || "1".equals(v) || "yes".equals(v) || "on".equals(v);
    }
}
