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

APP_NAME="CyberGhost VPN"
APP_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/cyberghost-vpn"
BUNDLE_DIR="$APP_DIR/openvpn"
VAULT_FILE="$APP_DIR/credentials.gpg"
DEFAULT_BUNDLE_ARCHIVE="${HOME}/Downloads/ubuntu_wild_lama_openvpn.zip"
DEFAULT_VPN_PROFILE="$BUNDLE_DIR/openvpn.ovpn"

mkdir -p "$APP_DIR"
chmod 700 "$APP_DIR" || true

need() {
  command -v "$1" >/dev/null 2>&1 || {
    echo "$APP_NAME: missing required command: $1" >&2
    exit 1
  }
}

need gpg
need unzip
need python3
need zenity

have_gui=1
if [[ -n "${CG_NONINTERACTIVE:-}" ]]; then
  have_gui=0
fi

prompt_text() {
  local title="$1" text="$2" default="${3:-}"
  if [[ "$have_gui" -eq 1 ]]; then
    zenity --entry --title="$title" --text="$text" ${default:+--entry-text="$default"}
  else
    local var_name="$4"
    printf '%s' "${!var_name:-}"
  fi
}

prompt_password() {
  local title="$1" text="${2:-}" var_name="${3:-}"
  if [[ "$have_gui" -eq 1 ]]; then
    if [[ -n "$text" ]]; then
      zenity --password --title="$title" --text="$text"
    else
      zenity --password --title="$title"
    fi
  else
    printf '%s' "${!var_name:-}"
  fi
}

show_info() {
  local text="$1"
  if [[ "$have_gui" -eq 1 ]]; then
    zenity --info --title="$APP_NAME" --text="$text"
  else
    printf '%s\n' "$text"
  fi
}

show_error() {
  local text="$1"
  if [[ "$have_gui" -eq 1 ]]; then
    zenity --error --title="$APP_NAME" --text="$text"
  else
    printf '%s\n' "$text" >&2
  fi
}

ensure_bundle() {
  mkdir -p "$BUNDLE_DIR"
  chmod 700 "$BUNDLE_DIR" || true
}

