#!/usr/bin/env bash

# WildlifeSystems - sensor read
#
# This script provides a standard wrapper for reading sensors in WildlifeSystems,
# and handles inserting the device serial number and timestamp into the JSON response.

set -u
set -o pipefail

# Return codes
# 0 - Success
# 2 - no arguments provided

readonly REQUIRED_CMDS=(jq timeout find basename sed awk pi-data)

# Check required external commands early and list all missing ones
missing=()
for _cmd in "${REQUIRED_CMDS[@]}"; do
  if ! command -v "$_cmd" >/dev/null 2>&1; then
    missing+=("$_cmd")
  fi
done
if (( ${#missing[@]} )); then
  printf 'Missing required commands: %s\n' "${missing[*]}" >&2
  exit 1
fi

if [[ $# -eq 0 ]]; then
  printf 'No arguments supplied\n' >&2
  exit 2
fi

# Resolve path to this script (prefer absolute where possible)
SELF="${BASH_SOURCE[0]:-$0}"
if [[ "${SELF}" != /* ]]; then
  if command -v "$(basename "${SELF}")" >/dev/null 2>&1; then
    SELF=$(command -v "$(basename "${SELF}")")
  else
    SELF="$(pwd)/${SELF}"
  fi
fi

SCRIPT="$1"
DEV=${2:-all}

# Constants
readonly LOG_DIR=/var/log/sensor-control
readonly SENS_DIR=/usr/bin
readonly TIMEOUT_SECS=5
readonly KILL_AFTER=5s

# Ensure log dir exists (packaging should create it; ignore errors)
mkdir -p "${LOG_DIR}" 2>/dev/null || true

# Read Pi serial and current timestamp once
PI_SERIAL=$(pi-data serial)
TSTAMP=$(date +%s)

run_list() {
  # Find executable sensor-* scripts, probe with 'identify', and print script name
  find "${SENS_DIR}" -name 'sensor-*' -executable -print0 |
    while IFS= read -r -d '' SENS_SCRIPT; do
      "${SENS_SCRIPT}" identify
      rc=$?
      if [[ $rc -eq 60 ]]; then
        SCRIPT_NAME=$(basename "${SENS_SCRIPT}")
        printf '%s\n' "${SCRIPT_NAME#sensor-}"
      fi
    done
}

if [[ "${SCRIPT}" == "list" ]]; then
  run_list
  exit 0
fi

if [[ "${SCRIPT}" == "all" ]]; then
  # Accumulate per-sensor JSONs into a bash array and merge once at the end
  declare -a ALL_JSONS=()

  while IFS= read -r SENSOR_NAME; do
    DEV=all
    SENS_SCRIPT="${SENS_DIR}/sensor-${SENSOR_NAME}"
    LOGFILE="${LOG_DIR}/${SENSOR_NAME}.log"

    # Run sensor script with timeout and capture output; keep rc in variable
    rc=0
    SENS_JSON=$(timeout --signal=TERM --kill-after=${KILL_AFTER} ${TIMEOUT_SECS} "${SENS_SCRIPT}" "${DEV}" 2>>"${LOGFILE}") || rc=$?
    rc=${rc:-0}

    if [[ $rc -ne 0 ]]; then
      continue
    fi

    # Exclude based on .internal flag where appropriate using jq directly
    if [[ "${SCRIPT}" == "external" ]]; then
      if jq -e 'any(.[]; .internal == true)' <<<"${SENS_JSON}" >/dev/null 2>&1; then
        continue
      fi
    fi
    if [[ "${SCRIPT}" == "internal" ]]; then
      if jq -e 'any(.[]; .internal == false)' <<<"${SENS_JSON}" >/dev/null 2>&1; then
        continue
      fi
    fi

    # Inject node_id and timestamp; ensure compact output
    SENS_JSON=$(jq -c --arg node "${PI_SERIAL}" --argjson ts "${TSTAMP}" 'map(.node_id = $node | .timestamp = $ts)' <<<"${SENS_JSON}")

    ALL_JSONS+=("${SENS_JSON}")
  done < <(run_list)

  if (( ${#ALL_JSONS[@]} == 0 )); then
    printf '[]\n'
    exit 0
  fi

  # Merge all arrays in one jq invocation
  printf '%s\n' "${ALL_JSONS[@]}" | jq -s 'add' -c
  exit 0
fi

if [[ "${SCRIPT}" == "external" ]]; then
  "${SELF}" all | jq -c 'del(.[] | select(.internal == true))'
  exit 0
fi

if [[ "${SCRIPT}" == "internal" ]]; then
  "${SELF}" all | jq -c 'del(.[] | select(.internal == false))'
  exit 0
fi

SENS_SCRIPT="${SENS_DIR}/sensor-${SCRIPT}"

if [[ "${DEV}" == "list" ]]; then
  "${SENS_SCRIPT}" list
  exit 0
fi

# Run single sensor script with timeout
SENSOR_BASENAME=$(basename "${SENS_SCRIPT}")
SENS_LOG="${LOG_DIR}/${SENSOR_BASENAME}.log"
rc=0
SENS_JSON=$(timeout --signal=TERM --kill-after=${KILL_AFTER} ${TIMEOUT_SECS} "${SENS_SCRIPT}" "${DEV}" 2>>"${SENS_LOG}") || rc=$?
rc=${rc:-0}

if [[ $rc -ne 0 ]]; then
  if [[ $rc -eq 20 ]]; then
    printf 'Unknown device\n'
  elif [[ $rc -eq 21 ]]; then
    printf 'Unknown sensor\n'
  fi
  exit "$rc"
fi

# Inject node_id and timestamp and print
jq -c --arg node "${PI_SERIAL}" --argjson ts "${TSTAMP}" 'map(.node_id = $node | .timestamp = $ts)' <<<"${SENS_JSON}"

exit 0
