#!/bin/bash
# ==============================================================================================
# ============================= Conf Auto-Populate =============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Reads credentials and settings from locally running services and writes
# them into the local host conf. Safe to run multiple times — only populates
# EMPTY fields, never overwrites existing values unless --overwrite is passed.
#
# After populating, pushes the updated conf to all partners via conf_sync.sh
# so they have the fresh keys in their /tmp/.cache/vv/d/ cache immediately.
#
# ==============================================================================================
# AUTO-DETECTED FIELDS
# ==============================================================================================
#
#   HOST CONF (Configurations/<hostid>.conf)
#   ──────────────────────────────────────────────────────────────────────────
#   HOSTN_OWNER               from hostname (strip unRAID- prefix, lowercase)
#   HOSTN_SSH_KEY             from hostname convention (/root/.ssh/<owner>_rsync_automation)
#   HOSTN_STORAGE_MODE_INTERNAL  from boot device transport (NVMe/SSD=true, USB=false)
#   HOSTN_RADARR_API_KEY      from Radarr config.xml (found via docker volume mount)
#   HOSTN_RADARR_URL          from Radarr config.xml port
#   HOSTN_RADARR_MOVIES_ROOT  from Radarr rootFolder API
#   HOSTN_RADARR_PATH_MAP     from Radarr docker volume mounts vs root folder path
#   HOSTN_SONARR_API_KEY      from Sonarr config.xml
#   HOSTN_SONARR_URL          from Sonarr config.xml port
#   HOSTN_SONARR_TV_ROOT      from Sonarr rootFolder API
#   HOSTN_SONARR_PATH_MAP     from Sonarr docker volume mounts vs root folder path
#   HOSTN_LIDARR_API_KEY      from Lidarr config.xml
#   HOSTN_LIDARR_URL          from Lidarr config.xml port
#   HOSTN_LIDARR_MUSIC_ROOT   from Lidarr rootFolder API
#   HOSTN_LIDARR_PATH_MAP     from Lidarr docker volume mounts vs root folder path
#   HOSTN_SABNZBD_API_KEY     from sabnzbd.ini
#   HOSTN_SABNZBD_URL         from sabnzbd.ini port
#   HOSTN_SLSKD_API_KEY       from slskd config.yml
#   HOSTN_SLSKD_URL           from slskd config.yml port
#   HOSTN_QBIT_URL            from qBittorrent.conf WebUI port
#   HOSTN_QBIT_USERNAME       from qBittorrent.conf WebUI username
#   HOSTN_QBIT_PASSWORD       from qBittorrent.conf WebUI password (plaintext only)
#   HOSTN_EMBY_CONTAINER      fuzzy match from docker ps
#   HOSTN_EMBY_URL            from docker port binding
#   HOSTN_JELLYFIN_CONTAINER  fuzzy match from docker ps
#   HOSTN_JELLYFIN_URL        from docker port binding
#   HOSTN_TRANSCODE_SSD       from Emby/Jellyfin container /transcode volume mount
#   HOSTN_AUTHELIA_CONTAINER  fuzzy match from docker ps
#   HOSTN_AUTHELIA_CONFIG     from Authelia container /config volume mount
#   HOSTN_SYS_WATCHDOG_NIC    from ip route default gateway interface
#
#   MASTER CONF (Configurations/master.conf)
#   ──────────────────────────────────────────────────────────────────────────
#   HOST1 / HOST2             local hostname written to MY_ID slot
#   GITEA_CONTAINER           fuzzy match from docker ps
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Resolve this host's conf from MY_ID
#   2. Per service, locate its container and read its own config file:
#        arrs      → config.xml via the container's /config volume mount
#        SABnzbd   → sabnzbd.ini
#        slskd     → config.yml
#        qBit      → qBittorrent.conf (plaintext WebUI credentials only)
#        Emby/JF   → docker port bindings and /transcode mount
#   3. Write each value only if the conf field is EMPTY, unless --overwrite
#   4. Push the updated conf to partners via conf_sync.sh, unless --no-push
#
# Container names are resolved by _resolve_container(): an exact name match wins, otherwise
# a prefix match must be unambiguous or the field is skipped.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Never Overwrite a Human's Value
#   Only empty fields are populated. A value already in the conf was either set deliberately
#   or populated from a service that has since changed — either way, the file wins over
#   detection. --overwrite exists for deliberate re-sync after a key rotation.
#
# Read From the Service, Not From Assumption
#   Every value comes out of the service's own config file or docker metadata — ports from
#   actual port bindings, paths from actual volume mounts. Nothing is derived from naming
#   convention where the real value is readable.
#
# Refuse to Guess a Container
#   An ambiguous prefix skips the field rather than picking one. Writing the wrong container
#   name is worse than writing nothing: an empty field is visibly incomplete and gets fixed,
#   while a wrong one silently points the whole stack at the wrong instance. This host has a
#   live example — "authelia" prefix-matches both Authelia and Authelia-Secondary.
#
# Push Immediately After Populating
#   Fresh credentials go to partners right away rather than waiting for the next scheduled
#   conf sync, so a partner is never authenticating with a key this host has already rotated.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Root Enforcement
#   Reads service config files owned by container users and writes the host conf.
#
# Host Detection
#   detect_hosts() resolves MY_ID, which selects which host conf is written. Populating the
#   wrong host's conf would write this machine's credentials into a partner's file.
#
# Conf Existence Guard
#   Aborts if the resolved host conf does not exist, rather than creating a partial one.
#
# Empty-Field-Only Writes
#   Existing values are preserved unless --overwrite is passed explicitly.
#
# Container Ambiguity Guard
#   _resolve_container() refuses a prefix matching more than one container, warning with the
#   full match list. Exact name matches short-circuit and are never treated as ambiguous.
#
# Missing Service Tolerance
#   A service that is not installed on this host is skipped with a log line. Absence is a
#   valid configuration, not a failure.
#
# Map Block Validation
#   Associative-array entries are only inserted when the target map actually exists in the
#   conf; a missing map warns and skips instead of appending an orphaned entry.
#
# Dry Run Support
#   --dry-run reports every value it would write, truncated, and writes nothing.
#
# Credential Truncation in Output
#   Detected secrets are printed truncated (first 8 chars) so a populate run can be pasted
#   into a log or issue without leaking full API keys.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# Reads and writes: Configurations/<hostid>.conf and Configurations/master.conf
#
# master.conf
#
#   DOCKER_APPDATA_BASE
#       Fallback appdata root when a container exposes no /config mount to read from.
#
# Everything else this script touches is a field it populates rather than one it consumes —
# see AUTO-DETECTED FIELDS above for the full list.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
#   conf_populate.sh              Populate empty fields only
#   conf_populate.sh --overwrite  Overwrite all detected fields (re-sync after arr key rotation)
#   conf_populate.sh --dry-run    Show what would be written, no changes
#   conf_populate.sh --log        Verbose output
#   conf_populate.sh --no-push    Skip pushing to partners after update
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../../load_config.sh"
SCRIPTS_ROOT="$SCRIPTS_DIR"

