#!/usr/bin/env bash
# Fixture /dev ONLY — offload rotated capture siblings to Dropbox, then delete local.
#
# Hard-gated to ${ETHERGOLD_ROOT}/dev/{data,logs}. Refuses /prod or any other tree.
# Does NOT touch live basenames (events.jsonl, uartd.log, …).
# Not production log_offload — see log_offload.sh for field.
#
# Usage (Pi soak seats):
#   cd ~/EtherGold/sync
#   DRY_RUN=1 bash offload_fixture_dev_rotated.sh
#   bash offload_fixture_dev_rotated.sh
#
# Watcher: bash install_fixture_dev_rotated_offload.sh

if grep -q $'\r' "${BASH_SOURCE[0]}" 2>/dev/null; then
  sed -i 's/\r$//' "${BASH_SOURCE[0]}"
  exec bash "${BASH_SOURCE[0]}" "$@"
fi

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${ROOT}/rclone.env"
DRY_RUN="${DRY_RUN:-0}"

if [[ -f "${ROOT}/ethergold_paths.sh" ]]; then
  # shellcheck source=ethergold_paths.sh
  source "${ROOT}/ethergold_paths.sh"
fi
ETHERGOLD_ROOT="${ETHERGOLD_ROOT:-/home/topbox/EtherGold}"
ETHERGOLD_LOGS="${ETHERGOLD_LOGS:-${ETHERGOLD_ROOT}/logs}"
DEV_ROOT="${ETHERGOLD_ROOT}/dev"
TRANSCRIPT="${ETHERGOLD_LOGS}/.fixture_dev_offload_last.log"

mkdir -p "${ETHERGOLD_LOGS}"
if [[ "${FIXTURE_DEV_OFFLOAD_TRUNCATE:-0}" == "1" ]]; then
  : > "${TRANSCRIPT}"
fi

log() {
  printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" | tee -a "${TRANSCRIPT}"
}

abs_path() {
  local p="${1}"
  if command -v realpath >/dev/null 2>&1; then
    realpath -m "${p}"
  else
    printf '%s\n' "${p}"
  fi
}

DEV_ABS="$(abs_path "${DEV_ROOT}")"
case "${DEV_ABS}" in
  */EtherGold/dev|*/EtherGold/dev/) ;;
  *)
    log "FATAL: fixture-dev offload refuses path '${DEV_ABS}' (must be …/EtherGold/dev)"
    exit 1
    ;;
