#!/bin/bash
set -u

PACKAGE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
APP_ROOT="${TRIM_APPDEST:-$PACKAGE_ROOT}"
DATA_ROOT="${TRIM_PKGVAR:-$PACKAGE_ROOT/var}"
VOLUME_ROOT="${TRIM_APPDEST_VOL:-$DATA_ROOT/volume}"
RECORD_ROOT="${LIVE_RECORDER_RECORD_ROOT:-$VOLUME_ROOT/@appshare/liverecorder/records}"
RUNTIME_ROOT="$APP_ROOT/runtime"
SERVER="$APP_ROOT/server/LiveRecorder.WebApi"
PG_BIN="$RUNTIME_ROOT/usr/lib/postgresql/15/bin"
PG_SHARE="$RUNTIME_ROOT/usr/share/postgresql/15"
PG_DATA="$DATA_ROOT/postgres"
RUN_ROOT="$DATA_ROOT/run"
LOG_ROOT="$DATA_ROOT/log"
APP_PID_FILE="$RUN_ROOT/liverecorder.pid"
APP_LOG="$LOG_ROOT/liverecorder.log"
PG_LOG="$LOG_ROOT/postgresql.log"
ADMIN_PASSWORD_FILE="$DATA_ROOT/admin-password.seed"
PG_PORT="${LIVE_RECORDER_POSTGRES_PORT:-54329}"
SERVICE_PORT="${TRIM_SERVICE_PORT:-18080}"
RUNTIME_LIBRARY_PATH="$RUNTIME_ROOT/lib/x86_64-linux-gnu:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/pulseaudio:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/blas:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/lapack:$RUNTIME_ROOT/usr/lib/postgresql/15/lib"

log_message() {
    mkdir -p "$LOG_ROOT"
    printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >>"$APP_LOG"
}

app_pid() {
    if [ -f "$APP_PID_FILE" ]; then
        pid=$(sed -n '1p' "$APP_PID_FILE" | tr -d '[:space:]')
        if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
            printf '%s' "$pid"
            return 0
        fi
    fi
    return 1
}

run_pg() {
    env LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH" PATH="$PG_BIN:$RUNTIME_ROOT/usr/bin:/usr/bin:/bin" "$@"
}

run_native() {
    env LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH" PATH="$RUNTIME_ROOT/usr/bin:/usr/bin:/bin" "$@"
}

postgres_running() {
    [ -x "$PG_BIN/pg_ctl" ] || return 1
    [ -f "$PG_DATA/PG_VERSION" ] || return 1
    run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" status >/dev/null 2>&1
}

initialize_postgres() {
    if [ -f "$PG_DATA/PG_VERSION" ]; then
        return 0
    fi

    mkdir -p "$PG_DATA" "$RUN_ROOT"
    chmod 0700 "$PG_DATA" "$RUN_ROOT"
    if ! run_pg "$PG_BIN/initdb" \
        -D "$PG_DATA" \
        -L "$PG_SHARE" \
        --username=liverecorder \
        --auth-local=trust \
        --auth-host=reject \
        --encoding=UTF8 \
        --no-locale >>"$PG_LOG" 2>&1; then
        log_message "PostgreSQL 初始化失败。"
        return 1
    fi

    {
        printf "listen_addresses = ''\n"
        printf "port = %s\n" "$PG_PORT"
        printf "unix_socket_directories = '%s'\n" "$RUN_ROOT"
        printf "max_connections = 40\n"
        printf "shared_buffers = '64MB'\n"
        printf "timezone = 'UTC'\n"
        printf "log_timezone = 'UTC'\n"
    } >>"$PG_DATA/postgresql.conf"
    return 0
}

start_postgres() {
    if postgres_running; then
        return 0
    fi
    initialize_postgres || return 1
    if ! run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" -l "$PG_LOG" -w start; then
        log_message "PostgreSQL 启动失败。"
        return 1
    fi

    database_exists=$(run_pg "$PG_BIN/psql" \
        -h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder -d postgres -Atqc \
        "SELECT 1 FROM pg_database WHERE datname = 'live_recorder'" 2>/dev/null || true)
    if [ "$database_exists" != "1" ]; then
        if ! run_pg "$PG_BIN/createdb" \
            -h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder live_recorder >>"$PG_LOG" 2>&1; then
            log_message "Live Recorder 数据库创建失败。"
            return 1
        fi
    fi
    return 0
}

