#!/bin/bash

# Read-only network-drift scan — the shared detection used by both the WebUI
# status generator (webuiSystemNetworkCheck) and the heal verb (network heal),
# so the two never diverge.
#
# networkScanConflicts sets these globals (call it DIRECTLY, never in $(...) —
# a subshell would drop the globals):
#   NET_DAEMON_OK     "true"/"false" — docker daemon reachable
#   NET_PRESENT       "true"/"false" — the shared network ($CFG_NETWORK_NAME) exists
#   NET_DOCKER_SUBNET                — its real subnet CIDR (e.g. 10.123.154.0/24)
#   NET_SCAN_ERROR                   — human note when the daemon/network is off (else "")
#   NET_CONFLICTS  (array)           — one "app|service|ip" entry per active IP that
#                                      no longer falls inside the docker network's
#                                      real subnet (the "network recreated with a
#                                      different /24, app stranded" drift).
# Gateway-routed services (no live shared-net ipv4 in their deployed compose,
# e.g. gluetun-routed service-1) are skipped, so they don't false-positive.
#
# Nothing here mutates state.

# Is this app/service NOT live on the shared network? Routed via a gateway, or
# its ipv4 simply isn't present uncommented in the deployed compose -> skip it.
# We key on the IP (unique per service): a routed service has its whole
# `ipv4_address:` block commented out (GLUETUN_OFF region), so an uncommented
# assignment carrying this exact IP means it IS live on the shared net.
_netServiceIsRouted() {
    local app="$1" ip="$2"
    local compose="${containers_dir}${app}/docker-compose.yml"
    [[ -f "$compose" ]] || return 1   # no compose to consult -> don't skip
    local esc_ip="${ip//./\\.}"
    grep -Eq "^[[:space:]]*ipv4_address:[[:space:]]*${esc_ip}([[:space:]]|#|$)" "$compose" && return 1
    return 0
}

networkScanConflicts() {
    NET_DAEMON_OK="false"; NET_PRESENT="false"; NET_DOCKER_SUBNET=""; NET_SCAN_ERROR=""
    NET_CONFLICTS=()

    # Distinguish "daemon down" (transient/benign — never alarm on what we can't
    # verify) from "daemon up but our network is gone" (a real conflict).
    if ! dockerCommandRun "docker info" >/dev/null 2>&1; then
        NET_SCAN_ERROR="docker daemon unreachable"
        return 0
    fi
    NET_DAEMON_OK="true"

    NET_DOCKER_SUBNET=$(dockerCommandRun "docker network inspect $CFG_NETWORK_NAME --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'" 2>/dev/null | tr -d '[:space:]')
    if [[ -z "$NET_DOCKER_SUBNET" ]]; then
        NET_SCAN_ERROR="network '$CFG_NETWORK_NAME' not found"
        return 0
    fi
    NET_PRESENT="true"

    local rows app service ip
    rows=$(runInstallOp sqlite3 "$docker_dir/$db_file" \
        "SELECT app_name, service_name, resource_value FROM network_resources WHERE resource_type='ip' AND status='active';" 2>/dev/null)
    [[ -z "$rows" ]] && return 0

    while IFS='|' read -r app service ip; do
        [[ -z "$app" || -z "$ip" ]] && continue
        _netServiceIsRouted "$app" "$ip" && continue
        if ! ipInSubnet "$ip" "$NET_DOCKER_SUBNET"; then
            NET_CONFLICTS+=("${app}|${service}|${ip}")
        fi
    done <<< "$rows"
}
