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

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_OWNER="${CAPTURE_USER:-$(stat -c %U "$SCRIPT_DIR")}"
SCRIPT_OWNER_HOME="$(getent passwd "$SCRIPT_OWNER" | cut -d: -f6)"
cd "$SCRIPT_DIR"

ENV_FILE="$SCRIPT_DIR/.env"
declare -A DOTENV_VALUES

trim_whitespace() {
  local value="$1"
  value="${value#"${value%%[![:space:]]*}"}"
  value="${value%"${value##*[![:space:]]}"}"
  printf '%s' "$value"
}

load_env_file() {
  local line key value quote_char

  if [[ ! -f "$ENV_FILE" ]]; then
    echo "Не найден .env: $ENV_FILE" >&2
    exit 1
  fi

  while IFS= read -r line || [[ -n "$line" ]]; do
    line="${line%$'\r'}"
    line="$(trim_whitespace "$line")"

    [[ -z "$line" || "$line" == \#* ]] && continue

    if [[ "$line" == export\ * ]]; then
      line="${line#export }"
    fi

    [[ "$line" == *=* ]] || continue

    key="$(trim_whitespace "${line%%=*}")"
    value="$(trim_whitespace "${line#*=}")"

    if [[ ${#value} -ge 2 ]]; then
      quote_char="${value:0:1}"
      if [[ ( "$quote_char" == "'" || "$quote_char" == '"' ) && "${value: -1}" == "$quote_char" ]]; then
        value="${value:1:${#value}-2}"
      fi
    fi

    DOTENV_VALUES["$key"]="$value"
  done < "$ENV_FILE"
}

get_env_setting() {
  local key="$1"
  local default="${2-}"

  if [[ -n "${!key+x}" ]]; then
    printf '%s' "${!key}"
  elif [[ -n "${DOTENV_VALUES[$key]+x}" ]]; then
    printf '%s' "${DOTENV_VALUES[$key]}"
  else
    printf '%s' "$default"
  fi
}

load_env_file
############################
# НАСТРОЙКИ
############################

BASE_DIR="$(get_env_setting CAPTURE_BASE_DIR "${SCRIPT_OWNER_HOME}/dataset/noise")"

# Путь к python из venv
PYTHON_BIN="$(get_env_setting PYTHON_BIN "$SCRIPT_DIR/.venv-sdr/bin/python")"

# Путь к headless скрипту
SCRIPT_PATH="$(get_env_setting SCRIPT_PATH "$SCRIPT_DIR/scripts_nn/data_saver_headless.py")"

RUN_ONCE="$(get_env_setting RUN_ONCE "0")"
CAPTURE_LOG_FILE="$(get_env_setting CAPTURE_LOG_FILE "$BASE_DIR/capture_hourly.log")"

SYSTEMCTL_BIN=(systemctl)
CURRENT_CAPTURE_PID=""
CURRENT_SERVICE_UNITS=()



# Лимиты

lim_all=10
PER_FREQ_LIMIT_BYTES=$((1 * 1024 * 1024 * 1024))     #  GiB на частоту за запуск
TOTAL_LIMIT_BYTES=$((lim_all * 1024 * 1024 * 1024))      # общий лимит  GiB
CYCLE_SECONDS=1                               # один цикл в час

# Параметры SDR
SAMP_RATE="20e6"
SPLIT_SIZE="400000"
DELAY="0.01"
RF_GAIN="12"
IF_GAIN="30"
BB_GAIN="36"

############################
# ЧАСТОТЫ И SERIAL ИЗ ENV
############################

ORDER=(433)


declare -A SERIAL
declare -A FREQ_HZ
declare -A SERVICE_UNIT

SERIAL[433]="$(get_env_setting hack_433)"
FREQ_HZ[433]="433000000"

SERIAL[750]="$(get_env_setting hack_750)"
FREQ_HZ[750]="750000000"


SERIAL[915]="$(get_env_setting hack_915)"
FREQ_HZ[915]="915000000"

SERIAL[1200]="$(get_env_setting hack_1200)"
FREQ_HZ[1200]="1200000000"

SERIAL[2400]="$(get_env_setting hack_2400)"
FREQ_HZ[2400]="2400000000"

SERIAL[3300]="$(get_env_setting hack_3300)"
FREQ_HZ[3300]="3300000000"

SERIAL[4500]="$(get_env_setting hack_4500)"
FREQ_HZ[4500]="4500000000"

SERIAL[5200]="$(get_env_setting hack_5200)"
FREQ_HZ[5200]="5200000000"

SERIAL[5800]="$(get_env_setting hack_5800)"
FREQ_HZ[5800]="5800000000"

SERVICE_UNIT[433]="dronedetector-sdr-433.service"
SERVICE_UNIT[750]="dronedetector-sdr-750.service"
SERVICE_UNIT[915]="dronedetector-sdr-915.service"
SERVICE_UNIT[1200]="dronedetector-sdr-1200.service"
SERVICE_UNIT[2400]="dronedetector-sdr-2400.service"
SERVICE_UNIT[3300]="dronedetector-sdr-3300.service"
SERVICE_UNIT[4500]="dronedetector-sdr-4500.service"
SERVICE_UNIT[5200]="dronedetector-sdr-5200.service"
SERVICE_UNIT[5800]="dronedetector-sdr-5800.service"

############################
# ВСПОМОГАТЕЛЬНОЕ
############################

log() {
  printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}

systemctl_run() {
  "${SYSTEMCTL_BIN[@]}" "$@"
}

service_exists() {
  local unit="$1"
  systemctl_run show "$unit" >/dev/null 2>&1
}

service_is_active() {
  local unit="$1"
  systemctl_run is-active --quiet "$unit"
}

remember_service_unit() {
  local unit="$1"
  local existing

  for existing in "${CURRENT_SERVICE_UNITS[@]}"; do
    if [[ "$existing" == "$unit" ]]; then
      return 0
    fi
  done

  CURRENT_SERVICE_UNITS+=("$unit")
}

stop_band_service() {
  local band="$1"
  local serial="${SERIAL[$band]:-}"
  local other_band unit other_serial

  if [[ -z "$serial" ]]; then
    log "Для band=$band пустой serial, пропускаю stop/start сервисов"
    return 0
  fi

  CURRENT_SERVICE_UNITS=()
  for other_band in "${!SERVICE_UNIT[@]}"; do
    unit="${SERVICE_UNIT[$other_band]:-}"
    other_serial="${SERIAL[$other_band]:-}"

    if [[ -z "$unit" || -z "$other_serial" || "$other_serial" != "$serial" ]]; then
      continue
    fi

    if ! service_exists "$unit"; then
      log "Service unit $unit не установлен, пропускаю"
      continue
    fi

    if service_is_active "$unit"; then
      remember_service_unit "$unit"
    fi

    log "Останавливаю service $unit перед записью band=$band"
    systemctl_run stop "$unit"
  done
}

start_current_service() {
  local unit

  if [[ "${#CURRENT_SERVICE_UNITS[@]}" -eq 0 ]]; then
    return 0
  fi

  for unit in "${CURRENT_SERVICE_UNITS[@]}"; do
    log "Запускаю service $unit после записи"
    systemctl_run start "$unit"
  done
  CURRENT_SERVICE_UNITS=()
}

cleanup_capture() {
  if [[ -n "$CURRENT_CAPTURE_PID" ]] && kill -0 "$CURRENT_CAPTURE_PID" 2>/dev/null; then
    log "Останавливаю текущий PID=$CURRENT_CAPTURE_PID при завершении скрипта"
    kill -TERM "$CURRENT_CAPTURE_PID" 2>/dev/null || true
    wait "$CURRENT_CAPTURE_PID" 2>/dev/null || true
  fi
  CURRENT_CAPTURE_PID=""

  if [[ "${#CURRENT_SERVICE_UNITS[@]}" -gt 0 ]]; then
    local unit
    for unit in "${CURRENT_SERVICE_UNITS[@]}"; do
      log "Восстанавливаю service $unit при завершении скрипта"
      systemctl_run start "$unit" || true
    done
    CURRENT_SERVICE_UNITS=()
  fi
}

on_signal() {
  local sig="$1"
  log "Получен сигнал $sig, завершаю работу"
  exit 130
}

dir_size_bytes() {
  local path="$1"
  if [[ -e "$path" ]]; then
    du -sb "$path" 2>/dev/null | awk '{print $1}'
  else
    echo 0
  fi
}

total_size_bytes() {
  dir_size_bytes "$BASE_DIR"
}

ensure_requirements() {
  if [[ ! -x "$PYTHON_BIN" ]]; then
    echo "Не найден python: $PYTHON_BIN" >&2
    exit 1
  fi

  if [[ ! -f "$SCRIPT_PATH" ]]; then
    echo "Не найден script: $SCRIPT_PATH" >&2
    exit 1
  fi

  if ! command -v systemctl >/dev/null 2>&1; then
    echo "Не найден systemctl" >&2
    exit 1
  fi

  if [[ "${EUID}" -ne 0 ]]; then
    if command -v sudo >/dev/null 2>&1; then
      SYSTEMCTL_BIN=(sudo systemctl)
    else
      echo "Для управления service нужен root или sudo" >&2
      exit 1
    fi
  fi

  mkdir -p "$BASE_DIR"
  mkdir -p "$(dirname "$CAPTURE_LOG_FILE")"

  local capture_order
  capture_order="$(get_env_setting CAPTURE_ORDER)"

  if [[ -n "$capture_order" ]]; then
    ORDER=()
    local band
    for band in ${capture_order//,/ }; do
      ORDER+=("$band")
    done
  fi

  if [[ "${#ORDER[@]}" -eq 0 ]]; then
    echo "ORDER пустой. Укажите частоты в ORDER или задайте CAPTURE_ORDER." >&2
    exit 1
  fi

  log "Рабочая директория: $SCRIPT_DIR"
  log "BASE_DIR: $BASE_DIR"
  log "ORDER: ${ORDER[*]}"
  log "RUN_ONCE: $RUN_ONCE"

}

run_one_freq() {
  local band="$1"
  local serial="${SERIAL[$band]}"
  local freq="${FREQ_HZ[$band]}"

  if [[ -z "$serial" ]]; then
    log "Для band=$band пустой serial, пропускаю"
    return 0
  fi

  local ts out_dir log_file
  ts="$(date '+%F_%H-%M-%S')"
  out_dir="$BASE_DIR/$band/$ts"
  log_file="$out_dir/run.log"

  log "Старт band=$band serial=$serial freq=$freq dir=$out_dir"
  if ! mkdir -p "$out_dir"; then
    log "Не удалось создать каталог $out_dir"
    return 1
  fi

  stop_band_service "$band"

  "$PYTHON_BIN" "$SCRIPT_PATH" \
    --serial "$serial" \
    --freq "$freq" \
    --save-dir "$out_dir" \
    --file-tag "${band}_" \
    --samp-rate "$SAMP_RATE" \
    --split-size "$SPLIT_SIZE" \
    --delay "$DELAY" \
    --rf-gain "$RF_GAIN" \
    --if-gain "$IF_GAIN" \
    --bb-gain "$BB_GAIN" \
    >"$log_file" 2>&1 &

  local pid=$!
  CURRENT_CAPTURE_PID="$pid"
  log "PID=$pid"

  while kill -0 "$pid" 2>/dev/null; do
    local cur_dir_size cur_total_size
    cur_dir_size="$(dir_size_bytes "$out_dir")"
    cur_total_size="$(total_size_bytes)"

    if (( cur_total_size >= TOTAL_LIMIT_BYTES )); then
      log "Достигнут общий лимит $lim_all GiB. Останавливаю PID=$pid"
      kill -TERM "$pid" 2>/dev/null || true
      wait "$pid" 2>/dev/null || true
      CURRENT_CAPTURE_PID=""
      start_current_service
      return 2
    fi

    if (( cur_dir_size >= PER_FREQ_LIMIT_BYTES )); then
      log "Для band=$band достигнут лимит 3 GiB. Останавливаю PID=$pid"
      kill -TERM "$pid" 2>/dev/null || true

      for _ in {1..10}; do
        if ! kill -0 "$pid" 2>/dev/null; then
          break
        fi
        sleep 1
      done

      if kill -0 "$pid" 2>/dev/null; then
        log "PID=$pid не завершился по TERM, отправляю KILL"
        kill -KILL "$pid" 2>/dev/null || true
      fi

      wait "$pid" 2>/dev/null || true
      break
    fi

    sleep 1
  done

  CURRENT_CAPTURE_PID=""
  start_current_service
  log "Завершен band=$band, размер=$(du -sh "$out_dir" | awk '{print $1}')"
  return 0
}

main_loop() {
  while true; do
    local total_before cycle_start elapsed sleep_left
    total_before="$(total_size_bytes)"

    if (( total_before >= TOTAL_LIMIT_BYTES )); then
      log "Общий размер уже >=  GiB, выхожу"
      break
    fi

    cycle_start="$(date +%s)"
    log "Новый цикл"

    for band in "${ORDER[@]}"; do
      if (( $(total_size_bytes) >= TOTAL_LIMIT_BYTES )); then
        log "Общий лимит достигнут внутри цикла, выхожу"
        return 0
      fi

      run_one_freq "$band" || {
        rc=$?
        if [[ $rc -eq 2 ]]; then
          log "Остановка по общему лимиту"
          return 0
        else
          log "Ошибка записи band=$band rc=$rc"
          return "$rc"
        fi
      }
    done

    if [[ "$RUN_ONCE" == "1" ]]; then
      log "RUN_ONCE=1, завершаю работу после одного цикла"
      break
    fi

    elapsed=$(( $(date +%s) - cycle_start ))
    sleep_left=$(( CYCLE_SECONDS - elapsed ))

    if (( sleep_left > 0 )); then
      log "Цикл занял ${elapsed} сек, жду ${sleep_left} сек до следующего часа"
      sleep "$sleep_left"
    else
      log "Цикл занял ${elapsed} сек, паузы нет"
    fi
  done
}

trap cleanup_capture EXIT
trap 'on_signal INT' INT
trap 'on_signal TERM' TERM

ensure_requirements
main_loop
