#!/bin/bash
set -e

source ${SCRIPT_DIR}/logging
source ${SCRIPT_DIR}/kasm_api

kasm_id=$1
container_name=$2
container_ns_id=$3
container_pid=$4
script_data_encoded=$5
notification_pipe=$6
container_setup_progress=0

openvpn_log_file=$LOG_DIR/$container_ns_id/openvpn.log
openvpn_status_file=$LOG_DIR/$container_ns_id/openvpn-status.log

notify_failure() {
  set +e

  error "Command failed: $BASH_COMMAND"

  # update the workspace status
  set_container_status "starting" "$container_setup_progress" "Connection to egress gateway failed. Contact an administrator if the issue persists."
  
  # log the error
  if [ -f "$openvpn_log_file" ]; then
    openvpn_log=$(cat "$openvpn_log_file")
    error "OpenVPN log: $openvpn_log"
  fi

  # show the error notification
  if [ "$show_vpn_status" == "1" ]; then
    show_container_notification "error" "VPN Connection Failed" "VPN Configuration Failed. An error has occurred starting the VPN. Refer to the logs for more information."
  fi

  # mark the container as failed with a delay to ensure the workspace error is visible to the user since
  # the container relies on REST API calls to update the status
  sleep 10
  nsenter --mount=/proc/$container_pid/ns/mnt --net=/proc/$container_pid/ns/net -- sh -c "echo 'error' > /dockerstartup/.egress_status"
  exit 0
}

if [ -z "$container_name" ] || [ -z "$container_ns_id" ] || [ -z "$container_pid" ] || [ -z "$script_data_encoded" ]; then
  error "Missing parameters. Got $@. Exiting."
  exit 1
fi

trap notify_failure ERR

container_setup_progress=5
set_container_status "starting" "$container_setup_progress" "Connecting to egress gateway..."

# extract script data
script_data=$(echo $script_data_encoded | base64 -d)
egress_provider=$(echo $script_data | jq -r '.egress_provider')
egress_gateway=$(echo $script_data | jq -r '.egress_gateway')
egress_country=$(echo $script_data | jq -r '.egress_country')
egress_city=$(echo $script_data | jq -r '.egress_city')
show_vpn_status=$(echo $script_data | jq -r '.show_vpn_status')
show_ip_status=$(echo $script_data | jq -r '.show_ip_status')

if [ "$show_vpn_status" == "1" ]; then
  show_container_notification "offline" "Connecting to VPN" "Please wait while the VPN connection is being established..."
fi


# prepare the credentials
config_file=/var/run/kasm-sidecar/$container_ns_id/openvpn.conf
info "Preparing config file: $config_file"
echo $script_data | jq -r '.config' > $config_file

username=$(echo $script_data | jq -r '.username')
password=$(echo $script_data | jq -r '.password')

if [ -n "$username" ] && [ -n "$password" ]; then
  credentials_file=/var/run/kasm-sidecar/$container_ns_id/openvpn_auth
  info "Preparing credentials file: $credentials_file"
  echo -e "$username\n$password" > $credentials_file
  sed -i "/auth-user-pass/c\auth-user-pass $credentials_file" $config_file
fi

# create a tun device
if [ ! -c /dev/net/tun ]; then
  mkdir -p /dev/net
  mknod /dev/net/tun c 10 200
fi

# start the client
touch $openvpn_log_file
touch $openvpn_status_file
info "Starting OpenVPN (full log: $openvpn_log_file, status log: $openvpn_status_file)"
nsenter --net=/proc/$container_pid/ns/net bash -c "openvpn --pull-filter ignore route-ipv6 --pull-filter ignore ifconfig-ipv6 --config $config_file --verb 3 --log \"$openvpn_log_file\" --status \"$openvpn_status_file\"" &
echo $! >> /var/run/kasm-sidecar/$container_ns_id/pids

# wait until the connection is established
info "Waiting for connection to be established"

SECONDS=0
MAX_WAIT=30
while true; do
  if grep -q "Initialization Sequence Completed" $openvpn_log_file; then
    break
  fi
  
  if [ $SECONDS -ge $MAX_WAIT ]; then
    error "Connection timeout after $MAX_WAIT seconds"
    notify_failure
    exit 0
  fi

  sleep 1
done

# use the DNS pushed down from the VPN server
dns_servers=$(grep -oP 'dhcp-option\s*DNS\s*[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' $openvpn_log_file | cut -d' ' -f3)

if [ -n "$dns_servers" ]; then
  info "Setting pushed down DNS servers for container: $dns_servers"

  for dns_server in $dns_servers; do
    nsenter --net=/proc/$container_pid/ns/net --mount=/proc/$container_pid/ns/mnt sh -c "echo nameserver $dns_server >> /etc/resolv.conf"
  done
fi

openvpn_log=$(cat $openvpn_log_file)
info "VPN connected. OpenVPN log:\n$openvpn_log"

# mark the container as ready
container_setup_progress=10
set_container_status "starting" "$container_setup_progress" "Connection to egress gateway established."
nsenter --mount=/proc/$container_pid/ns/mnt --net=/proc/$container_pid/ns/net -- sh -c "echo 'ready' > /dockerstartup/.egress_status"

# show the VPN status
if [ "$show_vpn_status" == "1" ]; then
  show_container_notification "info" "VPN Connected!" "Provider: $egress_provider\nCountry: $egress_country\nCity: $egress_city\nGateway: $egress_gateway"
fi

# show the connection details
if [ "$show_ip_status" == "1" ]; then
  attempt=1
  attempt_delay=5
  max_attempts=10

  while [ $attempt -le $max_attempts ]
  do  
    ip_status=$(nsenter --mount=/proc/$container_pid/ns/mnt --net=/proc/$container_pid/ns/net -- bash -c "curl -q --max-time 5 -s https://ipleak.net/json/ || true")
    
    if [ -n "$ip_status" ]; then
      info "Successfully retrieved public IP information"
      break
    fi
    
    error "Failed to retrieve public IP information (${attempt} attempts)"
    attempt=$((attempt + 1))
    sleep $attempt_delay
  done

  if [ -z "$ip_status" ]; then
    show_container_notification "error" "Public IP Status" "Failed to retrieve public IP information. The connection may not be fully established."
  else  
    show_container_notification "info" "Public IP Information" "$ip_status"
  fi
fi