#!/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.
#
# This script is part of the WildlifeSystems project. For further information
# please refer to https://docs.wildlife.systems, or for more information on
# the project itself, please refer to https://wildlife.systems.

# Return codes
#
# Further information on WildlifeSystems standard return codes can be found
# at https://docs.wildlife.systems/return-codes.html
# 0 - Success
# 2 - no arguments provided

set -u
set -o pipefail

# Check required external commands early (fail fast if missing)
for _cmd in jq timeout find basename sed awk pi-data; do
  if ! command -v "$_cmd" >/dev/null 2>&1; then
    echo "Required command '$_cmd' not found in PATH" >&2
    exit 1
  fi
done

if [[ $# -eq 0 ]]; then
  echo "No arguments supplied"
  exit 2
fi

# Normalize script name (call self via $0 when recursing)
SELF="$0"

if [[ "$1" == "list" ]]; then
  # Find executable sensor-* scripts and probe them with 'identify'. Use -print0
  # and read -d '' to safely handle unusual filenames.
  find /usr/bin -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}")
        # Remove the "sensor-" prefix
        SCRIPT_NAME=${SCRIPT_NAME#sensor-}
        echo "${SCRIPT_NAME}"
      fi
    done
  exit 0
fi

SCRIPT="$1"

if [[ $# -eq 2 ]]; then
  DEV="$2"
else
  DEV="all"
fi

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

# Initialize accumulator
ALL_JSON=''

if [[ "$SCRIPT" == "all" ]]; then
  # Iterate over sensor names (call this script recursively in 'list' mode)
  while IFS= read -r SENSOR_NAME; do
    DEV="all"
    SENS_SCRIPT="/usr/bin/sensor-${SENSOR_NAME}"

    # Run sensor script with timeout and capture output and return code
    SENS_JSON=$(timeout 5 "${SENS_SCRIPT}" "${DEV}" 2>/dev/null) || rc=$?
    rc=${rc:-0}

    # If error, skip to next sensor
    if [[ $rc -ne 0 ]]; then
      continue
    fi

    # If the internal object of SENS_JSON is true then exclude it from the output
    if [[ "${SCRIPT}" == "external" ]] && [[ "$(echo "${SENS_JSON}" | jq -r 'any(.[]; .internal == true)')" == "true" ]]; then
      continue
    fi

    # If the internal object of SENS_JSON is false then exclude it when asking for 'internal'
    if [[ "${SCRIPT}" == "internal" ]] && [[ "$(echo "${SENS_JSON}" | jq -r 'any(.[]; .internal == false)')" == "true" ]]; then
      continue
    fi

    # Inject node_id and timestamp using jq with args (safer than string interpolation)
    SENS_JSON=$(echo "${SENS_JSON}" | jq -c --arg node "${PI_SERIAL}" --argjson ts "${TSTAMP}" 'map(.node_id = $node | .timestamp = $ts)')

    # Use jq to merge the arrays ALL_JSON and SENS_JSON
    if [[ -z "${ALL_JSON}" ]]; then
      ALL_JSON="${SENS_JSON}"
    else
      ALL_JSON=$(printf '%s\n%s\n' "${ALL_JSON}" "${SENS_JSON}" | jq -s 'add')
    fi
  done < <("${SELF}" list)

  echo "${ALL_JSON}"
  exit 0
fi

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

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

SENS_SCRIPT="/usr/bin/sensor-$1"

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

# Run the sensor script
SENS_JSON=$("${SENS_SCRIPT}" "${DEV}")
rc=$?

# Terminate with error code from sensor script if error thrown
if [[ $rc -ne 0 ]]; then
  if [[ $rc -eq 20 ]]; then
    echo "Unknown device"
  elif [[ $rc -eq 21 ]]; then
    echo "Unknown sensor"
  fi
  exit $rc
fi

SENS_JSON=$(echo "${SENS_JSON}" | jq -c --arg node "${PI_SERIAL}" --argjson ts "${TSTAMP}" 'map(.node_id = $node | .timestamp = $ts)')

echo "${SENS_JSON}"

exit 0
