#!/bin/bash

# omarchy:summary=Returns a formatted battery status string with percentage and power draw/charge.
# omarchy:args=[--shell]

shell_output=false
power_supply_path="${OMARCHY_POWER_SUPPLY_PATH:-/sys/class/power_supply}"

case "${1:-}" in
  "")
    ;;
  --shell)
    shell_output=true
    ;;
  *)
    echo "Usage: omarchy-battery-status [--shell]" >&2
    exit 2
    ;;
esac

# Durations and the status line read in the Omarchy language; the --shell
# time feeds the power panel, which shows it as-is.
# The power reading is joined with no-break spaces so a longer language wraps
# the line between its parts rather than inside "13.1W / 53Wh".
format_duration() {
  local minutes=$1
  if [[ -z $minutes ]] || (( minutes <= 0 )); then
    return
  elif (( minutes < 60 )); then
    omarchy-i18n battery.durationMinutes "$minutes"
  elif (( minutes % 60 == 0 )); then
    omarchy-i18n battery.durationHours "$(( minutes / 60 ))"
  else
    omarchy-i18n battery.durationHoursMinutes "$(( minutes / 60 ))" "$(( minutes % 60 ))"
  fi
}

# UPower names the device after the sysfs entry, and only some kernels call
# that BAT*: Apple Silicon reports battery_macsmc_battery. Match on the class,
# then take the first one that powers the machine: a wireless mouse or keyboard
# is a battery_ device too, and UPower marks it "power supply: no".
battery=""
battery_info=""
while read -r candidate; do
  [[ -n $candidate ]] || continue
  candidate_info=$(upower -i "$candidate" 2>/dev/null) || continue
  if grep -qE '^\s*power supply:\s+yes' <<<"$candidate_info"; then
    battery=$candidate
    battery_info=$candidate_info
    break
  fi
done < <(upower -e 2>/dev/null | grep '/battery_')
[[ -z $battery ]] && exit 0

percentage=$(awk '/percentage/ { print int($2); exit }' <<<"$battery_info")
capacity=$(awk '/energy-full:/ { printf "%d", $2; exit }' <<<"$battery_info")
energy=$(awk '/^ *energy:/ { print $2; exit }' <<<"$battery_info")
energy_full=$(awk '/energy-full:/ { print $2; exit }' <<<"$battery_info")
upower_hours=$(awk '/time to (empty|full)/ { print ($5 ~ /^minute/ ? $4 / 60 : $4); exit }' <<<"$battery_info")
power_rate_raw=$(awk '/energy-rate/ { print $2; exit }' <<<"$battery_info")
native_path=$(awk '/native-path/ { print $2; exit }' <<<"$battery_info")
battery_path="$power_supply_path/$native_path"

# UPower's energy-rate can lag the kernel telemetry by tens of seconds. Use
# the instantaneous sysfs reading when available so the open panel stays live.
if [[ -r $battery_path/power_now ]]; then
  power_rate_raw=$(awk -v microwatts="$(<"$battery_path/power_now")" 'BEGIN { print microwatts / 1000000 }')
elif [[ -r $battery_path/current_now && -r $battery_path/voltage_now ]]; then
  power_rate_raw=$(awk \
    -v microamps="$(<"$battery_path/current_now")" \
    -v microvolts="$(<"$battery_path/voltage_now")" \
    'BEGIN { print microamps * microvolts / 1000000000000 }')
fi

