#!/bin/bash

set -u

LOG_FILE="${TRIM_PKGVAR}/lyranest.log"
SERVER_PID_FILE="${TRIM_PKGVAR}/lyranest-server.pid"
GATEWAY_PID_FILE="${TRIM_PKGVAR}/lyranest-gateway.pid"
SOCKET_PATH="${TRIM_APPDEST}/app.sock"
UPSTREAM_URL="http://127.0.0.1:39217"
DATA_DIR="${TRIM_PKGHOME}/data"
LAN_PORT_FILE="${TRIM_PKGETC}/lan_port"
MEMORY_LIMIT_FILE="${TRIM_PKGETC}/memory_limit"
PROVIDER_CREDENTIAL_KEY_FILE="${TRIM_PKGETC}/provider_credential_key"

log_msg() {
  echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}

ensure_provider_credential_key() {
  if [ -s "$PROVIDER_CREDENTIAL_KEY_FILE" ]; then
    tr -d '\r\n' < "$PROVIDER_CREDENTIAL_KEY_FILE"
    return 0
  fi

  if ! mkdir -p "$TRIM_PKGETC"; then
    log_msg "Unable to create private configuration directory for music provider credentials"
    return 1
  fi

  local key
  key="$(head -c 32 /dev/urandom | base64 | tr -d '\r\n')"
  if [ -z "$key" ]; then
    log_msg "Unable to generate music provider credential key"
    return 1
  fi

  (umask 077 && printf '%s\n' "$key" > "$PROVIDER_CREDENTIAL_KEY_FILE") || {
    log_msg "Unable to persist music provider credential key"
    return 1
  }
  chmod 600 "$PROVIDER_CREDENTIAL_KEY_FILE" 2>/dev/null || true
  printf '%s' "$key"
}

select_music_library_dirs() {
  local candidate
  local paths="${TRIM_DATA_ACCESSIBLE_PATHS:-}"
  local found="0"

  while IFS= read -r candidate || [ -n "$candidate" ]; do
    [ -n "$candidate" ] || continue
    if [ ! -d "$candidate" ] || [ ! -r "$candidate" ] || [ ! -x "$candidate" ]; then
      log_msg "Ignoring inaccessible authorized music directory: ${candidate}"
      continue
    fi
    if ! ls -A "$candidate" >/dev/null 2>&1; then
      log_msg "Ignoring unreadable authorized music directory: ${candidate}"
      continue
    fi
    printf '%s\n' "$candidate"
    found="1"
  done < <(printf '%s\n' "$paths" | tr ':' '\n')

  [ "$found" = "1" ]
}

read_pid() {
  [ -f "$1" ] || return 1
  tr -d '[:space:]' < "$1"
}