OVERWRITE=false
NO_PUSH=false
FILTERED_ARGS=()
for arg in "$@"; do
    case "$arg" in
        --overwrite) OVERWRITE=true ;;
        --no-push)   NO_PUSH=true ;;
        *)           FILTERED_ARGS+=("$arg") ;;
    esac
done
parse_args "${FILTERED_ARGS[@]}"
detect_hosts

[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }

CONF_FILE="$SCRIPTS_ROOT/Configurations/${MY_ID,,}.conf"
[[ ! -f "$CONF_FILE" ]] && { error "Conf file not found: $CONF_FILE"; exit 1; }

MASTER_CONF="$SCRIPTS_ROOT/Configurations/master.conf"

log "$ICON_GEAR Config: conf=${CONF_FILE} overwrite=${OVERWRITE:-false} no-push=${NO_PUSH:-false}"

UPDATED=0
SKIPPED=0

echo ""
echo "━━━ $ICON_GEAR Conf Auto-Populate — $MY_ID ($LOCAL_SERVER_NAME) ━━━"
echo ""
[[ "$DRY_RUN"   == true ]] && warn "DRY RUN — no changes will be made"
[[ "$OVERWRITE" == true ]] && warn "OVERWRITE mode — existing values will be replaced"

# ── Helper: write a var into a conf file if empty (or --overwrite) ────────────
# Optional 4th arg: target file (defaults to $CONF_FILE)
_set_conf_var() {
    local var_name="$1" value="$2" label="$3" target="${4:-$CONF_FILE}"
    [[ -z "$value" ]] && return

    local current
    current=$(grep -oP "(?<=^\s*${var_name}=\")[^\"]*" "$target" 2>/dev/null | head -1)

    if [[ -n "$current" ]] && [[ "$OVERWRITE" == false ]]; then
        log "$label: already set (${current:0:8}…) — skipping"
        (( SKIPPED++ ))
        return
    fi

    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — would set $var_name = ${value:0:8}…"
        return
    fi

    if grep -q "^\s*${var_name}=" "$target"; then
        sed -i "s|^\(\s*${var_name}\s*=\s*\)\"[^\"]*\"|\1\"${value}\"|" "$target"
    else
        printf '\n    %s="%s"\n' "$var_name" "$value" >> "$target"
    fi
    info "$label: set ✅"
    (( UPDATED++ ))
}

