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

SERVICE_NAME="power-network-shutdown-guard.service"
INSTALL_PATH="/usr/local/sbin/power-network-shutdown-guard.sh"
SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}"
LOG_TAG="power-network-shutdown-guard"

CHECK_INTERVAL_SECONDS="${CHECK_INTERVAL_SECONDS:-5}"
POWER_LOSS_GRACE_SECONDS="${POWER_LOSS_GRACE_SECONDS:-30}"
PING_TIMEOUT_SECONDS="${PING_TIMEOUT_SECONDS:-2}"
PING_TARGETS=("google.com" "1.1.1.1" "192.168.1.1")

print_usage() {
  cat <<'EOF'
Usage:
  ./power-network-shutdown-guard.sh --install
  ./power-network-shutdown-guard.sh --run
  ./power-network-shutdown-guard.sh --uninstall
  ./power-network-shutdown-guard.sh --status

Behavior:
  If incoming power is absent for more than 30 seconds, and all of these fail to ping:
  - google.com
  - 1.1.1.1
  - 192.168.1.1
  then a normal shutdown is started.
EOF
}

log_msg() {
  local message="$1"
  logger -t "${LOG_TAG}" -- "${message}" || true
  printf '%s %s\n' "$(date -Is)" "${message}" >&2
}

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "This action must be run as root." >&2
    exit 1
  fi
}

has_incoming_power() {
  local supply_path type_value online_value status_value

  shopt -s nullglob
  for supply_path in /sys/class/power_supply/*; do
    [[ -d "${supply_path}" ]] || continue
    [[ -r "${supply_path}/type" ]] || continue

    type_value=""
    read -r type_value < "${supply_path}/type" || type_value=""
    case "${type_value}" in
      Mains|USB|USB_C|USB_PD)
        if [[ -r "${supply_path}/online" ]]; then
          online_value="0"
          read -r online_value < "${supply_path}/online" || online_value="0"
          if [[ "${online_value}" == "1" ]]; then
            return 0
          fi
        fi
        ;;
    esac
  done

  for supply_path in /sys/class/power_supply/*; do
    [[ -d "${supply_path}" ]] || continue
    [[ -r "${supply_path}/type" ]] || continue

    type_value=""
    read -r type_value < "${supply_path}/type" || type_value=""
    if [[ "${type_value}" != "Battery" ]]; then
      continue
    fi

    if [[ -r "${supply_path}/status" ]]; then
      status_value="Unknown"
      read -r status_value < "${supply_path}/status" || status_value="Unknown"
      case "${status_value}" in
        Charging|Full)
          return 0
          ;;
      esac
    fi
  done

  return 1
}

can_ping() {
  local target="$1"
  ping -n -c 1 -W "${PING_TIMEOUT_SECONDS}" "${target}" >/dev/null 2>&1
}

all_ping_targets_unreachable() {
  local target
  for target in "${PING_TARGETS[@]}"; do
    if can_ping "${target}"; then
      return 1
    fi
  done
  return 0
}

run_monitor() {
  local outage_started_at=0
  local connectivity_notice_emitted=0
  local now elapsed

  if ! command -v ping >/dev/null 2>&1; then
    log_msg "ping command not found. Install iputils-ping."
    exit 1
  fi

  log_msg "Monitor started. Grace=${POWER_LOSS_GRACE_SECONDS}s targets=${PING_TARGETS[*]}"

  while true; do
    if has_incoming_power; then
      if (( outage_started_at > 0 )); then
        log_msg "Incoming power restored."
      fi
      outage_started_at=0
      connectivity_notice_emitted=0
    else
      if (( outage_started_at == 0 )); then
        outage_started_at="$(date +%s)"
        connectivity_notice_emitted=0
        log_msg "Incoming power lost. Waiting ${POWER_LOSS_GRACE_SECONDS}s before shutdown checks."
      fi

      now="$(date +%s)"
      elapsed=$(( now - outage_started_at ))

      if (( elapsed >= POWER_LOSS_GRACE_SECONDS )); then
        if all_ping_targets_unreachable; then
          log_msg "Power loss > ${POWER_LOSS_GRACE_SECONDS}s and all ping checks failed. Initiating shutdown."
          /sbin/shutdown -h now "Power+network outage detected"
          sleep 300
        elif (( connectivity_notice_emitted == 0 )); then
          log_msg "Power loss > ${POWER_LOSS_GRACE_SECONDS}s but at least one ping target is reachable; not shutting down."
          connectivity_notice_emitted=1
        fi
      fi
    fi

    sleep "${CHECK_INTERVAL_SECONDS}"
  done
}

install_self() {
  require_root

  local source_path
  source_path="$(readlink -f "$0")"

  install -m 0755 "${source_path}" "${INSTALL_PATH}"

  cat > "${SERVICE_PATH}" <<EOF
[Unit]
Description=Shutdown guard when power and network are both lost
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${INSTALL_PATH} --run
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

  systemctl daemon-reload
  systemctl enable --now "${SERVICE_NAME}"
  log_msg "Installed to ${INSTALL_PATH} and started ${SERVICE_NAME}."
}

uninstall_self() {
  require_root

  if systemctl list-unit-files | grep -q "^${SERVICE_NAME}"; then
    systemctl disable --now "${SERVICE_NAME}" || true
  fi

  rm -f "${SERVICE_PATH}"
  rm -f "${INSTALL_PATH}"
  systemctl daemon-reload
  log_msg "Uninstalled ${SERVICE_NAME} and removed ${INSTALL_PATH}."
}

show_status() {
  if command -v systemctl >/dev/null 2>&1; then
    systemctl status "${SERVICE_NAME}" --no-pager || true
  else
    echo "systemctl not found."
  fi
}

main() {
  local command="${1:-}"
  case "${command}" in
    --install)
      install_self
      ;;
    --run)
      run_monitor
      ;;
    --uninstall)
      uninstall_self
      ;;
    --status)
      show_status
      ;;
    --help|-h)
      print_usage
      ;;
    *)
      print_usage
      exit 1
      ;;
  esac
}

main "${@}"
