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

HOTSPOT_NAME="Hotspot"   # so heißt die NM-Verbindung standardmäßig
# Optional bevorzugtes WiFi-Interface (z.B. via HOTSPOT_WIFI_IF env)
PREFERRED_WIFI_IF="${HOTSPOT_WIFI_IF:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_PATH="${SCRIPT_DIR}/hotspot-toggle"
MODE=""
PRIV_WIFI_IF=""
PRIV_UPLINK_IF=""
PRIV_RUNTIME_DIR=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --privileged-enable)
      MODE="enable"
      shift
      ;;
    --privileged-disable)
      MODE="disable"
      shift
      ;;
    --wifi-if)
      PRIV_WIFI_IF="${2:-}"
      shift 2
      ;;
    --uplink-if)
      PRIV_UPLINK_IF="${2:-}"
      shift 2
      ;;
    --runtime-dir)
      PRIV_RUNTIME_DIR="${2:-}"
      shift 2
      ;;
    *)
      break
      ;;
  esac
done

RUNTIME_DIR="${PRIV_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/run/user/$(id -u)}}"
IP_FORWARD_STATE_FILE="${RUNTIME_DIR}/hotspot-toggle.ip_forward"
NAT_STATE_FILE="${RUNTIME_DIR}/hotspot-toggle.nat"
IPTABLES_BIN="$(command -v iptables || true)"
SYSCTL_BIN="$(command -v sysctl || true)"
SUDO_BIN="$(command -v sudo || true)"
PKEXEC_BIN="$(command -v pkexec || true)"
PRIV_MODE=""

if [[ -z "${IPTABLES_BIN}" ]]; then
  IPTABLES_BIN="/usr/sbin/iptables"
fi

if [[ -z "${SYSCTL_BIN}" ]]; then
  SYSCTL_BIN="/usr/sbin/sysctl"
fi

mkdir -p "${RUNTIME_DIR}" 2>/dev/null || true

LOCALE_DIR=""
for candidate in \
  "${SCRIPT_DIR}/locale" \
  "${SCRIPT_DIR}/hotspot@yurij.de/locale" \
  "${HOME}/.local/share/gnome-shell/extensions/hotspot@yurij.de/locale"; do
  if [[ -d "${candidate}" ]]; then
    LOCALE_DIR="${candidate}"
    break
  fi
done

HAS_GETTEXT=0
if command -v gettext >/dev/null 2>&1 && [[ -n "${LOCALE_DIR}" ]]; then
  export TEXTDOMAIN="hotspot-toggle"
  export TEXTDOMAINDIR="${LOCALE_DIR}"
  HAS_GETTEXT=1
fi

translate() {
  local msg=$1

  if ((HAS_GETTEXT)); then
    gettext "${msg}"
  else
    echo "${msg}"
  fi
}

log() {
  logger -t hotspot-toggle "$1"
}

sudo_needs_auth() {
  local output=$1

  if [[ "${output}" == sudo:* || "${output}" == *$'\nsudo:'* ]]; then
    return 0
  fi

  return 1
}

