// epm/src/update.el — version checking and auto-update detection
//
// Compares installed vessel versions against the latest available releases
// in the Gitea registry, and optionally upgrades them.
//
// Depends on: registry.el (registry_latest_version, registry_find),
//             install.el  (read_installed, install_vessel, installed_version)

// ── Semver helpers ────────────────────────────────────────────────────────────

// semver_part extracts the Nth dot-separated component from a semver string.
// Returns "0" if the component is missing.
fn semver_part(ver: String, idx: Int) -> String {
    let parts: [String] = str_split(ver, ".")
    let n: Int = native_list_len(parts)
    if idx >= n { return "0" }
    let v: String = str_trim(native_list_get(parts, idx))
    if str_eq(v, "") { return "0" }
    return v
}

// semver_compare compares two semver strings a and b.
// Returns -1 if a < b, 0 if a == b, 1 if a > b.
// Compares major, then minor, then patch numerically.
fn semver_compare(a: String, b: String) -> Int {
    let a_major: Int = native_str_to_int(semver_part(a, 0))
    let b_major: Int = native_str_to_int(semver_part(b, 0))
    if a_major < b_major { return -1 }
    if a_major > b_major { return 1 }

    let a_minor: Int = native_str_to_int(semver_part(a, 1))
    let b_minor: Int = native_str_to_int(semver_part(b, 1))
    if a_minor < b_minor { return -1 }
    if a_minor > b_minor { return 1 }

    let a_patch: Int = native_str_to_int(semver_part(a, 2))
    let b_patch: Int = native_str_to_int(semver_part(b, 2))
    if a_patch < b_patch { return -1 }
    if a_patch > b_patch { return 1 }

    return 0
}

// semver_gt returns true if a is strictly greater than b.
fn semver_gt(a: String, b: String) -> Bool {
    let cmp: Int = semver_compare(a, b)
    return cmp == 1
}

// ── Update checks ─────────────────────────────────────────────────────────────

// check_updates scans installed.json and checks if newer versions are available
// in the registry. Prints a notice for each outdated vessel.
// Returns the count of outdated vessels.
fn check_updates() -> Int {
    let installed: String = read_installed()
    if str_eq(installed, "") { return 0 }
    if str_eq(installed, "{}") { return 0 }

    // installed is a flat JSON object: {"name":"version",...}
    // We need to iterate over its keys. Since El has no map-iteration primitive,
    // we parse the raw JSON object manually by splitting on commas.
    let inner_len: Int = str_len(installed)
    if inner_len < 2 { return 0 }
    // Strip outer braces
    let body: String = str_slice(installed, 1, inner_len - 1)
    let body_trimmed: String = str_trim(body)
    if str_eq(body_trimmed, "") { return 0 }

    let pairs: [String] = str_split(body_trimmed, ",")
    let pair_count: Int = native_list_len(pairs)
    let outdated: Int = 0

    let i: Int = 0
    while i < pair_count {
        let pair: String = str_trim(native_list_get(pairs, i))
        // pair looks like "name":"version"
        let colon: Int = str_index_of(pair, ":")
        if colon > 0 {
            let raw_key: String = str_trim(str_slice(pair, 0, colon))
            let raw_val: String = str_trim(str_slice(pair, colon + 1, str_len(pair)))
            // Strip surrounding quotes
            let key_len: Int = str_len(raw_key)
            let val_len: Int = str_len(raw_val)
            let vessel_name: String = raw_key
            let vessel_ver: String  = raw_val
            if str_starts_with(raw_key, "\"") {
                let vessel_name = str_slice(raw_key, 1, key_len - 1)
            }
            if str_starts_with(raw_val, "\"") {
                let vessel_ver = str_slice(raw_val, 1, val_len - 1)
            }

            // Check latest version in registry
            let latest: String = registry_latest_version(vessel_name)
            if !str_eq(latest, "") {
                if semver_gt(latest, vessel_ver) {
                    println("  " + vessel_name + ": " + vessel_ver + " -> " + latest + " available")
                    let outdated = outdated + 1
                }
            }
        }
        let i = i + 1
    }

    return outdated
}

// ── Update command ────────────────────────────────────────────────────────────

// cmd_update fetches the latest version of all installed vessels (or a specific
// one) and installs it if newer than what's currently installed.
// If vessel_name is "" it updates all installed vessels.
fn cmd_update(vessel_name: String) -> Void {
    if !str_eq(vessel_name, "") {
        // Update a single named vessel
        let current: String = installed_version(vessel_name)
        if str_eq(current, "") {
            println("epm: '" + vessel_name + "' is not installed")
            exit(1)
        }

        let latest: String = registry_latest_version(vessel_name)
        if str_eq(latest, "") {
            println("epm: no releases found for '" + vessel_name + "' in registry")
            return
        }

        if !semver_gt(latest, current) {
            println("epm: " + vessel_name + " " + current + " is already up to date")
            return
        }

        println("epm: updating " + vessel_name + " " + current + " -> " + latest + " ...")
        let ok: Bool = install_vessel(vessel_name, latest)
        if !ok {
            println("epm: update failed for " + vessel_name)
            exit(1)
        }
        return
    }

    // Update all installed vessels
    let installed: String = read_installed()
    if str_eq(installed, "") {
        println("epm: no installed vessels")
        return
    }
    if str_eq(installed, "{}") {
        println("epm: no installed vessels")
        return
    }

    let inner_len: Int = str_len(installed)
    if inner_len < 2 {
        println("epm: no installed vessels")
        return
    }
    let body: String = str_slice(installed, 1, inner_len - 1)
    let body_trimmed: String = str_trim(body)
    if str_eq(body_trimmed, "") {
        println("epm: no installed vessels")
        return
    }

    let pairs: [String] = str_split(body_trimmed, ",")
    let pair_count: Int = native_list_len(pairs)
    let updated_count: Int = 0

    let i: Int = 0
    while i < pair_count {
        let pair: String = str_trim(native_list_get(pairs, i))
        let colon: Int = str_index_of(pair, ":")
        if colon > 0 {
            let raw_key: String = str_trim(str_slice(pair, 0, colon))
            let raw_val: String = str_trim(str_slice(pair, colon + 1, str_len(pair)))
            let key_len: Int = str_len(raw_key)
            let val_len: Int = str_len(raw_val)
            let vessel_name2: String = raw_key
            let vessel_ver2: String  = raw_val
            if str_starts_with(raw_key, "\"") {
                let vessel_name2 = str_slice(raw_key, 1, key_len - 1)
            }
            if str_starts_with(raw_val, "\"") {
                let vessel_ver2 = str_slice(raw_val, 1, val_len - 1)
            }

            let latest: String = registry_latest_version(vessel_name2)
            if !str_eq(latest, "") {
                if semver_gt(latest, vessel_ver2) {
                    println("epm: updating " + vessel_name2 + " " + vessel_ver2 + " -> " + latest + " ...")
                    let ok: Bool = install_vessel(vessel_name2, latest)
                    if ok {
                        let updated_count = updated_count + 1
                    }
                }
            }
        }
        let i = i + 1
    }

    if updated_count == 0 {
        println("epm: all installed vessels are up to date")
    } else {
        println("epm: updated " + native_int_to_str(updated_count) + " vessel(s)")
    }
}
