#!/bin/bash

# Per-app routine WebUI-refresh hook (appWebuiRefresh_<app>): webui_updater calls
# it on every WebUI update while gluetun is installed; the installer (gluetun.sh)
# and the 'gluetun_refresh_providers' tool call it directly too.
#
# Fetches gluetun's upstream servers.json, slims it down to a
# { providers: { <name>: { vpnTypes, countries } } } shape, and writes it
# to the WebUI data dir so the per-app config dropdowns stay honest as
# gluetun adds/removes providers and protocols. Falls back silently to the
# previous snapshot (or the bundled default) on network failure.
appWebuiRefresh_gluetun() {
    local output_file="${containers_dir}libreportal/frontend/data/apps/generated/gluetun-providers.json"
    local upstream="https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json"
    local tmp="$(mktemp)"
    local raw="${output_file}.raw.$$"

    runFileOp mkdir -p "$(dirname "$output_file")"

    if ! command -v jq >/dev/null 2>&1; then
        isNotice "jq not installed; skipping gluetun provider refresh."
        return 0
    fi

    # GitHub raw only sends ETag (no Last-Modified), so use If-None-Match
    # via a sidecar to skip the 7MB body when nothing has changed upstream.
    local etag_file="${output_file}.etag"
    local etag=""
    [[ -s "$etag_file" ]] && etag=$(<"$etag_file")
    local headers="${output_file}.hdr.$$"
    local http_code
    http_code=$(curl -sSL \
            ${etag:+-H "If-None-Match: $etag"} \
            --speed-limit 1000 --speed-time 30 \
            --retry 2 --retry-delay 2 --retry-all-errors \
            -D "$headers" -o "$raw" \
            -w '%{http_code}' "$upstream") || http_code=""

    if [[ "$http_code" == "304" ]]; then
        rm -f "$raw" "$headers"
        return 0
    fi
    if [[ "$http_code" != "200" ]]; then
        isNotice "Upstream fetch failed (${http_code:-no response}); keeping existing snapshot."
        rm -f "$raw" "$headers"
        return 0
    fi
    local new_etag
    new_etag=$(awk 'tolower($1)=="etag:"{print $2}' "$headers" | tr -d '\r')
    rm -f "$headers"

    # servers.json is a top-level object keyed by provider; each provider
    # entry has a `servers` array whose items have `vpn` (wireguard|openvpn),
    # `country`, `city`, etc. We collapse that into per-provider unique
    # vpn-type and country lists. Drop the `version` key (it's not a provider).
    if ! jq '
        [ to_entries[]
          | select(.key != "version")
          | { key: .key,
              value: {
                vpnTypes: ((.value.servers // []) | map(.vpn) | unique | map(select(. != null and . != ""))),
                countries: ((.value.servers // []) | map(.country) | unique | map(select(. != null and . != "")))
              }
            }
        ]
        | from_entries
        | { providers: . }
    ' "$raw" > "$tmp" 2>/dev/null; then
        isNotice "Failed to parse gluetun servers.json; keeping existing provider snapshot."
        rm -f "$raw" "$tmp"
        return 0
    fi

    rm -f "$raw"

    if [ -s "$tmp" ]; then
        runFileWrite "$output_file" < "$tmp"; rm -f "$tmp"
        [[ -n "$new_etag" ]] && echo "$new_etag" | tee "$etag_file" >/dev/null
        isSuccessful "Refreshed gluetun provider snapshot ($(jq '.providers | length' "$output_file") providers)."
    else
        rm -f "$tmp"
        isNotice "Empty gluetun snapshot generated; ignoring."
    fi
}