run_privileged() {
  local output rc

  if [[ "${EUID}" -eq 0 ]]; then
    "$@"
    return $?
  fi

  if [[ "${PRIV_MODE}" == "sudo" && -n "${SUDO_BIN}" ]]; then
    "${SUDO_BIN}" -n "$@"
    return $?
  fi

  if [[ "${PRIV_MODE}" == "pkexec" && -n "${PKEXEC_BIN}" ]]; then
    "${PKEXEC_BIN}" "$@"
    return $?
  fi

  if [[ -n "${SUDO_BIN}" ]]; then
    if output=$("${SUDO_BIN}" -n "$@" 2>&1); then
      PRIV_MODE="sudo"
      return 0
    fi
    rc=$?
    if sudo_needs_auth "${output}"; then
      if [[ -n "${PKEXEC_BIN}" ]]; then
        if "${PKEXEC_BIN}" "$@"; then
          PRIV_MODE="pkexec"
          return 0
        fi
        return $?
      fi
      if [[ -t 0 || -t 1 ]]; then
        "${SUDO_BIN}" "$@"
        return $?
      fi
      if [[ -n "${output}" ]]; then
        echo "${output}" >&2
      fi
      log "sudo benötigt Passwort, pkexec nicht verfügbar"
      return "${rc}"
    fi

    if [[ -n "${output}" ]]; then
      echo "${output}" >&2
    fi
    return "${rc}"
  fi

  if [[ -n "${PKEXEC_BIN}" ]]; then
    if "${PKEXEC_BIN}" "$@"; then
      PRIV_MODE="pkexec"
      return 0
    fi
    return $?
  fi

  log "Fehlende Berechtigungen: sudo/pkexec nicht verfügbar"
  return 1
}

run_privileged_script() {
  if [[ ! -x "${SCRIPT_PATH}" ]]; then
    log "Hotspot-Script nicht ausführbar: ${SCRIPT_PATH}"
    return 1
  fi

  run_privileged "${SCRIPT_PATH}" "$@"
}

privileged_enable() {
  local wifi_if=$1
  local uplink_if=$2
  local status=0

  if [[ -n "${wifi_if}" ]]; then
    ensure_firewall_open "${wifi_if}" || status=1
  else
    log "WARNUNG: Hotspot-Interface fehlt, Firewall-Regeln übersprungen"
    status=1
  fi

  ensure_nat_and_forwarding "${wifi_if}" "${uplink_if}" || status=1

  return $status
}

privileged_disable() {
  local wifi_if=$1

  if [[ -n "${wifi_if}" ]]; then
    clear_firewall_rules "${wifi_if}"
  else
    log "Hotspot-Interface nicht gefunden, überspringe Firewall-Cleanup"
  fi

  clear_nat_rules
  restore_ip_forward
}

find_wifi_interface() {
  local ifname=""

  # Erst versuchen, das Interface über die Hotspot-Verbindung zu lesen
  ifname=$( (nmcli -t -g GENERAL.DEVICES connection show "${HOTSPOT_NAME}" 2>/dev/null | cut -d: -f1) || true )

  # Falls konfiguriert, gewünschtes Interface bevorzugen
  if [[ -z "${ifname}" && -n "${PREFERRED_WIFI_IF}" ]]; then
    if nmcli -t -f DEVICE,TYPE device status 2>/dev/null | awk -F: -v d="${PREFERRED_WIFI_IF}" '$1==d && $2=="wifi"{found=1} END{exit found?0:1}'; then
      ifname="${PREFERRED_WIFI_IF}"
    else
      log "Konfiguriertes Interface ${PREFERRED_WIFI_IF} nicht verfügbar/kein WiFi"
    fi
  fi

  # Fallback: erstes WiFi-Interface aus der Geräteübersicht
  if [[ -z "${ifname}" ]]; then
    ifname=$( (nmcli -t -f DEVICE,TYPE device status 2>/dev/null | awk -F: '$2=="wifi"{print $1; exit}') || true )
  fi

  if [[ -z "${ifname}" ]]; then
    log "Kein WLAN-Interface für Hotspot gefunden"
    return 1
  fi

  echo "${ifname}"
}

find_uplink_interface() {
  ip route show default 0.0.0.0/0 | awk 'NR==1{print $5}'
}

enable_ip_forward() {
  local current
  current=$(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null || echo 0)

  if [[ "${current}" != "1" ]]; then
    echo "${current}" > "${IP_FORWARD_STATE_FILE}" 2>/dev/null || true
    if run_privileged "${SYSCTL_BIN}" -w net.ipv4.ip_forward=1 >/dev/null; then
      log "ip_forward auf 1 gesetzt"
    else
      log "WARNUNG: ip_forward konnte nicht aktiviert werden"
      return 1
    fi
  else
    log "ip_forward bereits aktiv"
  fi
}