# ── Helper: add or update an entry in a declare -A map block ──────────────────
# Inserts [key]="value" before the closing ) of the named map in $CONF_FILE.
# Skips if an identical uncommented entry already exists (unless --overwrite).
_set_conf_map_entry() {
    local map_name="$1" key="$2" value="$3" label="$4"
    [[ -z "$key" || -z "$value" ]] && return

    if grep -qP "^\s*\[\"${key//\//\\/}\"\]=" "$CONF_FILE" 2>/dev/null; then
        if [[ "$OVERWRITE" == false ]]; then
            log "$label: already set — skipping"
            (( SKIPPED++ ))
            return
        fi
        [[ "$DRY_RUN" == true ]] && { warn "DRY RUN — would update ${map_name}[\"${key}\"]"; return; }
        sed -i "s|^\(\s*\)\[\"${key}\"\]=\"[^\"]*\"|\1[\"${key}\"]=\"${value}\"|" "$CONF_FILE"
        info "$label: set ✅"
        (( UPDATED++ ))
        return
    fi

    if ! grep -q "declare -A ${map_name}=" "$CONF_FILE" 2>/dev/null; then
        warn "$label: ${map_name} not found in conf — skipping"
        return
    fi

    [[ "$DRY_RUN" == true ]] && { warn "DRY RUN — would set ${map_name}[\"${key}\"] = \"${value}\""; return; }

    awk -v mapname="${map_name}" -v key="$key" -v val="$value" '
    BEGIN { in_map=0; done=0 }
    !done && index($0, "declare -A " mapname) { in_map=1 }
    in_map && !done && /^\s*\)/ {
        printf "        [\"%s\"]=\"%s\"\n", key, val
        done=1; in_map=0
    }
    { print }
    ' "$CONF_FILE" > "${CONF_FILE}.tmp" && mv "${CONF_FILE}.tmp" "$CONF_FILE" || {
        rm -f "${CONF_FILE}.tmp"
        warn "$label: failed to update ${map_name}"
        return 1
    }
    info "$label: set ✅"
    (( UPDATED++ ))
}

# ── Helper: resolve a container name from a prefix, refusing ambiguity ────────
# grep -m1 silently returns whichever name docker happens to list first. On this very host
# "^authelia" matches both Authelia (9091, primary) and Authelia-Secondary (9092), and -m1
# picks the secondary — writing the wrong container into the conf that every other script
# then trusts. An exact match wins outright; otherwise a prefix match must be unambiguous.
# Same rule detect_hosts() applies to host identity: exactly one candidate, or none.
_resolve_container() {
    local pattern="$1"
    local all exact matches count

    all=$(docker ps -a --format '{{.Names}}' 2>/dev/null)
    [[ -z "$all" ]] && return 1

    # Exact name match short-circuits — "Authelia" is not ambiguous with "Authelia-Secondary"
    exact=$(printf '%s\n' "$all" | grep -ixm1 -- "$pattern")
    [[ -n "$exact" ]] && { echo "$exact"; return 0; }

    matches=$(printf '%s\n' "$all" | grep -i -- "^${pattern}")
    count=$(printf '%s\n' "$matches" | grep -c .)

    if [[ "$count" -gt 1 ]]; then
        warn "Container prefix '${pattern}' is ambiguous — matches: $(printf '%s' "$matches" | tr '\n' ' ')"
        warn "  Refusing to guess. Set the container name manually in host*.conf."
        return 1
    fi

    [[ "$count" -eq 1 ]] && { printf '%s\n' "$matches"; return 0; }
    return 1
}