is_running() {
  local pid
  pid="$(read_pid "$1")" || return 1
  [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null
}

stop_process() {
  local pid_file="$1"
  local name="$2"
  local pid

  pid="$(read_pid "$pid_file")" || return 0
  if ! kill -0 "$pid" 2>/dev/null; then
    rm -f "$pid_file"
    return 0
  fi

  log_msg "Stopping ${name} (pid ${pid})"
  kill -TERM "$pid" 2>> "$LOG_FILE" || true
  local count=0
  while kill -0 "$pid" 2>/dev/null && [ "$count" -lt 10 ]; do
    sleep 1
    count=$((count + 1))
  done
  if kill -0 "$pid" 2>/dev/null; then
    kill -KILL "$pid" 2>> "$LOG_FILE" || true
  fi
  rm -f "$pid_file"
}

status_app() {
  is_running "$SERVER_PID_FILE" && is_running "$GATEWAY_PID_FILE"
}

start_app() {
  if status_app; then
    return 0
  fi
  if ! mkdir -p "$DATA_DIR"; then
    echo "无法创建 LyraNest 私有数据目录。" > "$TRIM_TEMP_LOGFILE"
    return 1
  fi

  local downloads_root="${DATA_DIR}/downloads"
  if ! mkdir -p "$downloads_root"; then
    log_msg "Unable to create private downloads directory"
    return 1
  fi

  local provider_credential_key=""
  provider_credential_key="$(ensure_provider_credential_key || true)"
  if [ -z "$provider_credential_key" ]; then
    log_msg "Music provider plugins are unavailable because the credential key could not be prepared"
  fi

  local lan_port=""
  if [ -f "$LAN_PORT_FILE" ]; then
    lan_port="$(tr -d '[:space:]' < "$LAN_PORT_FILE")"
    case "$lan_port" in
      ''|*[!0-9]*)
        echo "局域网端口配置无效，请在应用设置中重新保存。" > "$TRIM_TEMP_LOGFILE"
        return 1
        ;;
    esac
    if [ "$lan_port" -lt 1024 ] || [ "$lan_port" -gt 65535 ]; then
      echo "局域网端口必须在 1024 到 65535 之间。" > "$TRIM_TEMP_LOGFILE"
      return 1
    fi
  fi

  stop_process "$GATEWAY_PID_FILE" "LyraNest fnOS gateway"
  stop_process "$SERVER_PID_FILE" "LyraNest server"
  pkill -f "${TRIM_APPDEST}/bin/lyranest-server" 2>/dev/null || true
  pkill -f "${TRIM_APPDEST}/bin/lyranest-fnos-gateway" 2>/dev/null || true
  rm -f "$SOCKET_PATH"

  local music_library_dir=""
  local music_library_roots=""
  local permission_required="0"
  local -a music_library_dirs=()
  mapfile -t music_library_dirs < <(select_music_library_dirs || true)
  if [ "${#music_library_dirs[@]}" -eq 0 ]; then
    music_library_dir="${DATA_DIR}/unconfigured-music-library"
    music_library_roots="$music_library_dir"
    permission_required="1"
    mkdir -p "$music_library_dir"
    log_msg "No accessible music directory permission granted; starting with an empty library"
  else
    music_library_dir="${music_library_dirs[0]}"
    music_library_roots="$(IFS=:; printf '%s' "${music_library_dirs[*]}")"
    log_msg "Using ${#music_library_dirs[@]} authorized music root(s); default root: ${music_library_dir}"
  fi
  printf '%s\n' "${TRIM_DATA_ACCESSIBLE_PATHS:-}" > "${TRIM_PKGVAR}/authorized_paths.snapshot"

  music_library_roots="${music_library_roots}:${downloads_root}"

  # fnOS runs the service directly on the NAS, with no container memory limit
  # to bound the Go heap. Without GOMEMLIMIT the collector only reclaims after
  # the heap doubles and returns freed pages lazily, so a scrape or scan peak
  # stays resident as RSS. These values keep the service inside a few hundred
  # megabytes on small machines; override them in the app settings file.
  local memory_limit="256MiB"
  if [ -f "$MEMORY_LIMIT_FILE" ]; then
    local configured_limit
    configured_limit="$(tr -d '[:space:]' < "$MEMORY_LIMIT_FILE")"
    if [ -n "$configured_limit" ]; then
      memory_limit="$configured_limit"
    fi
  fi

  log_msg "Starting LyraNest server (memory limit ${memory_limit})"
  MUSIC_LIBRARY_DIR="$music_library_dir" \
    MUSIC_LIBRARY_ROOTS="$music_library_roots" \
    MUSIC_LIBRARY_PERMISSION_REQUIRED="$permission_required" \
    DOWNLOADS_ROOT="$downloads_root" \
    PROVIDER_CREDENTIAL_KEY="$provider_credential_key" \
    MUSIC_DATA_DIR="$DATA_DIR" \
    MUSIC_CACHE_DIR="${DATA_DIR}/cache" \
    SERVER_ADDR="127.0.0.1:39217" \
    FFMPEG_BINARY="${TRIM_APPDEST}/bin/ffmpeg" \
    GOMEMLIMIT="$memory_limit" \
    GOGC="${LYRANEST_GOGC:-50}" \
    MUSIC_MEMORY_LIMIT="$memory_limit" \
    MUSIC_LOW_MEMORY="1" \
    FNOS_TRUSTED_GATEWAY="1" \
    AUTHORIZED_PATHS_FILE="${TRIM_PKGVAR}/authorized_paths.snapshot" \
    "${TRIM_APPDEST}/bin/lyranest-server" >> "$LOG_FILE" 2>&1 &
  echo "$!" > "$SERVER_PID_FILE"

  local startup_timeout="${LYRANEST_STARTUP_TIMEOUT:-120}"
  case "$startup_timeout" in
    ''|*[!0-9]*) startup_timeout=120 ;;
  esac

  local count=0
  until "${TRIM_APPDEST}/bin/lyranest-fnos-gateway" healthcheck >> "$LOG_FILE" 2>&1; do
    if ! is_running "$SERVER_PID_FILE" || [ "$count" -ge "$startup_timeout" ]; then
      echo "LyraNest 服务端启动失败，请查看 ${LOG_FILE}。" > "$TRIM_TEMP_LOGFILE"
      stop_process "$SERVER_PID_FILE" "LyraNest server"
      return 1
    fi
    sleep 1
    count=$((count + 1))
  done

  log_msg "Starting LyraNest fnOS gateway"
  FNOS_SOCKET_PATH="$SOCKET_PATH" \
    FNOS_GATEWAY_PREFIX="/app/lyranest" \
    FNOS_UPSTREAM_URL="$UPSTREAM_URL" \
    FNOS_LISTEN_ADDR="${lan_port:+0.0.0.0:${lan_port}}" \
    "${TRIM_APPDEST}/bin/lyranest-fnos-gateway" >> "$LOG_FILE" 2>&1 &
  echo "$!" > "$GATEWAY_PID_FILE"
}

stop_app() {
  stop_process "$GATEWAY_PID_FILE" "LyraNest fnOS gateway"
  stop_process "$SERVER_PID_FILE" "LyraNest server"
  pkill -f "${TRIM_APPDEST}/bin/lyranest-server" 2>/dev/null || true
  pkill -f "${TRIM_APPDEST}/bin/lyranest-fnos-gateway" 2>/dev/null || true
  rm -f "$SOCKET_PATH"
}

case "${1:-}" in
  start)
    start_app
    ;;
  stop)
    stop_app
    ;;
  status)
    status_app && exit 0
    exit 3
    ;;
  *)
    echo "Unknown command: ${1:-}" > "$TRIM_TEMP_LOGFILE"
    exit 1
    ;;
esac