restore_ip_forward() {
  local saved current

  if [[ ! -f "${IP_FORWARD_STATE_FILE}" ]]; then
    return 0
  fi

  saved=$(cat "${IP_FORWARD_STATE_FILE}" 2>/dev/null || echo "")
  rm -f "${IP_FORWARD_STATE_FILE}"

  current=$(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null || echo "")
  if [[ -n "${saved}" && "${saved}" != "${current}" ]]; then
    run_privileged "${SYSCTL_BIN}" -w net.ipv4.ip_forward="${saved}" >/dev/null || \
      log "WARNUNG: ip_forward konnte nicht zurückgesetzt werden"
  fi
}

ensure_firewall_open() {
  local wifi_if=$1
  local status=0

  # Docker-Kette: akzeptiere Verkehr vom Hotspot-Interface
  if run_privileged "${IPTABLES_BIN}" -n -L DOCKER-USER >/dev/null 2>&1; then
    if ! run_privileged "${IPTABLES_BIN}" -C DOCKER-USER -i "${wifi_if}" -j ACCEPT 2>/dev/null; then
      if run_privileged "${IPTABLES_BIN}" -I DOCKER-USER -i "${wifi_if}" -j ACCEPT; then
        log "Firewall-Regel in DOCKER-USER hinzugefügt (${wifi_if})"
      else
        log "WARNUNG: DOCKER-USER-Regel konnte nicht gesetzt werden"
        status=1
      fi
    else
      log "Firewall-Regel in DOCKER-USER bereits vorhanden (${wifi_if})"
    fi
  else
    log "DOCKER-USER Chain nicht gefunden, Docker-Freigabe übersprungen"
    status=1
  fi

  # FORWARD-Kette: Verkehr vom Hotspot ins Netz erlauben
  if ! run_privileged "${IPTABLES_BIN}" -C FORWARD -i "${wifi_if}" -j ACCEPT 2>/dev/null; then
    if run_privileged "${IPTABLES_BIN}" -I FORWARD -i "${wifi_if}" -j ACCEPT; then
      log "FORWARD-Regel für eingehenden Hotspot-Traffic gesetzt (${wifi_if})"
    else
      log "WARNUNG: FORWARD-Regel (eingehend) konnte nicht gesetzt werden"
      status=1
    fi
  else
    log "FORWARD-Regel (eingehend) bereits vorhanden (${wifi_if})"
  fi

  # Rückrichtung erlauben, damit Antworten durchkommen
  if ! run_privileged "${IPTABLES_BIN}" -C FORWARD -o "${wifi_if}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
    if run_privileged "${IPTABLES_BIN}" -I FORWARD -o "${wifi_if}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; then
      log "FORWARD-Regel für Rückverkehr gesetzt (${wifi_if})"
    else
      log "WARNUNG: FORWARD-Regel (Rückverkehr) konnte nicht gesetzt werden"
      status=1
    fi
  else
    log "FORWARD-Regel (Rückverkehr) bereits vorhanden (${wifi_if})"
  fi

  return $status
}

ensure_nat_and_forwarding() {
  local wifi_if=$1
  local uplink_if=${2:-}
  local status=0

  if [[ -z "${uplink_if}" ]]; then
    uplink_if=$(find_uplink_interface)
  fi
  if [[ -z "${uplink_if}" ]]; then
    log "WARNUNG: kein Uplink-Interface für NAT gefunden"
    status=1
  else
    log "NAT/FORWARD: ${wifi_if} -> ${uplink_if}"
    if ! run_privileged "${IPTABLES_BIN}" -t nat -C POSTROUTING -o "${uplink_if}" -j MASQUERADE 2>/dev/null; then
      if run_privileged "${IPTABLES_BIN}" -t nat -A POSTROUTING -o "${uplink_if}" -j MASQUERADE; then
        echo "${uplink_if}" > "${NAT_STATE_FILE}" 2>/dev/null || true
        log "NAT-MASQUERADE gesetzt auf ${uplink_if}"
      else
        log "WARNUNG: NAT-Regel konnte nicht gesetzt werden (${uplink_if})"
        status=1
      fi
    else
      log "NAT-MASQUERADE bereits vorhanden (${uplink_if})"
    fi
  fi

  enable_ip_forward || status=1

  return $status
}