# ── Helper: find arr config dir via docker volume mount ───────────────────────
_arr_config_dir() {
    local pattern="$1"
    local container_name
    container_name=$(_resolve_container "$pattern") || return 1
    [[ -z "$container_name" ]] && return 1

    local config_path
    config_path=$(docker inspect "$container_name" 2>/dev/null | \
        jq -r '.[0].Mounts[]? | select(.Destination == "/config") | .Source' 2>/dev/null | head -1)
    [[ -z "$config_path" ]] && config_path="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}/${container_name}"

    [[ -d "$config_path" ]] && echo "$config_path" || return 1
}

# ── Helper: read XML tag value ────────────────────────────────────────────────
_xml_val() {
    local file="$1" tag="$2"
    grep -oP "(?<=<${tag}>)[^<]+" "$file" 2>/dev/null | head -1
}

# ── Helper: get host-side port for a container's internal port ────────────────
_docker_host_port() {
    local container="$1" container_port="$2"
    docker inspect "$container" 2>/dev/null | \
        jq -r --arg p "${container_port}/tcp" \
        '.[0].NetworkSettings.Ports[$p]?[0].HostPort // empty' 2>/dev/null | head -1
}

# ── Helper: get host path for a container destination mount ──────────────────
_docker_volume_host() {
    local container="$1" dest="$2"
    docker inspect "$container" 2>/dev/null | \
        jq -r --arg d "$dest" \
        '.[0].Mounts[]? | select(.Destination == $d) | .Source' 2>/dev/null | head -1
}

# ── Helper: find host↔container path mapping for a given container path ───────
# Finds the most specific mount whose destination is a prefix of container_path
# and where source != destination (i.e. an actual remapping exists).
# Outputs "source|destination" or nothing if no remapping found.
_docker_path_map() {
    local container="$1" container_path="$2"
    docker inspect "$container" 2>/dev/null | \
        jq -r '.[0].Mounts[]? | select(.Destination != "/config") | "\(.Source)|\(.Destination)"' \
        2>/dev/null | \
        awk -F'|' -v target="$container_path" '
            $1 != $2 && length($2) > 0 && index(target, $2) == 1 { print length($2), $0 }
        ' | sort -rn | head -1 | cut -d' ' -f2-
}

# ==============================================================================================
# ── Owner short name + SSH key ────────────────────────────────────────────────────────────────
# ==============================================================================================

owner=$(echo "$LOCAL_SERVER_NAME" | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//i' | tr '[:upper:]' '[:lower:]')
_set_conf_var "${MY_ID}_OWNER"   "$owner"                                      "Owner short name"
_set_conf_var "${MY_ID}_SSH_KEY" "/root/.ssh/${owner}_rsync_automation"        "SSH key path"

# ==============================================================================================
# ── Arr API keys + URLs + root paths + path maps ──────────────────────────────────────────────
# ==============================================================================================

for arr in radarr sonarr lidarr; do
    arr_upper="${arr^^}"
    arr_container=$(_resolve_container "$arr")
    config_dir=$(_arr_config_dir "$arr") || {
        log "${arr_upper}: no running container found — skipping"
        continue
    }
    config_xml="${config_dir}/config.xml"

    if [[ ! -f "$config_xml" ]]; then
        log "${arr_upper}: config.xml not found at $config_xml — skipping"
        continue
    fi

    key=$(_xml_val "$config_xml" "ApiKey")
    port=$(_xml_val "$config_xml" "Port")
    port="${port:-$(case $arr in radarr) echo 7878;; sonarr) echo 8989;; lidarr) echo 8686;; esac)}"
    url_base="http://localhost:${port}"

    _set_conf_var "${MY_ID}_${arr_upper}_API_KEY" "$key"      "${arr_upper} API key"
    _set_conf_var "${MY_ID}_${arr_upper}_URL"     "$url_base" "${arr_upper} URL"

    if [[ -n "$key" ]]; then
        case "$arr" in lidarr) api_ver="v1" ;; *) api_ver="v3" ;; esac
        root_json=$(curl -sf --max-time 5 \
            -H "X-Api-Key: $key" "${url_base}/api/${api_ver}/rootfolder" 2>/dev/null)
        root_path=$(echo "$root_json" | jq -r '.[0].path // empty' 2>/dev/null)

        case "$arr" in
            radarr) _set_conf_var "${MY_ID}_RADARR_MOVIES_ROOT" "$root_path" "Radarr movies root" ;;
            sonarr) _set_conf_var "${MY_ID}_SONARR_TV_ROOT"     "$root_path" "Sonarr TV root"     ;;
            lidarr) _set_conf_var "${MY_ID}_LIDARR_MUSIC_ROOT"  "$root_path" "Lidarr music root"  ;;
        esac

        if [[ -n "$root_path" && -n "$arr_container" ]]; then
            map_entry=$(_docker_path_map "$arr_container" "$root_path")
            if [[ -n "$map_entry" ]]; then
                map_src="${map_entry%%|*}"
                map_dest="${map_entry##*|}"
                case "$arr" in
                    radarr) _set_conf_map_entry "${MY_ID}_RADARR_PATH_MAP" "$map_dest" "$map_src" "Radarr path map" ;;
                    sonarr) _set_conf_map_entry "${MY_ID}_SONARR_PATH_MAP" "$map_dest" "$map_src" "Sonarr path map" ;;
                    lidarr) _set_conf_map_entry "${MY_ID}_LIDARR_PATH_MAP" "$map_dest" "$map_src" "Lidarr path map" ;;
                esac
            fi
        fi
    fi
