package com.foxx.androidcast.ota;

/*********************************************************************
 * OtaUpdateChecker.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 OtaUpdateChecker
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 170 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	35c0b4dd9db5095245a025fe2ea4971146d322b7ad41e6101b54afe7b4b7246c
**********************************************************************/
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

import org.json.JSONObject;

/** Resolves v0 channel or manifest URL and compares against the installed build. */
public final class OtaUpdateChecker {
    private static final int MAX_CHANNEL_BYTES = 8 * 1024;
    private static final int MAX_MANIFEST_BYTES = 64 * 1024;
    private static final int MAX_SIGN_BYTES = 16 * 1024;

    private OtaUpdateChecker() {}

    public static final class Result {
        public final OtaManifest manifest;
        public final int installedVersionCode;
        public final String installedVersionName;
        public final boolean updateAvailable;
        public final String sourceUrl;
        public final String error;

        Result(OtaManifest manifest, int installedVersionCode, String installedVersionName,
                boolean updateAvailable, String sourceUrl, String error) {
            this.manifest = manifest;
            this.installedVersionCode = installedVersionCode;
            this.installedVersionName = installedVersionName != null ? installedVersionName : "";
            this.updateAvailable = updateAvailable;
            this.sourceUrl = sourceUrl != null ? sourceUrl : "";
            this.error = error;
        }

        static Result error(String sourceUrl, String message) {
            return new Result(null, 0, "", false, sourceUrl, message);
        }
    }

    public static int installedVersionCode(Context context) {
        return OtaVersion.installed().packed();
    }

    public static String installedVersionName(Context context) {
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
            return info.versionName != null ? info.versionName : OtaVersion.installed().displayName();
        } catch (PackageManager.NameNotFoundException e) {
            return OtaVersion.installed().displayName();
        }
    }

    public static Result check(Context context, String entryUrl) {
        return check(context, entryUrl, true);
    }

    public static Result check(Context context, String entryUrl, boolean strictVerification) {
        if (entryUrl == null || entryUrl.trim().isEmpty()) {
            return Result.error("", "No OTA channel URL configured");
        }
        String normalized = entryUrl.trim();
        int installed = installedVersionCode(context);
        String installedName = installedVersionName(context);
        try {
            OtaManifest manifest = resolveManifest(normalized);
            if (manifest == null) {
                return Result.error(normalized, "Invalid manifest JSON");
            }
            if (!manifest.isValid()) {
                return Result.error(normalized, "Manifest missing version or download URL");
            }
            manifest = enrichFromSignBundle(manifest, strictVerification);
            long sizeBytes = resolveDisplaySizeBytes(manifest);
            if (sizeBytes > 0L) {
                manifest = new OtaManifest(
                        manifest.major,
                        manifest.minor,
                        manifest.build,
                        manifest.versionCode,
                        manifest.versionName,
                        manifest.apkUrl,
                        manifest.signUrl,
                        manifest.sha256Hex,
                        manifest.hasHttpBundle() ? manifest.packageSizeBytes : sizeBytes,
                        manifest.mandatory,
                        manifest.releaseNotes,
                        manifest.bundleUrl,
                        manifest.bundleSha256Hex,
                        manifest.hasHttpBundle() ? sizeBytes : manifest.bundleSizeBytes);
            }
            boolean available = OtaVersion.isUpdateAvailable(
                    manifest.effectiveVersionCode(), installed);
            return new Result(manifest, installed, installedName, available, normalized, null);
        } catch (Exception e) {
            return Result.error(
                    normalized,
                    e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName());
        }
    }

    static OtaManifest resolveManifest(String entryUrl) throws Exception {
        JSONObject root = OtaJsonFetch.getJson(entryUrl, MAX_MANIFEST_BYTES);
        if (root.has("apkUrl")) {
            return OtaManifest.fromJson(root);
        }
        OtaChannelPointer channel = OtaChannelPointer.fromJson(root);
        if (!channel.isValid()) {
            throw new IllegalStateException("Channel JSON missing manifestUrl");
        }
        return fetchManifest(channel.manifestUrl);
    }

    static OtaManifest fetchManifest(String manifestUrl) throws Exception {
        return OtaManifest.fromJson(OtaJsonFetch.getJson(manifestUrl, MAX_MANIFEST_BYTES));
    }

    static OtaManifest enrichFromSignBundle(OtaManifest manifest, boolean strictVerification) throws Exception {
        if (manifest == null) {
            return manifest;
        }
        if (!manifest.sha256Hex.isEmpty()) {
            return manifest;
        }
        if (manifest.signUrl.isEmpty()) {
            if (strictVerification) {
                throw new IllegalStateException("Manifest missing sha256/signUrl");
            }
            return manifest;
        }
        OtaSignBundle sign = OtaSignBundle.fromJson(
                OtaJsonFetch.getJson(manifest.signUrl, MAX_SIGN_BYTES));
        if (sign.sha256Hex.isEmpty()) {
            if (strictVerification) {
                throw new IllegalStateException("sign.json missing sha256");
            }
            return manifest;
        }
        return new OtaManifest(manifest.major, manifest.minor, manifest.build, manifest.versionCode,
                manifest.versionName, manifest.apkUrl, manifest.signUrl, sign.sha256Hex,
                manifest.packageSizeBytes, manifest.mandatory, manifest.releaseNotes,
                manifest.bundleUrl, manifest.bundleSha256Hex, manifest.bundleSizeBytes);
    }

    static long resolveDisplaySizeBytes(OtaManifest manifest) throws Exception {
        if (manifest.hasHttpBundle()) {
            if (manifest.bundleSizeBytes > 0L) {
                return manifest.bundleSizeBytes;
            }
            long len = OtaJsonFetch.getContentLength(manifest.bundleUrl);
            if (len > 0L) {
                return len;
            }
        }
        if (manifest.packageSizeBytes > 0L) {
            return manifest.packageSizeBytes;
        }
        if (!manifest.apkUrl.isEmpty()) {
            return OtaJsonFetch.getContentLength(manifest.apkUrl);
        }
        return 0L;
    }
}
