#!/usr/bin/env bash
set -euo pipefail

POOL_FILE="${PHP_FPM_POOL_FILE:-/etc/php/8.3/fpm/pool.d/microfinance.conf}"
NGINX_SITE_FILE="${NGINX_SITE_FILE:-/etc/nginx/sites-enabled/microfinance}"
EXPECTED_SOCKET="${EXPECTED_PHP_FPM_SOCKET:-/run/php/php8.3-fpm-microfinance.sock}"

MIN_UPLOAD_BYTES=$((20 * 1024 * 1024))
MIN_POST_BYTES=$((25 * 1024 * 1024))
MIN_MEMORY_BYTES=$((256 * 1024 * 1024))
MIN_EXECUTION_SECONDS=120
MIN_TERMINATE_SECONDS=180

failures=()

fail() {
    failures+=("$1")
}

pool_value() {
    local key="$1"

    awk -F= -v wanted="$key" '
        /^[[:space:]]*[;#]/ { next }
        NF < 2 { next }
        {
            left=$1
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", left)
            if (left == wanted) {
                value=substr($0, index($0, "=") + 1)
                gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
                print value
            }
        }
    ' "$POOL_FILE" | tail -n 1
}

nginx_value() {
    local key="$1"

    awk -v wanted="$key" '
        {
            line=$0
            sub(/#.*/, "", line)
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
            if (line == "") next
            split(line, fields, /[[:space:]]+/)
            if (fields[1] == wanted) {
                value=fields[2]
                sub(/;$/, "", value)
                print value
            }
        }
    ' "$NGINX_SITE_FILE" | tail -n 1
}

to_bytes() {
    local raw="${1^^}"

    if [[ "$raw" =~ ^([0-9]+)([KMG])?$ ]]; then
        local value="${BASH_REMATCH[1]}"
        local unit="${BASH_REMATCH[2]:-}"
        case "$unit" in
            K) echo $((value * 1024)) ;;
            M) echo $((value * 1024 * 1024)) ;;
            G) echo $((value * 1024 * 1024 * 1024)) ;;
            *) echo "$value" ;;
        esac
        return 0
    fi

    return 1
}

to_seconds() {
    local raw="${1,,}"

    if [[ "$raw" =~ ^([0-9]+)(s|m|h)?$ ]]; then
        local value="${BASH_REMATCH[1]}"
        local unit="${BASH_REMATCH[2]:-s}"
        case "$unit" in
            s) echo "$value" ;;
            m) echo $((value * 60)) ;;
            h) echo $((value * 3600)) ;;
        esac
        return 0
    fi

    return 1
}

require_file() {
    local path="$1"
    local label="$2"

    if [[ ! -r "$path" ]]; then
        echo "FAIL: $label is not readable: $path" >&2
        exit 1
    fi
}

check_size() {
    local label="$1"
    local raw="$2"
    local minimum="$3"
    local bytes

    if [[ -z "$raw" ]]; then
        fail "$label is missing."
        return
    fi

    if ! bytes="$(to_bytes "$raw")"; then
        fail "$label has an unsupported value: $raw"
        return
    fi

    if (( bytes < minimum )); then
        fail "$label is too low: $raw"
    else
        echo "PASS: $label = $raw"
    fi
}

check_duration() {
    local label="$1"
    local raw="$2"
    local minimum="$3"
    local seconds

    if [[ -z "$raw" ]]; then
        fail "$label is missing."
        return
    fi

    if ! seconds="$(to_seconds "$raw")"; then
        fail "$label has an unsupported value: $raw"
        return
    fi

    if (( seconds < minimum )); then
        fail "$label is too low: $raw"
    else
        echo "PASS: $label = $raw"
    fi
}

require_file "$POOL_FILE" "PHP-FPM pool configuration"
require_file "$NGINX_SITE_FILE" "Nginx site configuration"

echo "Microfinance PHP runtime preflight"
echo "Pool:  $POOL_FILE"
echo "Nginx: $NGINX_SITE_FILE"
echo

pool_socket="$(pool_value 'listen')"
nginx_socket="$(nginx_value 'fastcgi_pass')"
nginx_socket="${nginx_socket#unix:}"

if [[ "$pool_socket" != "$EXPECTED_SOCKET" ]]; then
    fail "PHP-FPM listen socket must be $EXPECTED_SOCKET (found: ${pool_socket:-missing})."
else
    echo "PASS: PHP-FPM listen socket matches the deployment baseline."
fi

if [[ "$nginx_socket" != "$EXPECTED_SOCKET" ]]; then
    fail "Nginx fastcgi_pass must use $EXPECTED_SOCKET (found: ${nginx_socket:-missing})."
else
    echo "PASS: Nginx and PHP-FPM use the same socket."
fi

upload_raw="$(pool_value 'php_admin_value[upload_max_filesize]')"
post_raw="$(pool_value 'php_admin_value[post_max_size]')"
memory_raw="$(pool_value 'php_admin_value[memory_limit]')"
execution_raw="$(pool_value 'php_admin_value[max_execution_time]')"
terminate_raw="$(pool_value 'request_terminate_timeout')"
nginx_body_raw="$(nginx_value 'client_max_body_size')"

check_size "PHP upload_max_filesize" "$upload_raw" "$MIN_UPLOAD_BYTES"
check_size "PHP post_max_size" "$post_raw" "$MIN_POST_BYTES"
check_size "PHP memory_limit" "$memory_raw" "$MIN_MEMORY_BYTES"
check_duration "PHP max_execution_time" "$execution_raw" "$MIN_EXECUTION_SECONDS"
check_duration "PHP-FPM request_terminate_timeout" "$terminate_raw" "$MIN_TERMINATE_SECONDS"
check_size "Nginx client_max_body_size" "$nginx_body_raw" "$MIN_POST_BYTES"

upload_bytes="$(to_bytes "${upload_raw:-0}" 2>/dev/null || echo 0)"
post_bytes="$(to_bytes "${post_raw:-0}" 2>/dev/null || echo 0)"
nginx_body_bytes="$(to_bytes "${nginx_body_raw:-0}" 2>/dev/null || echo 0)"

if (( post_bytes < upload_bytes )); then
    fail "PHP post_max_size must be greater than or equal to upload_max_filesize."
else
    echo "PASS: PHP post_max_size can contain the configured upload size."
fi

if (( nginx_body_bytes < post_bytes )); then
    fail "Nginx client_max_body_size must be greater than or equal to PHP post_max_size."
else
    echo "PASS: Nginx request-body limit can contain the configured PHP POST size."
fi

if (( ${#failures[@]} > 0 )); then
    echo
    for message in "${failures[@]}"; do
        echo "FAIL: $message" >&2
    done
    echo "PHP runtime preflight failed." >&2
    exit 1
fi

echo
echo "PHP runtime preflight passed."