done

# ==============================================================================================
# ── SABnzbd API key + URL ─────────────────────────────────────────────────────────────────────
# ==============================================================================================

sab_dir=$(_arr_config_dir "sabnzbd") && {
    sab_ini=$(find "$sab_dir" -maxdepth 2 -name "sabnzbd.ini" 2>/dev/null | head -1)
    if [[ -f "$sab_ini" ]]; then
        sab_key=$(grep -oP  '(?<=^api_key\s*=\s*)\S+'  "$sab_ini" 2>/dev/null | head -1)
        sab_port=$(grep -oP '(?<=^port\s*=\s*)\d+'     "$sab_ini" 2>/dev/null | head -1)
        _set_conf_var "${MY_ID}_SABNZBD_API_KEY" "$sab_key"                           "SABnzbd API key"
        _set_conf_var "${MY_ID}_SABNZBD_URL"     "http://localhost:${sab_port:-8080}" "SABnzbd URL"
    fi
}

# ==============================================================================================
# ── slskd API key + URL ───────────────────────────────────────────────────────────────────────
# ==============================================================================================

slskd_dir=$(_arr_config_dir "slskd") && {
    slskd_yml=$(find "$slskd_dir" -maxdepth 2 \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | head -1)
    if [[ -f "$slskd_yml" ]]; then
        slskd_key=$(grep -oP '(?<=api_key:\s)[\w-]+'  "$slskd_yml" 2>/dev/null | head -1)
        [[ -z "$slskd_key" ]] && \
            slskd_key=$(grep -oP '(?<=apikey:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
        slskd_port=$(grep -oP '(?<=port:\s)\d+' "$slskd_yml" 2>/dev/null | head -1)
        _set_conf_var "${MY_ID}_SLSKD_API_KEY" "$slskd_key"                           "slskd API key"
        _set_conf_var "${MY_ID}_SLSKD_URL"     "http://localhost:${slskd_port:-5030}" "slskd URL"
    fi
}

# ==============================================================================================
# ── qBittorrent URL + credentials ────────────────────────────────────────────────────────────
# ==============================================================================================

qbit_dir=$(_arr_config_dir "qbittorrent") && {
    qbit_conf=$(find "$qbit_dir" -maxdepth 3 -name "qBittorrent.conf" 2>/dev/null | head -1)
    if [[ -f "$qbit_conf" ]]; then
        qbit_port=$(grep -oP '(?<=WebUI\\Port=)\d+'    "$qbit_conf" 2>/dev/null | head -1)
        qbit_user=$(grep -oP '(?<=WebUI\\Username=)\S+' "$qbit_conf" 2>/dev/null | head -1)
        # Only capture plaintext password — PBKDF2 hashes are not usable
        qbit_pass=$(grep -oP '(?<=WebUI\\Password=)[^\r\n]+' "$qbit_conf" 2>/dev/null | \
            grep -v '@ByteArray' | head -1)
        _set_conf_var "${MY_ID}_QBIT_URL"      "http://localhost:${qbit_port:-8080}" "qBittorrent URL"
        _set_conf_var "${MY_ID}_QBIT_USERNAME" "$qbit_user"                          "qBittorrent username"
        _set_conf_var "${MY_ID}_QBIT_PASSWORD" "$qbit_pass"                          "qBittorrent password"
    fi
}

# ==============================================================================================
# ── Media server container names + URLs + transcode path ─────────────────────────────────────
# ==============================================================================================

transcode_dir=""

for pattern in "emby" "jellyfin"; do
    container=$(_resolve_container "$pattern") || continue
    [[ -z "$container" ]] && continue

    case "$pattern" in
        emby)
            host_port=$(_docker_host_port "$container" "8096")
            _set_conf_var "${MY_ID}_EMBY_CONTAINER" "$container"                          "Emby container name"
            _set_conf_var "${MY_ID}_EMBY_URL"       "http://localhost:${host_port:-8096}" "Emby URL"
            ;;
        jellyfin)
            host_port=$(_docker_host_port "$container" "8096")
            _set_conf_var "${MY_ID}_JELLYFIN_CONTAINER" "$container"                          "Jellyfin container name"
            _set_conf_var "${MY_ID}_JELLYFIN_URL"       "http://localhost:${host_port:-8095}" "Jellyfin URL"
            ;;
    esac

    # Transcode path: first container with a /transcode mount wins
    if [[ -z "$transcode_dir" ]]; then
        transcode_dir=$(_docker_volume_host "$container" "/transcode")
    fi
