#!/bin/bash

# WebUI network-drift detector.
# Writes frontend/data/system/network_status.json so the dashboard + topbar can
# surface apps whose allocated static IP no longer falls inside the docker
# network's real subnet (the "network recreated with a different /24, apps
# stranded" case — see scripts/checks/requirements/check_docker_network.sh).
#
# Read-only: it inspects docker + the network_resources DB and writes a status
# file. The actual fix is the locked-down `libreportal system network heal`.
#
# Scheduling: invoked from the task processor's idle poll (~60s) via
# `libreportal system network check`, and from webuiSystemUpdate on full
# refreshes. Self-throttled to CFG_NETWORK_CHECK_INTERVAL so most calls no-op.
# Pass "force" to bypass the throttle (the manual re-check button / post-heal).
webuiSystemNetworkCheck() {
    local force_flag="$1"

    local system_dir="$containers_dir/libreportal/frontend/data/system"
    local final_file="${system_dir}/network_status.json"
    local stamp_file="${system_dir}/.network_check_stamp"
    local interval="${CFG_NETWORK_CHECK_INTERVAL:-300}"

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

    local do_run="false"
    if [[ "$force_flag" == "force" || ! -f "$final_file" || ! -f "$stamp_file" ]]; then
        do_run="true"
    else
        local _now _last; _now=$(date +%s); _last=$(stat -c '%Y' "$stamp_file" 2>/dev/null || echo 0)
        (( _now - _last >= interval )) && do_run="true"
    fi
    [[ "$do_run" == "true" ]] || return 0
    runFileOp touch "$stamp_file" 2>/dev/null || true

    # Read-only scan — call directly (NOT in $(...)): it sets NET_* globals + the
    # NET_CONFLICTS array, which a subshell would discard.
    networkScanConflicts

    local conflicts_found="false" can_auto_heal="false" conflict_count=0
    local apps_json="" error_json="null"

    if [[ "$NET_DAEMON_OK" != "true" ]]; then
        # Daemon unreachable — neutral status; never alarm on what we can't check.
        error_json="\"${NET_SCAN_ERROR//\"/\\\"}\""
    elif [[ "$NET_PRESENT" != "true" ]]; then
        # Network gone — a conflict only if something is installed to use it.
        error_json="\"${NET_SCAN_ERROR//\"/\\\"}\""
        local installed; installed=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT COUNT(*) FROM apps WHERE status=1;" 2>/dev/null)
        if [[ "${installed:-0}" -gt 0 ]]; then
            conflicts_found="true"; can_auto_heal="true"; conflict_count="$installed"
        fi
    else
        local row app service ip _a _s
        for row in "${NET_CONFLICTS[@]}"; do
            IFS='|' read -r app service ip <<< "$row"
            [[ -z "$app" || -z "$ip" ]] && continue
            _a=${app//\\/\\\\}; _a=${_a//\"/\\\"}
            _s=${service//\\/\\\\}; _s=${_s//\"/\\\"}
            apps_json+="${apps_json:+,}"$'\n'"    {\"app\": \"${_a}\", \"service\": \"${_s}\", \"reason\": \"ip_out_of_subnet\", \"stored_ip\": \"${ip}\", \"expected_subnet\": \"${NET_DOCKER_SUBNET}\", \"heal\": \"reset-ip\"}"
            ((conflict_count++))
        done
        if (( conflict_count > 0 )); then
            conflicts_found="true"; can_auto_heal="true"
        fi
    fi

    local apps_arr="[]"
    [[ -n "$apps_json" ]] && apps_arr="[${apps_json}"$'\n'"  ]"

    local temp_file; temp_file="$(mktemp)"
    cat << EOF > "$temp_file"
{
  "conflicts_found": ${conflicts_found},
  "conflict_count": ${conflict_count},
  "can_auto_heal": ${can_auto_heal},
  "network_name": "${CFG_NETWORK_NAME}",
  "docker_subnet": "${NET_DOCKER_SUBNET}",
  "config_subnet": "${CFG_NETWORK_SUBNET}",
  "apps": ${apps_arr},
  "error": ${error_json},
  "checked_at": "$(date -Iseconds)"
}
EOF
    runFileWrite "$final_file" < "$temp_file"; rm -f "$temp_file"
}
