#!/usr/bin/env bash
LAYEROPS_USER=layerops
LAYEROPS_GROUP=$LAYEROPS_USER
LAYEROPS_HOME_DIR=/home/layerops
LAYEROPS_BIN_DIR=${LAYEROPS_HOME_DIR}/bin
LAYEROPS_SCRIPT_PATH=/usr/local/bin/layerops
SUDO_FILE=/etc/sudoers.d/99-layerops-users
LAYEROPS_ROOT_DIR=/data/layerops
LAYEROPS_UPDATE_LXC_CONFIG_SCRIPT_PATH=/usr/local/bin/ly-lxc-config-update.sh

function usage {
  cat << EOF
Usage: $(basename "${BASH_SOURCE[0]}") [init|clean]
EOF
  exit 0
}

function check_envvars() {
  MISSING=""
  for envvar in LAYEROPS_SCRIPT_URL
  do
    [ -z "${!envvar}" ] && MISSING="$envvar $MISSING"
  done

  if [ ! -z "$MISSING" ]
  then
    echo "Missing following environment variables:"
    for var in $MISSING
    do
      echo "  $var"
    done
    exit 1
  fi
}

function _init() {
  # Install required packages
  apt-get update && apt-get install -y jq

  # Get layerops run script
  wget -O $LAYEROPS_SCRIPT_PATH ${LAYEROPS_SCRIPT_URL}
  chmod 755 $LAYEROPS_SCRIPT_PATH

  # Create layerops user
  id $LAYEROPS_USER > /dev/null 2>&1 || adduser -q --gecos "" --disabled-password --home $LAYEROPS_HOME_DIR $LAYEROPS_USER
  mkdir -p $LAYEROPS_HOME_DIR/.ssh
  touch $LAYEROPS_HOME_DIR/.ssh/authorized_keys
  chown -R $LAYEROPS_USER:$LAYEROPS_GROUP $LAYEROPS_HOME_DIR

  # Set sudo access to layerops user
  cat > $SUDO_FILE <<EOF
# Group rules for layerops
%layerops ALL=(ALL) NOPASSWD: ALL
EOF

  mkdir -p $LAYEROPS_ROOT_DIR

  # Add update-lxc-config script
  cat > $LAYEROPS_UPDATE_LXC_CONFIG_SCRIPT_PATH <<EOF
#!/bin/bash
set -e

API_URL=${API_URL}
ORCHESTRATOR_UUID=${ORCHESTRATOR_UUID}
INSTANCE_UUID=${INSTANCE_UUID}
LXC_BRIDGE_DEVICE=lxdbr0

EOF

  cat >> $LAYEROPS_UPDATE_LXC_CONFIG_SCRIPT_PATH <<'EOF'
# Define the layerops-lb container name using INSTANCE_UUID
LB_CONTAINER="layerops-lb-$INSTANCE_UUID"

# Check if the container exists
if ! lxc info $LB_CONTAINER &> /dev/null; then
  echo "Container $LB_CONTAINER does not exist. Exiting."
  exit 0
fi

echo "Found LB container: $LB_CONTAINER"

# Get the list of custom public ports from the API
echo "Fetching custom public ports from API..."
CUSTOM_PORTS=$(curl -s -A "Nimeops-UA" "$API_URL/v1/orchestrators/$ORCHESTRATOR_UUID/customPublicPorts")

# Check if the response is valid JSON
if ! echo "$CUSTOM_PORTS" | jq -e . >/dev/null 2>&1; then
  echo "ERROR: API returned invalid JSON response. Exiting for safety."
  exit 1
fi

if [ -z "$CUSTOM_PORTS" ] || [ "$CUSTOM_PORTS" == "null" ] || [ "$CUSTOM_PORTS" == "[]" ]; then
  echo "No custom ports defined or API call failed."
  CUSTOM_PORTS="[]"
  # Initialize an empty API_PORTS variable to avoid processing errors
  API_PORTS=""
else
  # Get the complete list of ports from the API only if we have valid data
  API_PORTS=$(echo "$CUSTOM_PORTS" | jq -c '.[]')
fi

# Parse the current LXC configuration
echo "Getting current LXC configuration..."
CURRENT_CONFIG=$(lxc config device show $LB_CONTAINER)

# Find all custom port devices
CUSTOM_DEVICES=$(echo "$CURRENT_CONFIG" | grep -o "custom_[a-z0-9_]*" || echo "")

# Get the container IP
LXC_BRIDGE_CIDR=$(lxc network list -f json | jq -r ".[] | select (.name == \"$LXC_BRIDGE_DEVICE\").config.\"ipv4.address\"")
LXC_CONTAINER_IP="$(ipcalc-ng -n $LXC_BRIDGE_CIDR | cut -d '=' -f 2 | cut -d '.' -f 1,2,3).100"

# Get the host IP
HOST_NET_INTERFACE=$(ip route list |grep default  | awk -F 'dev' '{print $2}' | awk '{print $1}')
HOST_IP=$(ip -brief address show $HOST_NET_INTERFACE | awk '{print $3}' | cut -d '/' -f 1)

echo "Container IP: $LXC_CONTAINER_IP"
echo "Public IP: $HOST_IP"

# Process each port from the API
echo "Processing custom ports from API..."
if [ ! -z "$API_PORTS" ]; then
  echo "$API_PORTS" | while read -r port_entry; do
    protocol=$(echo "$port_entry" | jq -r '.protocol')
    port=$(echo "$port_entry" | jq -r '.port')
    device_name="custom_${protocol}_${port}"
    
    # Check if the device already exists
    if echo "$CURRENT_CONFIG" | grep -q "$device_name"; then
      echo "Port $protocol/$port already configured"
    else
      echo "Adding new port configuration: $protocol/$port"
      lxc config device add $LB_CONTAINER "$device_name" proxy\
        connect=$protocol:$LXC_CONTAINER_IP:$port \
        listen=$protocol:$HOST_IP:$port \
        nat=true
    fi
  done
else
  echo "No ports to add - API returned empty list"
fi

# Check and remove obsolete ports
echo "Checking for obsolete port configurations..."
for device in $CUSTOM_DEVICES; do
  # Only process devices that start with custom_
  if [[ "$device" =~ ^custom_([a-z]+)_([0-9]+)$ ]]; then
    protocol="${BASH_REMATCH[1]}"
    port="${BASH_REMATCH[2]}"
    
    # Check if this port is present in the API list
    if [ -z "$API_PORTS" ] || ! echo "$API_PORTS" | jq -e --arg p "$protocol" --arg port "$port" '.protocol == $p and .port == ($port|tonumber)' >/dev/null; then
      echo "Removing obsolete port: $protocol/$port ($device)"
      lxc config device remove $LB_CONTAINER "$device"
    fi
  fi
done

echo "LXC configuration update completed."
EOF

  chmod 755 $LAYEROPS_UPDATE_LXC_CONFIG_SCRIPT_PATH

  cat > /etc/systemd/system/ly-update-lxc-config.service <<EOF
[Unit]
Description=Update LXC config

[Service]
Type=oneshot
ExecStart=$LAYEROPS_UPDATE_LXC_CONFIG_SCRIPT_PATH
StandardOutput=journal

[Install]
WantedBy=multi-user.target
EOF

    cat > /etc/systemd/system/ly-update-lxc-config.timer <<EOF
[Unit]
Description=Update LXC config

[Timer]
OnBootSec=1m
OnUnitActiveSec=1m

[Install]
WantedBy=timers.target
EOF

  systemctl daemon-reload
  systemctl start ly-update-lxc-config.service
  systemctl enable --now ly-update-lxc-config.timer
}


function _clean() {
  # Remove layerops sudo config
  rm -f $SUDO_FILE

  # delete layerops user
  id $LAYEROPS_USER > /dev/null 2>&1 && userdel $LAYEROPS_USER

  # Clean files
  rm -fR $LAYEROPS_HOME_DIR $LAYEROPS_ROOT_DIR
}

if [[ "$#" -eq 0 ]]; then
  usage
fi

case "$1" in
  init)
    check_envvars
    _init
    ;;
  clean)
    _clean;;
  *)
    usage;;
esac