done

[[ -n "$transcode_dir" ]] && \
    _set_conf_var "${MY_ID}_TRANSCODE_SSD" "${transcode_dir%/}/" "Transcode SSD path"

# ==============================================================================================
# ── Authelia container + config path ──────────────────────────────────────────────────────────
# ==============================================================================================

authelia_container=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -im1 "authelia")
if [[ -n "$authelia_container" ]]; then
    _set_conf_var "${MY_ID}_AUTHELIA_CONTAINER" "$authelia_container" "Authelia container"
    authelia_cfg_dir=$(_docker_volume_host "$authelia_container" "/config")
    [[ -n "$authelia_cfg_dir" ]] && \
        _set_conf_var "${MY_ID}_AUTHELIA_CONFIG" "${authelia_cfg_dir}/configuration.yml" "Authelia config path"
fi

# ==============================================================================================
# ── Network interface ─────────────────────────────────────────────────────────────────────────
# ==============================================================================================

nic=$(ip route show default 2>/dev/null | grep -oP '(?<=dev )\S+' | head -1)
_set_conf_var "${MY_ID}_SYS_WATCHDOG_NIC" "$nic" "Default NIC"

# ==============================================================================================
# ── Storage mode (internal NVMe/SSD vs USB flash boot) ───────────────────────────────────────
# ==============================================================================================

boot_src=$(findmnt -n -o SOURCE /boot 2>/dev/null)
if [[ -n "$boot_src" ]]; then
    boot_dev=$(lsblk -no PKNAME "$boot_src" 2>/dev/null | head -1)
    if [[ "$boot_dev" =~ ^nvme ]]; then
        storage_mode=true
    else
        boot_transport=$(cat "/sys/block/${boot_dev}/device/transport" 2>/dev/null)
        [[ "$boot_transport" == "usb" ]] && storage_mode=false || storage_mode=true
    fi
    _set_conf_var "${MY_ID}_STORAGE_MODE_INTERNAL" "$storage_mode" \
        "Storage mode (NVMe/SSD=true, USB=false)"
fi

# ==============================================================================================
# ── Master conf: host identity + Gitea container ─────────────────────────────────────────────
# ==============================================================================================

if [[ -f "$MASTER_CONF" ]]; then
    echo ""
    echo "  ── Master conf ──"
    _set_conf_var "$MY_ID" "$LOCAL_SERVER_NAME" "master.conf ${MY_ID} hostname" "$MASTER_CONF"

    gitea_container=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -im1 "gitea")
    [[ -n "$gitea_container" ]] && \
        _set_conf_var "GITEA_CONTAINER" "$gitea_container" "Gitea container" "$MASTER_CONF"
fi

# ==============================================================================================
# ── Summary + push ────────────────────────────────────────────────────────────────────────────
# ==============================================================================================

echo ""
echo "━━━━━ $ICON_SUMMARY Conf Populate Summary ━━━━━"
echo "  Updated: $UPDATED field(s)"
echo "  Skipped: $SKIPPED already set"
echo "  Conf:    $CONF_FILE"
echo "━━━━━━━━━━━━━━━━━━━━━"

if [[ "$UPDATED" -gt 0 ]] && [[ "$DRY_RUN" == false ]] && [[ "$NO_PUSH" == false ]]; then
    echo ""
    info "Pushing updated conf to partners..."
    bash "$SCRIPTS_ROOT/System_Essentials/conf_sync.sh" --push-only "${EXTRA_FLAGS[@]}" || true
fi
