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

WG_DIR="/etc/wireguard"
WG_CONF="${WG_DIR}/wg0.conf"
WG_SERVICE="wg-quick@wg0"
SSH_SERVICE="ssh"

DEFAULT_PEER_PUBLIC_KEY="4F0N2czBHBOf3PYidV4Be1ccVUqLhXb4N3/1ohFJyRc="
DEFAULT_ALLOWED_IPS="1.1.1.0/24"
DEFAULT_ENDPOINT="185.241.197.138:13233"
DEFAULT_KEEPALIVE="10"
DEFAULT_DNS="8.8.8.8"

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Uruchom skrypt jako root, np.: sudo bash $0"
    exit 1
  fi
}

trim_spaces() {
  local s="$1"
  s="${s#"${s%%[![:space:]]*}"}"
  s="${s%"${s##*[![:space:]]}"}"
  printf '%s' "$s"
}

get_conf_value() {
  local key="$1"
  local file="$2"
  [[ -f "$file" ]] || return 0

  awk -v wanted="$key" '
    BEGIN { FS="=" }
    $0 ~ "^[[:space:]]*" wanted "[[:space:]]*=" {
      value = substr($0, index($0, "=") + 1)
      sub(/^[[:space:]]+/, "", value)
      sub(/[[:space:]]+$/, "", value)
      print value
      exit
    }
  ' "$file"
}

get_comment_public_key() {
  local file="$1"
  [[ -f "$file" ]] || return 0

  awk '
    /^#publickey:[[:space:]]*/ {
      sub(/^#publickey:[[:space:]]*/, "", $0)
      sub(/[[:space:]]+$/, "", $0)
      print
      exit
    }
  ' "$file"
}

extract_n_from_address() {
  local address="$1"
  if [[ "$address" =~ ^1\.1\.1\.([0-9]{1,3})/32$ ]]; then
    printf '%s' "${BASH_REMATCH[1]}"
  fi
}

is_valid_n() {
  local v="$1"
  [[ "$v" =~ ^[0-9]+$ ]] && (( v >= 1 && v <= 254 ))
}

is_nonempty() {
  [[ -n "${1:-}" ]]
}

is_valid_base64_wg_key() {
  local key="$1"
  [[ "$key" =~ ^[A-Za-z0-9+/]{43}=$ ]]
}

ask_keep_current() {
  local label="$1"
  local current="$2"
  local answer

  while true; do
    if [[ -n "$current" ]]; then
      read -r -p "$label [obecnie: $current] Zostawić bez zmian? [Y/n]: " answer
    else
      read -r -p "$label [brak obecnej wartości] Zostawić bez zmian? [Y/n]: " answer
    fi

    answer="$(trim_spaces "$answer")"
    case "${answer,,}" in
      ""|y|yes|t|tak) return 0 ;;
      n|no|nie) return 1 ;;
      *) echo "Wpisz Y lub n, albo naciśnij Enter." ;;
    esac
  done
}

ask_value() {
  local prompt="$1"
  local validator="${2:-}"
  local value

  while true; do
    read -r -p "$prompt: " value
    value="$(trim_spaces "$value")"

    if [[ -z "$value" ]]; then
      echo "Ta wartość nie może być pusta."
      continue
    fi

    if [[ -n "$validator" ]]; then
      if "$validator" "$value"; then
        printf '%s' "$value"
        return 0
      fi
      echo "Podano nieprawidłową wartość."
    else
      printf '%s' "$value"
      return 0
    fi
  done
}

ask_or_keep() {
  local label="$1"
  local current="$2"
  local validator="${3:-}"
  local fallback="${4:-}"

  if [[ -n "$current" ]]; then
    if ask_keep_current "$label" "$current"; then
      printf '%s' "$current"
    else
      ask_value "Podaj nową wartość dla: $label" "$validator"
    fi
  else
    if [[ -n "$fallback" ]]; then
      if ask_keep_current "$label" "$fallback"; then
        printf '%s' "$fallback"
      else
        ask_value "Podaj nową wartość dla: $label" "$validator"
      fi
    else
      ask_value "Podaj wartość dla: $label" "$validator"
    fi
  fi
}

generate_keys() {
  local private_key public_key
  umask 077
  private_key="$(wg genkey)"
  public_key="$(printf '%s' "$private_key" | wg pubkey)"
  printf '%s\n%s\n' "$private_key" "$public_key"
}

install_packages() {
  echo "==> Aktualizacja listy pakietów"
  apt update

  echo "==> Instalacja WireGuard i OpenSSH Server"
  apt install -y wireguard openssh-server
}

backup_existing_config() {
  if [[ -f "$WG_CONF" ]]; then
    local backup="${WG_CONF}.bak.$(date +%Y%m%d-%H%M%S)"
    cp -a "$WG_CONF" "$backup"
    echo "Utworzono kopię zapasową: $backup"
  fi
}

write_config() {
  local interface_public_key="$1"
  local interface_private_key="$2"
  local n="$3"
  local dns="$4"
  local peer_public_key="$5"
  local allowed_ips="$6"
  local endpoint="$7"
  local keepalive="$8"

  install -d -m 700 "$WG_DIR"

  cat > "$WG_CONF" <<EOF
#publickey: ${interface_public_key}

[Interface]
PrivateKey = ${interface_private_key}
Address = 1.1.1.${n}/32
DNS = ${dns}

[Peer]
PublicKey = ${peer_public_key}
AllowedIPs = ${allowed_ips}
Endpoint = ${endpoint}
PersistentKeepalive = ${keepalive}
EOF

  chmod 600 "$WG_CONF"
}

