package com.foxx.androidcast.ota;

/*********************************************************************
 * OtaManifest.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 OtaManifest
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 179 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	9d252cd4e87fbbb7368a325b3c8a19734691281b4638e6d3b71c23214c096463
**********************************************************************/
import android.text.TextUtils;

import org.json.JSONException;
import org.json.JSONObject;

/** Per-build OTA descriptor ({@code *_manifest.json}). */
public final class OtaManifest {
    private static final int UNSET = -1;

    public final int major;
    public final int minor;
    public final int build;
    /** Legacy field; ignored when {@link #hasVersionTriple()} is true. */
    public final int versionCode;
    public final String versionName;
    public final String apkUrl;
    public final String signUrl;
    /** SHA-256 of {@code package.apk} inside the bundle or standalone {@code .otapkg}. */
    public final String sha256Hex;
    public final long packageSizeBytes;
    public final boolean mandatory;
    public final String releaseNotes;
    /** Optional HTTP(S) zip bundle ({@code manifest.json}, {@code sign.json}, {@code package.apk}). */
    public final String bundleUrl;
    public final String bundleSha256Hex;
    public final long bundleSizeBytes;

    public OtaManifest(int major, int minor, int build, int versionCode, String versionName,
            String apkUrl, String signUrl, String sha256Hex, long packageSizeBytes,
            boolean mandatory, String releaseNotes) {
        this(major, minor, build, versionCode, versionName, apkUrl, signUrl, sha256Hex,
                packageSizeBytes, mandatory, releaseNotes, "", "", 0L);
    }

    public OtaManifest(int major, int minor, int build, int versionCode, String versionName,
            String apkUrl, String signUrl, String sha256Hex, long packageSizeBytes,
            boolean mandatory, String releaseNotes, String bundleUrl, String bundleSha256Hex,
            long bundleSizeBytes) {
        this.major = major;
        this.minor = minor;
        this.build = build;
        this.versionCode = versionCode;
        this.versionName = versionName != null ? versionName : "";
        this.apkUrl = apkUrl != null ? apkUrl : "";
        this.signUrl = signUrl != null ? signUrl : "";
        this.sha256Hex = sha256Hex != null ? sha256Hex.toLowerCase() : "";
        this.packageSizeBytes = Math.max(0L, packageSizeBytes);
        this.mandatory = mandatory;
        this.releaseNotes = releaseNotes != null ? releaseNotes : "";
        this.bundleUrl = bundleUrl != null ? bundleUrl : "";
        this.bundleSha256Hex = bundleSha256Hex != null ? bundleSha256Hex.toLowerCase() : "";
        this.bundleSizeBytes = Math.max(0L, bundleSizeBytes);
    }

    public boolean hasVersionTriple() {
        return major >= 0 && minor >= 0 && build >= 0;
    }

    public int effectiveVersionCode() {
        if (hasVersionTriple()) {
            return OtaVersion.pack(major, minor, build);
        }
        return versionCode;
    }

    public String displayVersion() {
        if (!versionName.isEmpty()) {
            return versionName;
        }
        if (hasVersionTriple()) {
            return new OtaVersion(major, minor, build).displayName();
        }
        return String.valueOf(versionCode);
    }

    /** HTTP(S) bundle download is preferred over separate artifact fetches. */
    public boolean hasHttpBundle() {
        return !bundleUrl.isEmpty() && OtaJsonFetch.isHttpUrl(bundleUrl);
    }

    /** Size shown in the update dialog (bundle zip when applicable). */
    public long downloadSizeBytes() {
        if (hasHttpBundle() && bundleSizeBytes > 0L) {
            return bundleSizeBytes;
        }
        return packageSizeBytes;
    }

    public boolean isValid() {
        return effectiveVersionCode() > 0
                && (!apkUrl.isEmpty() || hasHttpBundle());
    }

    public static OtaManifest fromJson(JSONObject root) {
        if (root == null) {
            return null;
        }
        return new OtaManifest(
                root.has("major") ? root.optInt("major", UNSET) : UNSET,
                root.has("minor") ? root.optInt("minor", UNSET) : UNSET,
                root.has("build") ? root.optInt("build", UNSET) : UNSET,
                root.optInt("versionCode", 0),
                root.optString("versionName", ""),
                root.optString("apkUrl", ""),
                root.optString("signUrl", ""),
                root.optString("sha256", ""),
                root.optLong("sizeBytes", 0L),
                root.optBoolean("mandatory", false),
                root.optString("releaseNotes", ""),
                root.optString("bundleUrl", ""),
                root.optString("bundleSha256", ""),
                root.optLong("bundleSizeBytes", 0L));
    }

    public static OtaManifest fromJsonString(String json) {
        if (TextUtils.isEmpty(json)) {
            return null;
        }
        try {
            return fromJson(new JSONObject(json));
        } catch (JSONException e) {
            return null;
        }
    }

    public JSONObject toJson() throws JSONException {
        JSONObject o = new JSONObject();
        if (hasVersionTriple()) {
            o.put("major", major);
            o.put("minor", minor);
            o.put("build", build);
        }
        if (versionCode != 0) {
            o.put("versionCode", versionCode);
        }
        if (!versionName.isEmpty()) {
            o.put("versionName", versionName);
        }
        o.put("apkUrl", apkUrl);
        if (!signUrl.isEmpty()) {
            o.put("signUrl", signUrl);
        }
        if (!sha256Hex.isEmpty()) {
            o.put("sha256", sha256Hex);
        }
        if (packageSizeBytes > 0) {
            o.put("sizeBytes", packageSizeBytes);
        }
        if (!bundleUrl.isEmpty()) {
            o.put("bundleUrl", bundleUrl);
        }
        if (!bundleSha256Hex.isEmpty()) {
            o.put("bundleSha256", bundleSha256Hex);
        }
        if (bundleSizeBytes > 0) {
            o.put("bundleSizeBytes", bundleSizeBytes);
        }
        o.put("mandatory", mandatory);
        o.put("releaseNotes", releaseNotes);
        return o;
    }

    public String toJsonString() {
        try {
            return toJson().toString();
        } catch (JSONException e) {
            return "{}";
        }
    }
}
