#!/bin/bash

# WebUI System Update Coordinator
# Coordinates the update of all system information

webuiSystemUpdate() {
    webuiSystemInfo
    webuiSystemDisk
    webuiSystemMemory
    webuiSystemUpdateCheck
    isSuccessful "System information updated!"
}

# ---------------------------------------------------------------------------
#  WebUI "Out of Date" detection
# ---------------------------------------------------------------------------
# Writes frontend/data/system/update_status.json so the dashboard + topbar can
# tell the user when their LibrePortal install is behind the upstream release.
#
# Detection mirrors the model the CLI updater already trusts (scripts/update):
# a git working copy at $script_dir tracking a remote branch. We `git fetch`
# (throttled — the network call is the expensive part) and compare the local
# HEAD to origin/<branch>. The local VERSION file vs the remote VERSION file
# turns the raw "N commits behind" into a friendly "v0.1.0 -> v0.2.0".
#
# The JSON is deliberately source-agnostic: when get.libreportal.org is ready
# in the website session, only the `latest_version` / `latest_commit` /
# `behind` values need to come from the HTTP endpoint instead of git — the
# frontend and the rest of this file stay the same.
#
# Pass "force" as $1 to bypass the fetch throttle (used by the manual
# "Check for updates" action: `libreportal update check`).
webuiSystemUpdateCheck() {
    local force_flag="$1"

    local repo_dir="${script_dir}"
    local system_dir="$containers_dir/libreportal/frontend/data/system"
    local final_file="${system_dir}/update_status.json"
    local stamp_file="${system_dir}/.update_check_stamp"
    # How long (seconds) a fetch result stays "fresh" before we hit the network
    # again. Overridable via CFG_UPDATE_CHECK_INTERVAL; defaults to 3 hours.
    local fetch_interval="${CFG_UPDATE_CHECK_INTERVAL:-10800}"

    createFolders "quiet" "$sudo_user_name" "$system_dir"

    local install_mode="${CFG_INSTALL_MODE:-git}"
    local git_updates="${CFG_GIT_UPDATES:-true}"
    local auto_updates="${CFG_GIT_AUTO_UPDATES:-false}"

    # Local version: VERSION file first, fall back to the latest reachable tag.
    local current_version=""
    if [[ -f "$repo_dir/VERSION" ]]; then
        current_version=$(tr -d ' \t\n\r' < "$repo_dir/VERSION")
    fi
    if [[ -z "$current_version" ]]; then
        current_version=$(sudo -u "$sudo_user_name" git -C "$repo_dir" describe --tags --abbrev=0 2>/dev/null)
    fi
    [[ -z "$current_version" ]] && current_version="unknown"

    # Atomic JSON writer. Args (positional):
    #   1 update_available  2 can_update  3 current_version  4 latest_version
    #   5 current_commit    6 latest_commit  7 behind  8 ahead  9 branch
    #   10 source           11 error_message (empty = null)
    _webuiWriteUpdateStatus() {
        local _update_available="$1" _can_update="$2"
        local _current_version="$3" _latest_version="$4"
        local _current_commit="$5" _latest_commit="$6"
        local _behind="$7" _ahead="$8" _branch="$9" _source="${10}" _error="${11}"

        local _error_json="null"
        [[ -n "$_error" ]] && _error_json="\"$_error\""

        local temp_file="${final_file}.tmp.$$"
        cat << EOF > "$temp_file"
{
  "update_available": ${_update_available},
  "can_update": ${_can_update},
  "current_version": "${_current_version}",
  "latest_version": "${_latest_version}",
  "current_commit": "${_current_commit}",
  "latest_commit": "${_latest_commit}",
  "behind": ${_behind},
  "ahead": ${_ahead},
  "branch": "${_branch}",
  "install_mode": "${install_mode}",
  "git_updates_enabled": ${git_updates},
  "auto_updates": ${auto_updates},
  "source": "${_source}",
  "error": ${_error_json},
  "checked_at": "$(date -Iseconds)"
}
EOF
        if [ $? -eq 0 ]; then
            mv "$temp_file" "$final_file"
            createTouch "$final_file" "$sudo_user_name" "silent"
        else
            rm -f "$temp_file" 2>/dev/null
        fi
    }

    # Not a git working copy, or a deliberately local install: we can't compare
    # against an upstream, so report "managed manually" rather than an error.
    if [[ ! -d "$repo_dir/.git" || "$install_mode" == "local" ]]; then
        _webuiWriteUpdateStatus "false" "false" \
            "$current_version" "$current_version" \
            "" "" "0" "0" "" "local" ""
        return 0
    fi

    local branch
    branch=$(sudo -u "$sudo_user_name" git -C "$repo_dir" rev-parse --abbrev-ref HEAD 2>/dev/null)
    [[ -z "$branch" || "$branch" == "HEAD" ]] && branch="main"

    sudo -u "$sudo_user_name" git -C "$repo_dir" config core.fileMode false >/dev/null 2>&1

    # Decide whether to hit the network this run.
    local do_fetch="false"
    if [[ "$force_flag" == "force" || ! -f "$stamp_file" ]]; then
        do_fetch="true"
    else
        local _now _last
        _now=$(date +%s)
        _last=$(stat -c '%Y' "$stamp_file" 2>/dev/null || echo 0)
        (( _now - _last >= fetch_interval )) && do_fetch="true"
    fi

    local fetch_error=""
    if [[ "$do_fetch" == "true" ]]; then
        local _fetched="false"
        if [[ "$install_mode" == "git" && -n "$CFG_GIT_USER" && "$CFG_GIT_USER" != "empty" && "$CFG_GIT_USER" != "changeme" ]]; then
            if sudo -u "$sudo_user_name" git -C "$repo_dir" \
                -c "credential.helper=" \
                -c "credential.helper=!f() { echo username=$CFG_GIT_USER; echo password=$CFG_GIT_KEY; }; f" \
                fetch --quiet origin "$branch" >/dev/null 2>&1; then
                _fetched="true"
            fi
        else
            if sudo -u "$sudo_user_name" git -C "$repo_dir" fetch --quiet origin "$branch" >/dev/null 2>&1; then
                _fetched="true"
            fi
        fi

        if [[ "$_fetched" == "true" ]]; then
            sudo -u "$sudo_user_name" touch "$stamp_file" 2>/dev/null || touch "$stamp_file" 2>/dev/null
        else
            fetch_error="Could not reach the update server."
        fi
    fi

    # Compare local HEAD against the (possibly just-fetched) remote ref.
    local current_commit latest_commit behind ahead latest_version
    current_commit=$(sudo -u "$sudo_user_name" git -C "$repo_dir" rev-parse --short HEAD 2>/dev/null)
    latest_commit=$(sudo -u "$sudo_user_name" git -C "$repo_dir" rev-parse --short "refs/remotes/origin/$branch" 2>/dev/null)
    behind=$(sudo -u "$sudo_user_name" git -C "$repo_dir" rev-list --count "HEAD..refs/remotes/origin/$branch" 2>/dev/null)
    ahead=$(sudo -u "$sudo_user_name" git -C "$repo_dir" rev-list --count "refs/remotes/origin/$branch..HEAD" 2>/dev/null)
    [[ -z "$behind" ]] && behind=0
    [[ -z "$ahead" ]] && ahead=0
    [[ -z "$current_commit" ]] && current_commit="unknown"
    [[ -z "$latest_commit" ]] && latest_commit="$current_commit"

    latest_version=$(sudo -u "$sudo_user_name" git -C "$repo_dir" show "refs/remotes/origin/$branch:VERSION" 2>/dev/null | tr -d ' \t\n\r')
    [[ -z "$latest_version" ]] && latest_version="$current_version"

    local update_available="false"
    [[ "$behind" -gt 0 ]] && update_available="true"

    local can_update="false"
    [[ "$install_mode" == "git" && "$git_updates" == "true" ]] && can_update="true"

    _webuiWriteUpdateStatus "$update_available" "$can_update" \
        "$current_version" "$latest_version" \
        "$current_commit" "$latest_commit" \
        "$behind" "$ahead" "$branch" "git" "$fetch_error"
}