import_bundle() {
  local src="${1:-}"
  if [[ -z "$src" ]]; then
    if [[ "$have_gui" -eq 1 ]]; then
      src=$(zenity --file-selection --title="Choisis le ZIP CyberGhost OpenVPN" --file-filter='ZIP files | *.zip')
    else
      src="$DEFAULT_BUNDLE_ARCHIVE"
    fi
  fi

  [[ -f "$src" ]] || { show_error "Archive introuvable: $src"; exit 1; }
  ensure_bundle
  unzip -o -q "$src" -d "$BUNDLE_DIR"
  chmod 600 "$BUNDLE_DIR"/* || true

  for f in ca.crt client.crt client.key openvpn.ovpn; do
    [[ -f "$BUNDLE_DIR/$f" ]] || { show_error "Fichier manquant après import: $f"; exit 1; }
  done

  show_info "Config importée dans:\n$BUNDLE_DIR"
}

save_credentials() {
  local username password vault1 vault2 payload

  if [[ "$have_gui" -eq 1 ]]; then
    username=$(zenity --entry --title="$APP_NAME" --text="CyberGhost username / email")
    password=$(zenity --password --title="$APP_NAME" --text="Mot de passe CyberGhost")
    vault1=$(zenity --password --title="$APP_NAME" --text="Choisis un mot de passe local pour chiffrer le coffre")
    vault2=$(zenity --password --title="$APP_NAME" --text="Confirme le mot de passe local")
  else
    username="${CG_USERNAME:-}"
    password="${CG_PASSWORD:-}"
    vault1="${CG_VAULT_PASS:-}"
    vault2="${CG_VAULT_PASS_CONFIRM:-$vault1}"
  fi

  [[ -n "$username" ]] || { show_error "Username vide"; exit 1; }
  [[ -n "$password" ]] || { show_error "Mot de passe vide"; exit 1; }
  [[ -n "$vault1" ]] || { show_error "Mot de passe local vide"; exit 1; }
  [[ "$vault1" == "$vault2" ]] || { show_error "Les mots de passe locaux ne matchent pas"; exit 1; }

  payload=$(python3 - <<'PY' "$username" "$password"
import json, sys, time
username, password = sys.argv[1], sys.argv[2]
print(json.dumps({
    "username": username,
    "password": password,
    "saved_at": int(time.time()),
}, ensure_ascii=False))
PY
)

  umask 077
  exec 3< <(printf '%s' "$vault1")
  printf '%s' "$payload" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 3 --symmetric --cipher-algo AES256 --output "$VAULT_FILE"
  exec 3<&-
  chmod 600 "$VAULT_FILE"

  show_info "Identifiants chiffrés et stockés ici:\n$VAULT_FILE"
}

read_credentials() {
  local vault_pass json username password
  if [[ "$have_gui" -eq 1 ]]; then
    vault_pass=$(zenity --password --title="$APP_NAME" --text="Mot de passe local du coffre")
  else
    vault_pass="${CG_VAULT_PASS:-}"
  fi
  [[ -n "$vault_pass" ]] || { show_error "Mot de passe du coffre vide"; exit 1; }
  [[ -f "$VAULT_FILE" ]] || { show_error "Aucun coffre trouvé: $VAULT_FILE"; exit 1; }

  exec 3< <(printf '%s' "$vault_pass")
  json=$(gpg --batch --yes --pinentry-mode loopback --passphrase-fd 3 --decrypt "$VAULT_FILE")
  exec 3<&-

  mapfile -t creds < <(printf '%s' "$json" | python3 -c 'import json,sys; obj=json.load(sys.stdin); print(obj["username"]); print(obj["password"])')
  username="${creds[0]:-}"
  password="${creds[1]:-}"

  printf '%s\n%s\n' "$username" "$password"
}

status() {
  echo "APP_DIR=$APP_DIR"
  echo "BUNDLE_DIR=$BUNDLE_DIR"
  echo "VAULT_FILE=$VAULT_FILE"
  [[ -f "$VAULT_FILE" ]] && echo "vault=present" || echo "vault=missing"
  [[ -f "$DEFAULT_VPN_PROFILE" ]] && echo "bundle=present" || echo "bundle=missing"
}

connect_vpn() {
  local username password auth_file runtime_ovpn cleanup
  [[ -f "$DEFAULT_VPN_PROFILE" ]] || { show_error "Bundle OpenVPN manquant. Importe d'abord le ZIP."; exit 1; }

  mapfile -t creds < <(read_credentials)
  username="${creds[0]}"
  password="${creds[1]}"

  auth_file=$(mktemp /dev/shm/cg-auth.XXXXXX)
  runtime_ovpn=$(mktemp /dev/shm/cg-ovpn.XXXXXX)
  cleanup() {
    rm -f "$auth_file" "$runtime_ovpn"
  }
  trap cleanup EXIT

  printf '%s\n%s\n' "$username" "$password" > "$auth_file"
  chmod 600 "$auth_file"

  awk -v auth="$auth_file" '
    /^auth-user-pass([[:space:]]*)$/ { print "auth-user-pass " auth; next }
    { print }
  ' "$DEFAULT_VPN_PROFILE" > "$runtime_ovpn"

  if sudo -n true >/dev/null 2>&1; then
    sudo -n openvpn --config "$runtime_ovpn"
  else
    sudo openvpn --config "$runtime_ovpn"
  fi
}

menu() {
  local choice
  if [[ "$have_gui" -eq 1 ]]; then
    set +e
    choice=$(zenity --list --title="$APP_NAME" --width=500 --height=320 \
      --column="Action" \
      "Importer la config OpenVPN" \
      "Enregistrer les identifiants (chiffrés)" \
      "Se connecter au VPN" \
      "Voir l'état" \
      "Quitter")
    rc=$?
    set -e
    if [[ $rc -ne 0 ]]; then
      exit 0
    fi
  else
    choice=""
  fi

  case "${choice:-}" in
    "Importer la config OpenVPN") import_bundle ;;
    "Enregistrer les identifiants (chiffrés)") save_credentials ;;
    "Se connecter au VPN") connect_vpn ;;
    "Voir l'état") status ;;
    *) status ;;
  esac
}

case "${1:-menu}" in
  import)
    import_bundle "${2:-}"
    ;;
  save)
    save_credentials
    ;;
  connect)
    connect_vpn
    ;;
  status)
    status
    ;;
  menu|*)
    menu
    ;;
esac