enable_and_start_services() {
  echo "==> Włączanie i uruchamianie WireGuard"
  systemctl daemon-reload
  systemctl enable "$WG_SERVICE"
  systemctl restart "$WG_SERVICE" || systemctl start "$WG_SERVICE"

  echo "==> Włączanie i uruchamianie SSH"
  systemctl enable "$SSH_SERVICE"
  systemctl restart "$SSH_SERVICE" || systemctl start "$SSH_SERVICE"
}

print_summary() {
  local interface_public_key="$1"
  local interface_private_key="$2"
  local n="$3"
  local dns="$4"
  local peer_public_key="$5"
  local allowed_ips="$6"
  local endpoint="$7"
  local keepalive="$8"

  echo
  echo "===== Wygenerowane / użyte klucze ====="
  echo "Public key interfejsu : ${interface_public_key}"
  echo "Private key interfejsu: ${interface_private_key}"
  echo
  echo "===== Końcowa konfiguracja ====="
  echo "Address             = 1.1.1.${n}/32"
  echo "DNS                 = ${dns}"
  echo "Peer PublicKey      = ${peer_public_key}"
  echo "AllowedIPs          = ${allowed_ips}"
  echo "Endpoint            = ${endpoint}"
  echo "PersistentKeepalive = ${keepalive}"
  echo
  echo "===== Status usług ====="
  systemctl --no-pager --full status "$WG_SERVICE" | sed -n '1,12p' || true
  echo
  systemctl --no-pager --full status "$SSH_SERVICE" | sed -n '1,12p' || true
}

main() {
  require_root
  install_packages

  local current_interface_private_key=""
  local current_interface_public_key=""
  local current_address=""
  local current_n=""
  local current_dns=""
  local current_peer_public_key=""
  local current_allowed_ips=""
  local current_endpoint=""
  local current_keepalive=""

  if [[ -f "$WG_CONF" ]]; then
    current_interface_private_key="$(get_conf_value "PrivateKey" "$WG_CONF")"
    current_interface_public_key="$(get_comment_public_key "$WG_CONF")"
    current_address="$(get_conf_value "Address" "$WG_CONF")"
    current_n="$(extract_n_from_address "$current_address")"
    current_dns="$(get_conf_value "DNS" "$WG_CONF")"
    current_peer_public_key="$(get_conf_value "PublicKey" "$WG_CONF")"
    current_allowed_ips="$(get_conf_value "AllowedIPs" "$WG_CONF")"
    current_endpoint="$(get_conf_value "Endpoint" "$WG_CONF")"
    current_keepalive="$(get_conf_value "PersistentKeepalive" "$WG_CONF")"
  fi

  echo
  echo "==> Konfiguracja WireGuard"

  local final_interface_private_key=""
  local final_interface_public_key=""
  local final_n=""
  local final_dns=""
  local final_peer_public_key=""
  local final_allowed_ips=""
  local final_endpoint=""
  local final_keepalive=""

  if is_valid_base64_wg_key "$current_interface_private_key" && is_valid_base64_wg_key "$current_interface_public_key"; then
    if ask_keep_current "Klucze interfejsu WireGuard" "public=${current_interface_public_key}"; then
      final_interface_private_key="$current_interface_private_key"
      final_interface_public_key="$current_interface_public_key"
    else
      mapfile -t generated < <(generate_keys)
      final_interface_private_key="${generated[0]}"
      final_interface_public_key="${generated[1]}"
    fi
  else
    echo "Brak poprawnych kluczy interfejsu w obecnej konfiguracji — generuję nową parę."
    mapfile -t generated < <(generate_keys)
    final_interface_private_key="${generated[0]}"
    final_interface_public_key="${generated[1]}"
  fi

  final_n="$(ask_or_keep "Numer n dla Address = 1.1.1.n/32" "$current_n" is_valid_n)"
  final_dns="$(ask_or_keep "DNS" "$current_dns" is_nonempty "$DEFAULT_DNS")"
  final_peer_public_key="$(ask_or_keep "Peer PublicKey" "$current_peer_public_key" is_valid_base64_wg_key "$DEFAULT_PEER_PUBLIC_KEY")"
  final_allowed_ips="$(ask_or_keep "AllowedIPs" "$current_allowed_ips" is_nonempty "$DEFAULT_ALLOWED_IPS")"
  final_endpoint="$(ask_or_keep "Endpoint" "$current_endpoint" is_nonempty "$DEFAULT_ENDPOINT")"
  final_keepalive="$(ask_or_keep "PersistentKeepalive" "$current_keepalive" is_nonempty "$DEFAULT_KEEPALIVE")"

  backup_existing_config
  write_config \
    "$final_interface_public_key" \
    "$final_interface_private_key" \
    "$final_n" \
    "$final_dns" \
    "$final_peer_public_key" \
    "$final_allowed_ips" \
    "$final_endpoint" \
    "$final_keepalive"

  enable_and_start_services
  print_summary \
    "$final_interface_public_key" \
    "$final_interface_private_key" \
    "$final_n" \
    "$final_dns" \
    "$final_peer_public_key" \
    "$final_allowed_ips" \
    "$final_endpoint" \
    "$final_keepalive"
}

main "$@"
