#!/usr/bin/env bash

hardware_led_enabled() {
  python3 - <<'PY' 2>/dev/null
import os, sys, toml
try:
    import pwnagotchi
    defaults = toml.load(os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.toml'))
except Exception:
    sys.exit(0)
enabled = defaults.get('main', {}).get('hardware_led', True)
try:
    user = toml.load('/etc/pwnagotchi/config.toml')
    if 'hardware_led' in user.get('main', {}):
        enabled = user['main']['hardware_led']
except Exception:
    pass
sys.exit(0 if enabled else 1)
PY
}

blink_led() {
  local led_dir=/sys/class/leds/led0
  [ -d "$led_dir" ] || led_dir=/sys/class/leds/ACT

  for i in $(seq 1 "$1"); do
    echo 0 | tee "$led_dir/brightness"
    sleep 0.3
    echo 1 | tee "$led_dir/brightness"
    sleep 0.3
  done

  if hardware_led_enabled; then
    echo mmc0 | tee "$led_dir/trigger"
  else
    echo none | tee "$led_dir/trigger"
    echo 0 | tee "$led_dir/brightness"
  fi
}

brcm_hard_failure() {
  if journalctl -k --since -6m 2>/dev/null | grep -qE 'brcmf_fw_crashed|failed backplane access|dongle is not responding'; then
    return 0
  fi
  if [[ "$(journalctl -k --since -10m 2>/dev/null | grep -c 'brcmf_psm_watchdog_notify')" -ge 3 ]]; then
    return 0
  fi
  return 1
}

brcm_chip_alive() {
  iw dev wlan0 info >/dev/null 2>&1
}

check_brcm() {
  if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
    return 1
  fi
  if [[ "$(journalctl -k --since -10m 2>/dev/null | grep -c 'brcmf_psm_watchdog_notify')" -ge 3 ]]; then
    return 1
  fi
  if [[ "$(journalctl -k --since -6m 2>/dev/null | grep -c 'brcmf_fw_crashed')" -ge 1 ]]; then
    return 1
  fi
  return 0
}

_brcm_verify_reattached() {
  if journalctl -k --since -15s 2>/dev/null | grep -qE 'dongle is not responding|brcmf_attach failed|failed backplane access|error -22 whilst initialising'; then
    return 1
  fi
  local iface driver
  for path in /sys/class/net/*/wireless; do
    [ -e "$path" ] || continue
    iface="$(basename "$(dirname "$path")")"
    driver="$(basename "$(readlink -f "/sys/class/net/$iface/device/driver" 2>/dev/null)")"
    if [ "$driver" = "brcmfmac" ]; then
      return 0
    fi
  done
  return 1
}

BRCM_REBOOT_STATE_FILE="/root/.pwnagotchi-brcm-reboot-state"
BRCM_REBOOT_WINDOW_SECS=1800
BRCM_REBOOT_MAX_IN_WINDOW=3

brcm_reboot_allowed() {
  local now count last
  now="$(date +%s)"
  count=0
  last=0
  if [ -r "$BRCM_REBOOT_STATE_FILE" ]; then
    read -r count last < "$BRCM_REBOOT_STATE_FILE" || true
    case "$count" in ''|*[!0-9]*) count=0 ;; esac
    case "$last"  in ''|*[!0-9]*) last=0 ;; esac
  fi

  if [ $((now - last)) -gt "$BRCM_REBOOT_WINDOW_SECS" ]; then
    count=0
  fi

  if [ "$count" -ge "$BRCM_REBOOT_MAX_IN_WINDOW" ]; then
    return 1
  fi

  count=$((count + 1))
  printf '%d %d' "$count" "$now" > "$BRCM_REBOOT_STATE_FILE" 2>/dev/null
  sync
  return 0
}

RELOAD_BRCM_MARKER="/run/pwnagotchi-reload-brcm-last"
RELOAD_BRCM_COOLDOWN_SECONDS=90
RELOAD_BRCM_SKIPPED=75

reload_brcm() {
  local now last
  now="$(date +%s)"
  if [ -f "$RELOAD_BRCM_MARKER" ]; then
    last="$(stat -c %Y "$RELOAD_BRCM_MARKER" 2>/dev/null || echo 0)"
    if [ $((now - last)) -lt "$RELOAD_BRCM_COOLDOWN_SECONDS" ]; then
      return "$RELOAD_BRCM_SKIPPED"
    fi
  fi
  touch "$RELOAD_BRCM_MARKER"

  if ! modprobe -r brcmfmac; then
    return 1
  fi
  if ! modprobe brcmfmac; then
    return 1
  fi

  sleep 5
  _brcm_verify_reattached
}

check_bt() {
  if [[ "$(journalctl -k --since -5m 2>/dev/null | grep -c 'hci0: hardware error')" -ge 1 ]]; then
    return 1
  fi
  if ! hciconfig hci0 2>/dev/null | grep -q 'UP RUNNING'; then
    return 1
  fi
  return 0
}

reload_bt() {
  systemctl stop bt-agent bluetooth 2>/dev/null
  timeout 5 hciconfig hci0 down 2>/dev/null
  if ! modprobe -r hci_uart; then
    return 1
  fi
  if ! modprobe hci_uart; then
    return 1
  fi
  sleep 4
  systemctl start bluetooth bt-agent
  hciconfig hci0 2>/dev/null | grep -q 'UP RUNNING'
}

phy_iface() {
  local iface driver
  for path in /sys/class/net/*/wireless; do
    [ -e "$path" ] || continue
    iface="$(basename "$(dirname "$path")")"
    if [ "$iface" = "wlan0" ] || [ "$iface" = "mon0" ]; then
      continue
    fi
    driver="$(basename "$(readlink -f "/sys/class/net/$iface/device/driver" 2>/dev/null)")"
    if [ "$driver" = "brcmfmac" ]; then
      continue
    fi
    echo "$iface"
    return 0
  done
  echo "wlan0"
}

start_monitor_interface() {
  local iface
  iface="$(phy_iface)"

  local driver kind
  driver="$(basename "$(readlink -f "/sys/class/net/$iface/device/driver" 2>/dev/null)")"
  if [ "$driver" = "brcmfmac" ]; then
    kind="built-in WiFi"
  elif [ -z "$driver" ] || [ "$driver" = "." ]; then
    kind="unknown, $iface unavailable"
  else
    kind="external WiFi adapter"
  fi
  logger -t pwnlib "mon0 will use $kind ($iface, driver: $driver)"

  rfkill unblock all
  iw dev "$iface" set power_save off

  ifconfig "$iface" up

  local target_phy
  target_phy="$(iw dev "$iface" info | awk '/wiphy/{print "phy"$2}')"

  local mon_phy=""
  if ip link show mon0 &>/dev/null && iw dev mon0 info &>/dev/null; then
    mon_phy="$(iw dev mon0 info | awk '/wiphy/{print "phy"$2}')"
  fi

  if [[ -z "$mon_phy" || "$mon_phy" != "$target_phy" ]]; then
    if ip link show mon0 &>/dev/null; then
      ip link set mon0 down &>/dev/null
      if [[ -f /run/pwnagotchi-mon0-src-iface ]]; then
        iw dev mon0 set type managed
        ip link set mon0 name "$(cat /run/pwnagotchi-mon0-src-iface)"
      else
        iw dev mon0 del &>/dev/null
      fi
    fi
    rm -f /run/pwnagotchi-mon0-src-iface

    if [[ "$iface" == "wlan0" ]]; then
      iw phy "$target_phy" interface add mon0 type monitor
    else
      ip link set "$iface" down
      iw dev "$iface" set type monitor
      ip link set "$iface" name mon0
      echo "$iface" > /run/pwnagotchi-mon0-src-iface
    fi
  fi

  ifconfig mon0 up
  [[ "$iface" != "mon0" ]] && ip link show "$iface" &>/dev/null && ifconfig "$iface" down

  iw dev "$iface" set power_save off 2>/dev/null

  ip link show mon0 up &>/dev/null
}

stop_monitor_interface() {
  if [[ -f /run/pwnagotchi-mon0-src-iface ]]; then
    local src_iface
    src_iface="$(cat /run/pwnagotchi-mon0-src-iface)"
    ip link set mon0 down
    iw dev mon0 set type managed
    ip link set mon0 name "$src_iface"
    ifconfig "$src_iface" up
  else
    ifconfig mon0 down && iw dev mon0 del
    ifconfig wlan0 up
  fi

  rm -f /run/pwnagotchi-mon0-src-iface
}

is_interface_up() {
  if grep -qi 'up' /sys/class/net/$1/operstate; then
    return 0
  else
    return 1
  fi
}

is_auto_mode() {
  if [ -f /root/.pwnagotchi-manual ]; then
    rm -rf /root/.pwnagotchi-manual
    return 1
  fi

  if [ -f /root/.pwnagotchi-auto ]; then
    rm -rf /root/.pwnagotchi-auto
    return 0
  fi

  return 0
}

is_auto_mode_no_delete() {
  if [ -f /root/.pwnagotchi-manual ]; then
    return 1
  fi

  if [ -f /root/.pwnagotchi-auto ]; then
    return 0
  fi

  return 0
}

is_crypted_mode() {
  if [ -f /root/.pwnagotchi-crypted ]; then
    return 0
  fi
  return 1
}

is_decrypted() {
  while read -r mapping container mount; do
    if ! mountpoint -q "$mount" >/dev/null 2>&1; then
      if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
        </tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
        if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
          if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
            echo "Container decrypted!"
          fi
        fi

        if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
            echo "Mounted /dev/mapper/$mapping to $mount"
            continue
        fi
      fi

      if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
        >/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
      fi

      if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
        >/dev/null 2>&1 decryption-webserver &
      fi

      if ! pgrep wpa_supplicant >/dev/null 2>&1; then
        >/tmp/wpa_supplicant.conf cat <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
ap_scan=2

network={
    ssid="DECRYPT-ME"
    mode=2
    key_mgmt=WPA-PSK
    psk="pwnagotchi"
    frequency=2437
}
EOF
        >/dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
      fi

      if ! pgrep dnsmasq >/dev/null 2>&1; then
        >/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
      fi

      return 1
    fi
  done </root/.pwnagotchi-crypted

  python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
  rm /tmp/.pwnagotchi-secret-*
  sync

  pkill wpa_supplicant
  pkill dnsmasq
  pid="$(pgrep -f "decryption-webserver")"
  [[ -n "$pid" ]] && kill "$pid"

  return 0
}