# Some kernels sign power_now by direction (negative while discharging on
# Apple Silicon); the state field already says which way it flows.
power_rate=$(awk -v rate="${power_rate_raw:-0}" 'BEGIN {
  if (rate < 0) rate = -rate
  rounded = sprintf("%.1f", rate)
  sub(/\.0$/, "", rounded)
  print rounded
}')
state=$(awk '/state/ { print $2; exit }' <<<"$battery_info")
threshold_start=$(awk '/charge-start-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info")
threshold_end=$(awk '/charge-end-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info")

# Read the thresholds from the battery UPower resolved rather than a BAT* glob,
# which misses every battery with another name.
[[ -n $threshold_end || ! -r $battery_path/charge_control_end_threshold ]] || threshold_end=$(<"$battery_path/charge_control_end_threshold")
[[ -n $threshold_start || ! -r $battery_path/charge_control_start_threshold ]] || threshold_start=$(<"$battery_path/charge_control_start_threshold")
# The time to full counts to this battery's own limit, never another one's.
charge_limit=${threshold_end:-100}
[[ -n $threshold_end ]] || threshold_end=$(cat "$power_supply_path"/BAT*/charge_control_end_threshold 2>/dev/null | head -1)
[[ -n $threshold_start ]] || threshold_start=$(cat "$power_supply_path"/BAT*/charge_control_start_threshold 2>/dev/null | head -1)

# Time left is the energy left over the draw shown beside it, so the two always
# agree; charging counts up to the charge limit. UPower's own estimate averages
# the draw, and stands in only when the readings to do the sum are missing.
minutes_remaining=$(awk \
  -v energy="$energy" -v full="$energy_full" -v rate="${power_rate_raw:-0}" \
  -v state="$state" -v limit="$charge_limit" -v fallback="$upower_hours" 'BEGIN {
  if (rate < 0) rate = -rate
  if (energy != "" && full != "" && rate > 0.2) {
    hours = state == "charging" ? (full * limit / 100 - energy) / rate : energy / rate
  } else {
    hours = fallback
  }
  if (hours == "" || hours <= 0) exit
  printf "%d", int(hours * 60 + 0.5)
}')
time_remaining=$(format_duration "$minutes_remaining")

ac_online=false
for supply in "$power_supply_path"/*; do
  [[ -r $supply/type ]] || continue
  [[ $(<"$supply/type") == "Mains" ]] || continue
  [[ -r $supply/online ]] || continue

  if [[ $(<"$supply/online") == "1" ]]; then
    ac_online=true
    break
  fi
done

charge_idle=false
if awk -v rate="${power_rate_raw:-0}" 'BEGIN { if (rate < 0) rate = -rate; exit !(rate <= 0.2) }'; then
  charge_idle=true
fi

charge_holding=false
if [[ $ac_online == "true" && -n $threshold_end ]]; then
  if [[ $state == "pending-charge" ]]; then
    charge_holding=true
  elif [[ $state == "fully-charged" ]] && (( percentage < 99 )); then
    charge_holding=true
  elif [[ $state == "charging" && $charge_idle == "true" ]] && (( threshold_end < 99 && percentage >= threshold_end )); then
    charge_holding=true
  fi
fi

if [[ $shell_output == "true" ]]; then
  printf 'percentage\t%s\n' "${percentage}%"
  if [[ $charge_holding == "true" ]]; then
    printf 'state\tholding\n'
  else
    printf 'state\t%s\n' "$state"
  fi
  printf 'rate\t%s\n' "${power_rate}W"
  printf 'size\t%s\n' "${capacity}Wh"
  printf 'time\t%s\n' "$time_remaining"

  cycles=""
  [[ ! -r $battery_path/cycle_count ]] || cycles=$(<"$battery_path/cycle_count")
  [[ -n $cycles ]] || cycles=$(cat "$power_supply_path"/BAT*/cycle_count 2>/dev/null | head -1)

  [[ -n $cycles ]] && printf 'cycles\t%s\n' "$cycles"

  if [[ -n $threshold_end ]]; then
    if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then
      printf 'threshold\t%s-%s%%\n' "$threshold_start" "$threshold_end"
    else
      printf 'threshold\t%s%%\n' "$threshold_end"
    fi
  fi

  exit 0
fi

# Each part of the line shows only when it has a reading behind it: a full or
# idle battery has no time to count and no draw worth a "0W".
battery_full=false
if [[ $state == "fully-charged" ]]; then
  battery_full=true
elif [[ $ac_online == "true" ]] && (( percentage >= 100 )); then
  battery_full=true
fi

parts=("$(omarchy-i18n battery.statusLabel "$percentage")")

if [[ $charge_holding == "true" ]]; then
  if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then
    threshold_label="$(omarchy-i18n --number "$threshold_start")-$(omarchy-i18n --number "$threshold_end")%"
  else
    threshold_label="$(omarchy-i18n --number "$threshold_end")%"
  fi

  parts+=("$(omarchy-i18n battery.holdingAt "$threshold_label")")
elif [[ $battery_full == "true" ]]; then
  parts+=("$(omarchy-i18n battery.fullyCharged)")
elif [[ -n $time_remaining && $state == "charging" ]]; then
  parts+=("$(omarchy-i18n battery.toFull "$time_remaining")")
elif [[ -n $time_remaining ]]; then
  parts+=("$(omarchy-i18n battery.timeLeft "$time_remaining")")
fi

# The power reading shows its numbers in the language's own way, with an arrow
# for the direction of a live draw.
capacity_reading=""
(( ${capacity:-0} > 0 )) && capacity_reading="$(omarchy-i18n --number "$capacity")Wh"
if [[ $charge_holding == "false" && $battery_full == "false" && $charge_idle == "false" ]]; then
  if [[ $state == "charging" ]]; then
    draw=" $(omarchy-i18n --number "$power_rate")W"
  else
    draw=" $(omarchy-i18n --number "$power_rate")W"
  fi

  if [[ -n $capacity_reading ]]; then
    parts+=("${draw} / ${capacity_reading}")
  else
    parts+=("$draw")
  fi
elif [[ -n $capacity_reading ]]; then
  parts+=("$capacity_reading")
fi

line=${parts[0]}
for part in "${parts[@]:1}"; do
  line+="  ·  $part"
done
echo "$line"