clear_firewall_rules() {
  local wifi_if=$1

  if run_privileged "${IPTABLES_BIN}" -D DOCKER-USER -i "${wifi_if}" -j ACCEPT 2>/dev/null; then
    log "Firewall-Regel für Hotspot aus DOCKER-USER entfernt (${wifi_if})"
  fi

  if run_privileged "${IPTABLES_BIN}" -D FORWARD -i "${wifi_if}" -j ACCEPT 2>/dev/null; then
    log "FORWARD-Regel für Hotspot entfernt (${wifi_if})"
  fi

  if run_privileged "${IPTABLES_BIN}" -D FORWARD -o "${wifi_if}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
    log "FORWARD-Rückregel für Hotspot entfernt (${wifi_if})"
  fi
}

clear_nat_rules() {
  local uplink_if

  if [[ ! -f "${NAT_STATE_FILE}" ]]; then
    return 0
  fi

  uplink_if=$(cat "${NAT_STATE_FILE}" 2>/dev/null || echo "")
  rm -f "${NAT_STATE_FILE}"

  if [[ -n "${uplink_if}" ]]; then
    if run_privileged "${IPTABLES_BIN}" -t nat -D POSTROUTING -o "${uplink_if}" -j MASQUERADE 2>/dev/null; then
      log "NAT-MASQUERADE entfernt (${uplink_if})"
    fi
  fi
}

is_active() {
  nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep -q "^${HOTSPOT_NAME}"
}

if [[ "${MODE}" == "enable" ]]; then
  privileged_enable "${PRIV_WIFI_IF}" "${PRIV_UPLINK_IF}"
  exit $?
fi

if [[ "${MODE}" == "disable" ]]; then
  privileged_disable "${PRIV_WIFI_IF}"
  exit $?
fi

if is_active; then
  priv_args=(--privileged-disable --runtime-dir "${RUNTIME_DIR}")
  if wifi_if=$(find_wifi_interface); then
    priv_args+=(--wifi-if "${wifi_if}")
  else
    log "Hotspot-Interface nicht gefunden, überspringe Firewall-Cleanup"
  fi
  if ! run_privileged_script "${priv_args[@]}"; then
    log "WARNUNG: Privilegierte Deaktivierung fehlgeschlagen"
  fi

  nmcli connection down "${HOTSPOT_NAME}"
  nmcli radio wifi off
  notify-send "$(translate "Hotspot deaktiviert")"
else
  # Falls Verbindung noch nicht existiert, versuch sie zu erstellen:
  nmcli radio wifi on
  nmcli connection up "${HOTSPOT_NAME}" 2>/dev/null || \
    nmcli dev wifi hotspot 

  if ! wifi_if=$(find_wifi_interface); then
    notify-send "$(translate "Kein WLAN-Interface für Hotspot gefunden")"
    exit 1
  fi
  log "Nutze WiFi-Interface ${wifi_if} für Hotspot-Firewall"

  fw_status=0
  nat_status=0
  if ! run_privileged_script --privileged-enable --wifi-if "${wifi_if}" --runtime-dir "${RUNTIME_DIR}"; then
    log "WARNUNG: Privilegierte Aktivierung fehlgeschlagen"
    fw_status=1
    nat_status=1
  fi

  if ((fw_status==0 && nat_status==0)); then
    notify-send "$(translate "Hotspot aktiviert")"
  else
    notify-send "$(translate "Hotspot aktiviert (Warnung)")"
  fi
fi