stop_postgres() {
    if postgres_running; then
        run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" -m fast -w stop >>"$PG_LOG" 2>&1 || true
    fi
}

launch_app_process() {
    (
        export ASPNETCORE_ENVIRONMENT=Production
        export ASPNETCORE_URLS="http://0.0.0.0:$SERVICE_PORT"
        export ConnectionStrings__DefaultConnection="Host=$RUN_ROOT;Port=$PG_PORT;Database=live_recorder;Username=liverecorder;Timeout=15;Command Timeout=120;Keepalive=30"
        export LIVE_RECORDER_DEFAULT_OUTPUT_ROOT="$RECORD_ROOT"
        if [ -f "$ADMIN_PASSWORD_FILE" ]; then
            LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD=$(sed -n '1p' "$ADMIN_PASSWORD_FILE")
            export LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD
        fi
        export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
        export DOTNET_BUNDLE_EXTRACT_BASE_DIR="$DATA_ROOT/dotnet-bundle"
        export XDG_CACHE_HOME="$DATA_ROOT/cache"
        export TMPDIR="$DATA_ROOT/tmp"
        export LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH"
        export PATH="$RUNTIME_ROOT/usr/bin:/usr/bin:/bin"
        mkdir -p "$XDG_CACHE_HOME" "$TMPDIR"
        cd "$APP_ROOT/server" || exit 1
        exec "$SERVER"
    ) >>"$APP_LOG" 2>&1 &
    APP_PROCESS_PID=$!
    printf '%s\n' "$APP_PROCESS_PID" >"$APP_PID_FILE"
}

start_app() {
    mkdir -p "$DATA_ROOT" "$RUN_ROOT" "$LOG_ROOT" "$RECORD_ROOT" "$DATA_ROOT/dotnet-bundle"
    chmod 0700 "$DATA_ROOT" "$RUN_ROOT" "$DATA_ROOT/dotnet-bundle" 2>/dev/null || true

    if [ ! -x "$SERVER" ]; then
        log_message "应用程序不存在或不可执行：$SERVER"
        return 1
    fi
    if [ ! -x "$PG_BIN/postgres" ] || [ ! -x "$RUNTIME_ROOT/usr/bin/ffmpeg" ] || [ ! -x "$RUNTIME_ROOT/usr/bin/node" ]; then
        log_message "FPK 原生运行环境不完整。"
        return 1
    fi
    if pid=$(app_pid); then
        log_message "应用已运行，PID $pid。"
        return 0
    fi

    rm -f "$APP_PID_FILE"
    start_postgres || return 1
    launch_attempt=1
    launch_app_process
    pid=$APP_PROCESS_PID

    attempt=0
    while [ "$attempt" -lt 90 ]; do
        if run_native "$RUNTIME_ROOT/usr/bin/curl" -fsS "http://127.0.0.1:$SERVICE_PORT/health/ready" >/dev/null 2>&1; then
            rm -f "$ADMIN_PASSWORD_FILE"
            log_message "应用启动成功，PID $pid，端口 $SERVICE_PORT。"
            return 0
        fi
        if ! kill -0 "$pid" 2>/dev/null; then
            if [ "$launch_attempt" -ge 3 ]; then
                break
            fi
            log_message "应用启动进程提前退出，3 秒后重试（$launch_attempt/3）。"
            sleep 3
            launch_attempt=$((launch_attempt + 1))
            launch_app_process
            pid=$APP_PROCESS_PID
        fi
        attempt=$((attempt + 1))
        sleep 1
    done

    log_message "应用未能在 90 秒内就绪。"
    stop_app
    return 1
}

stop_app() {
    if pid=$(app_pid); then
        kill "$pid" 2>/dev/null || true
        attempt=0
        while kill -0 "$pid" 2>/dev/null && [ "$attempt" -lt 30 ]; do
            sleep 1
            attempt=$((attempt + 1))
        done
        if kill -0 "$pid" 2>/dev/null; then
            kill -9 "$pid" 2>/dev/null || true
        fi
    fi
    rm -f "$APP_PID_FILE"
    stop_postgres
}

case "${1:-status}" in
    start) start_app ;;
    stop) stop_app ;;
    restart) stop_app; start_app ;;
    status)
        if app_pid >/dev/null && postgres_running; then
            exit 0
        fi
        exit 3
        ;;
    *) printf 'usage: %s {start|stop|restart|status}\n' "$0" >&2; exit 2 ;;
esac