esac
case "${DEV_ABS}" in
  */EtherGold/prod|*/EtherGold/prod/*)
    log "FATAL: refuse /prod"
    exit 1
    ;;
esac

DATA_DIR="${DEV_ABS}/data"
LOGS_DIR="${DEV_ABS}/logs"

if [[ ! -f "${ENV_FILE}" ]]; then
  log "skip: missing ${ENV_FILE} (no-op)"
  exit 0
fi

if grep -q $'\r' "${ENV_FILE}" 2>/dev/null; then
  sed -i 's/\r$//' "${ENV_FILE}"
fi

# shellcheck source=/dev/null
source "${ENV_FILE}"

RCLONE_REMOTE="${RCLONE_REMOTE:-}"
DROPBOX_DEST="${DROPBOX_DEST:-}"

if [[ -z "${RCLONE_REMOTE}" || -z "${DROPBOX_DEST}" ]]; then
  log "skip: RCLONE_REMOTE / DROPBOX_DEST unset in rclone.env (no-op)"
  exit 0
fi

if ! command -v rclone >/dev/null 2>&1; then
  log "ERROR: rclone not installed"
  exit 1
fi

REMOTE_DATA="${RCLONE_REMOTE}:${DROPBOX_DEST}/data"
REMOTE_LOGS="${RCLONE_REMOTE}:${DROPBOX_DEST}/logs"

# True if name is a rotated sibling of a known live stem (never the live file).
is_rotated_data_name() {
  local f="${1}"
  case "${f}" in
    events.jsonl.*|packets.raw.*|inject_echo.jsonl.*|hw_debug.jsonl.*|wide_rx.bin.*)
      return 0
      ;;
    *) return 1 ;;
  esac
}

is_rotated_log_name() {
  local f="${1}"
  case "${f}" in
    fixture_bridge.log.*|orchestrator-bridge.log.*|pogcc-com.log.*)
      return 0
      ;;
    uartd.log.*|uartd_*.log.*)
      return 0
      ;;
    fixture_bridge.log|orchestrator-bridge.log|pogcc-com.log|uartd.log)
      return 1
      ;;
    *) return 1 ;;
  esac
}

list_rotated_files() {
  local dir="${1}"
  local kind="${2}"
  local f
  [[ -d "${dir}" ]] || return 0
  (
    cd "${dir}" || exit 0
    shopt -s nullglob
    for f in *; do
      [[ -f "${f}" ]] || continue
      if [[ "${kind}" == data ]]; then
        is_rotated_data_name "${f}" || continue
      else
        is_rotated_log_name "${f}" || continue
      fi
      printf '%s\n' "${f}"
    done
    shopt -u nullglob
  ) | LC_ALL=C sort -u
}

file_size_bytes() {
  local path="${1}"
  if stat -c '%s' "${path}" >/dev/null 2>&1; then
    stat -c '%s' "${path}"
  else
    stat -f '%z' "${path}"
  fi
}

remote_size_of() {
  local remote_dir="${1}"
  local name="${2}"
  rclone size "${remote_dir}/${name}" 2>/dev/null \
    | awk '/Total size:/ {
        for (i = 1; i <= NF; i++) if ($i ~ /^[0-9]+$/) { print $i; exit }
      }' || true
}

offload_listed() {
  local local_dir="${1}"
  local remote_dir="${2}"
  shift 2
  local name local_path sz rem_sz

  if [[ "$#" -eq 0 ]]; then
    log "ok: no rotated files under ${local_dir}"
    return 0
  fi

  log "=== offload ${local_dir} → ${remote_dir} ($# files) ==="

  for name in "$@"; do
    local_path="${local_dir}/${name}"
    [[ -f "${local_path}" ]] || continue
    sz="$(file_size_bytes "${local_path}")"
    log "candidate: ${name} (${sz} bytes)"

    if [[ "${DRY_RUN}" == "1" ]]; then
      log "DRY_RUN: would rclone copy → delete ${name}"
      COPIED=$((COPIED + 1))
      continue
    fi

    if ! rclone copyto "${local_path}" "${remote_dir}/${name}" --retries 3; then
      log "ERROR: rclone copy failed for ${name}"
      FAILED=$((FAILED + 1))
      continue
    fi

    rem_sz="$(remote_size_of "${remote_dir}" "${name}")"
    if [[ -n "${rem_sz}" && "${rem_sz}" != "${sz}" ]]; then
      log "ERROR: size mismatch ${name} local=${sz} remote=${rem_sz} — keeping local"
      FAILED=$((FAILED + 1))
      continue
    fi

    if rm -f "${local_path}"; then
      log "deleted local: ${name}"
      DELETED=$((DELETED + 1))
      COPIED=$((COPIED + 1))
    else
      log "WARN: copied but could not delete ${local_path}"
      COPIED=$((COPIED + 1))
    fi
  done
}

COPIED=0
DELETED=0
FAILED=0

log "=== offload_fixture_dev_rotated ==="
log "dev_root: ${DEV_ABS}"
log "landing: ${RCLONE_REMOTE}:${DROPBOX_DEST}"

mapfile -t DATA_NAMES < <(list_rotated_files "${DATA_DIR}" data)
offload_listed "${DATA_DIR}" "${REMOTE_DATA}" "${DATA_NAMES[@]+"${DATA_NAMES[@]}"}"

mapfile -t LOG_NAMES < <(list_rotated_files "${LOGS_DIR}" logs)
offload_listed "${LOGS_DIR}" "${REMOTE_LOGS}" "${LOG_NAMES[@]+"${LOG_NAMES[@]}"}"

log "done: copied=${COPIED} deleted=${DELETED} failed=${FAILED} dry_run=${DRY_RUN}"
if [[ "${FAILED}" -gt 0 ]]; then
  exit 1
fi
exit 0
