#!/bin/bash

# Pre-flight checker for app installs.
#
# Each app declares its prerequisites in its .config as
#   CFG_<APP>_REQUIRES="<csv>"
# where <csv> is a comma-separated list of requirement keys (see the
# `case` arms below for the full set). Examples:
#   CFG_AUTHELIA_REQUIRES="domain,traefik"
#   CFG_BOOKSTACK_REQUIRES="domain,traefik"
#
# Call this as the first step inside an app's install lifecycle:
#
#   if ! appInstallCheckRequirements "$app_name" "$CFG_AUTHELIA_REQUIRES"; then
#       return 1
#   fi
#
# Returns 0 if all requirements are met, 1 if any are missing. On
# failure, prints one line per missing requirement so the user can
# resolve them in order.
appInstallCheckRequirements()
{
    local app_name="$1"
    local reqs_csv="$2"

    [[ -z "$reqs_csv" ]] && return 0

    local missing=()
    local req
    local IFS=','
    for req in $reqs_csv; do
        # Trim whitespace.
        req="${req#"${req%%[![:space:]]*}"}"
        req="${req%"${req##*[![:space:]]}"}"
        [[ -z "$req" ]] && continue

        case "$req" in
            "domain")
                if ! _appReqHasDomain; then
                    missing+=("Set at least one CFG_DOMAIN_N (General → Network) before installing $app_name.")
                fi
                ;;
            "traefik")
                if ! _appReqServiceInstalled "traefik"; then
                    missing+=("Install Traefik first — $app_name needs a reverse proxy to publish itself.")
                fi
                ;;
            "gluetun")
                if ! _appReqServiceInstalled "gluetun"; then
                    missing+=("Install Gluetun first — $app_name expects a VPN gateway to route through.")
                fi
                ;;
            "authelia")
                if ! _appReqServiceInstalled "authelia"; then
                    missing+=("Install Authelia first — $app_name's auth integration depends on it.")
                fi
                ;;
            "headscale")
                if ! _appReqServiceInstalled "headscale"; then
                    missing+=("Install Headscale first.")
                fi
                ;;
            "prometheus")
                if ! _appReqServiceInstalled "prometheus"; then
                    missing+=("Install Prometheus first — $app_name has nothing to query without it.")
                fi
                ;;
            "mail")
                if [[ "$CFG_MAIL_ENABLED" != "true" ]]; then
                    missing+=("Configure global mail (CFG_MAIL_ENABLED=true under General → Mail) first.")
                fi
                ;;
            *)
                isNotice "Unknown requirement '$req' declared by $app_name — ignoring."
                ;;
        esac
    done
    unset IFS

    if [[ ${#missing[@]} -gt 0 ]]; then
        isError "Cannot install $app_name — prerequisites are not met:"
        local m
        for m in "${missing[@]}"; do
            isError "  • $m"
        done
        return 1
    fi
    return 0
}

# True if any CFG_DOMAIN_<n> is set to a non-empty value.
_appReqHasDomain()
{
    local i var val
    for i in 1 2 3 4 5 6 7 8 9; do
        var="CFG_DOMAIN_$i"
        val="${!var}"
        [[ -n "$val" ]] && return 0
    done
    return 1
}

# Thin wrapper so we can use either dockerCheckAppInstalled or whatever
# convenience helper a future refactor introduces, without rewriting the
# call sites in here.
_appReqServiceInstalled()
{
    local svc="$1"
    if declare -f checkServiceInstalled >/dev/null 2>&1; then
        checkServiceInstalled "$svc" && return 0 || return 1
    fi
    if declare -f dockerCheckAppInstalled >/dev/null 2>&1; then
        local status
        status=$(dockerCheckAppInstalled "$svc" "docker" 2>/dev/null)
        [[ "$status" == "installed" ]] && return 0 || return 1
    fi
    # Fallback: ask docker directly.
    sudo docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${svc}-service$"
}
