#!/bin/bash
# daemon_lib - Common daemon/monitor infrastructure for pvmcp/pdscp/pvmig
# Should be sourced after setting TOOL_NAME and TOOL_LOGNAME

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  cat <<EOF >&2
Error: This is a library script and should be sourced, not executed directly.

Usage:
  Before sourcing, set:
    TOOL_NAME     - Script name (e.g., "pvmcp", "pdscp", "pvmig")
    TOOL_LOGNAME  - Operation name (e.g., "Transfer", "Migration")
  
  Then source:
    source /usr/lib/pvmzync/daemon_lib
  
  The library provides standard paths and functions:
    DAEMON_PIDFILE, DAEMON_LOG, TRANSFERLOG
    RUNDIR, SSH_DATA_DIR, ZFS_ANALYZER, SAVELOGS
  
  Functions:
    log, error
    daemon_alive, daemon_cleanup, savelogs_finish
    monitor_run, daemon_trap_exit, fresh_run
    daemon_run (common startup: trap, parse_args, direction, reachability, savelogs)
    main (entry point dispatcher)
  
  The sourcing script must define:
    parse_args()  - Parse CLI arguments; must set 'direction' and 'remote_host'
    tool_main()   - Main tool logic (called after common init)
    show_usage()  - Help text
EOF
  exit 1
fi

# Validate required variables
if [[ -z "${TOOL_NAME:-}" ]]; then
  echo "Error: TOOL_NAME not set before sourcing daemon_lib" >&2
  exit 1
fi

if [[ -z "${TOOL_LOGNAME:-}" ]]; then
  echo "Error: TOOL_LOGNAME not set before sourcing daemon_lib" >&2
  exit 1
fi

# --- Standard paths ---

ZYNCLIB="${ZYNCLIB:-/usr/lib/pvmzync/zynclib}"
RUNDIR="${RUNDIR:-/var/run/pvmzync}"
SSH_DATA_DIR="${SSH_DATA_DIR:-${RUNDIR}/ssh}"
ZFS_ANALYZER="${ZFS_ANALYZER:-/usr/bin/zynalyz}"

DAEMON_PIDFILE="${RUNDIR}/${TOOL_NAME}.pid"
DAEMON_LOG="${RUNDIR}/${TOOL_NAME}.log"
TRANSFERLOG="${RUNDIR}/${TOOL_NAME}_transfer.log"

# --- Basic functions ---

log() { echo "[$(date '+%H:%M:%S')] $*" >&2; }
error() { echo "ERROR: $*" >&2; exit 1; }

# --- Daemon helpers ---

savelogs_finish() {
  [[ "$SAVELOGS" != "true" ]] && return
  local ts=$(date +%Y%m%d_%H%M%S)
  local archive="${SAVELOGS_STARTDIR}/${TOOL_NAME}_logs_${ts}.tar.gz"
  cp "$DAEMON_LOG" "${SAVELOGS_TMPDIR}/${TOOL_NAME}.log" 2>/dev/null
  (cd "$SAVELOGS_TMPDIR"; tar -czf $archive *) || log "WARNING: save logs failed"
  rm -rf "$SAVELOGS_TMPDIR"
  log "Logs saved: $archive"
}

daemon_alive() {
  [[ -f "$DAEMON_PIDFILE" ]] || return 1
  kill -0 "$(cat "$DAEMON_PIDFILE")" 2>/dev/null
}

daemon_cleanup() {
  rm -f "$DAEMON_PIDFILE" "$DAEMON_LOG"
}

# --- Monitor mode ---

monitor_run() {
  if ! daemon_alive; then
    if [[ -f "$DAEMON_LOG" ]]; then
      echo "[$(date '+%H:%M:%S')] ${TOOL_LOGNAME} finished." >&2
      cat "$DAEMON_LOG"
      daemon_cleanup
    else
      show_usage
    fi
    exit 0
  fi

  pid=$(cat "$DAEMON_PIDFILE")
  echo "[$(date '+%H:%M:%S')] Monitoring ${TOOL_LOGNAME,,} (PID $pid)." >&2
  (tail -f "$DAEMON_LOG" | while IFS= read -r line; do
    echo "$line"
    if [[ "$line" =~ ^===\ (DONE|FAILED|KILLED) ]]; then
      break
    fi
  done) &
  tail_pid=$!
  echo "*** [k]ill  [s]howstat  [e]xit" >&2
  while :; do
    read -rsn1 -t1 choice </dev/tty || true
    kill -0 $tail_pid 2>/dev/null || break
    case "$choice" in
      k)
        local pid
        pid=$(cat "$DAEMON_PIDFILE" 2>/dev/null) || break
        echo "Killing ${TOOL_LOGNAME,,} (PID $pid)..." >&2
        kill -TERM -"$pid" 2>/dev/null
        break
        ;;
      s)
        show_transfer_stats 2>&1 | less
        ;;
      e)
        kill -0 "$pid" 2>/dev/null && echo "Detached. ${TOOL_LOGNAME} continues in background." >&2
        break
        ;;
    esac
  done
  kill $tail_pid 2>/dev/null
  wait $tail_pid 2>/dev/null
}

# --- Daemon trap ---

daemon_trap_exit() {
  local exit_code=$?
  trap - EXIT TERM INT  # prevent re-entry
  savelogs_finish >> "$DAEMON_LOG"
  if [[ ${exit_code} -eq 0 ]]; then
    echo "=== DONE ===" >> "$DAEMON_LOG"
  elif [[ ${exit_code} -eq 143 || ${exit_code} -eq 130 ]]; then
    echo "=== KILLED ===" >> "$DAEMON_LOG"
  else
    echo "=== FAILED ===" >> "$DAEMON_LOG"
  fi
  kill 0 2>/dev/null
  exit $exit_code
}

# --- Fresh run ---

fresh_run() {
  if daemon_alive; then
    error "${TOOL_LOGNAME} already running (PID $(cat "$DAEMON_PIDFILE")). Run '${TOOL_NAME}' to monitor."
  fi

  mkdir -p "$RUNDIR"
  [[ -f "$DAEMON_PIDFILE" ]] && daemon_cleanup

  setsid bash "$0" daemon "$@" </dev/null &>"$DAEMON_LOG" &
  echo $! > "$DAEMON_PIDFILE"
  monitor_run
}

# --- Common daemon startup ---
# Calls parse_args (tool-defined), sets up direction/hosts, checks reachability,
# initializes savelogs, then calls tool_main (tool-defined).

daemon_run() {
  trap daemon_trap_exit EXIT
  trap 'exit 143' TERM
  trap 'exit 130' INT

  declare -g direction="push" remote_host \
    SAVELOGS=false SAVELOGS_TMPDIR SAVELOGS_STARTDIR="${PWD}" \
    src_host dst_host src_ds dst_ds

  parse_args "$@"
  if [[ "$SAVELOGS" == "true" ]]; then
    SAVELOGS_TMPDIR=$(mktemp -d /tmp/${TOOL_NAME}_logs_XXXXXX)
  fi

  if [[ "$direction" == "push" ]]; then
    src_host="local"; dst_host="$remote_host"
  else
    src_host="$remote_host"; dst_host="local"
  fi

  check_reachable || error "host unreachable."

  tool_main
}

# --- Main dispatcher ---

main() {
  case "${1:-}" in
    daemon) shift; daemon_run "$@" ;;
    "")     monitor_run ;;
    *)      fresh_run "$@" ;;
  esac
}
