#!/bin/bash
set -euo pipefail

# --- Defaults ---
LOG_DIR="./logs"
mkdir -p "$LOG_DIR"
UDM_HOST="100.100.10.1"
UDM_SSH_USER="root"
LEASE_FILE="/mnt/.rwfs/data/data/udapi-config/dnsmasq.lease"
SUBNET="192.168.10.0/24"
RESERVATION_FILE="reserved_ips.conf"

# --- Technitium API credentials ---
TECHNITIUM_API_CONF="/root/technitium_api.conf"
if [[ ! -f "$TECHNITIUM_API_CONF" ]]; then
  echo "❌ Missing Technitium credentials file: $TECHNITIUM_API_CONF" >&2
  exit 1
fi
TECHNITIUM_API_HOST="$(grep -E '^API_HOST=' "$TECHNITIUM_API_CONF" | cut -d'=' -f2-)"
TECHNITIUM_API_KEY="$(grep -E '^API_KEY=' "$TECHNITIUM_API_CONF" | cut -d'=' -f2-)"
DNS_ZONE="inxs.be"

# --- Variables ---
CT_ID=""
CT_IP=""
CT_MAC=""
CT_HOSTNAME=""
CT_DESCRIPTION=""
CT_ROOT_PASS=""
CT_STORAGE="tier1"
CT_NET_ID="10"
CT_BRIDGE="vmbr0"
CT_NAME_SERVER="192.168.10.4"
ACTION=""
MAC_ADDRESS=""
OUTPUT_MODE="full"
DRY_RUN=false
GENERATE_REPORT=false
NO_DNS=false
FORCE_DELETE=false
EXPORT_INVENTORY=false
INVENTORY_FORMAT="json"

# --- Usage/help function ---
usage() {
  cat <<EOF
Usage: $0 --action ACTION [options]

Actions:
  create-lxc       Create a new LXC container
  delete-lxc       Delete an existing LXC container
  list-inventory   List all containers in inventory

Options:
  --ct-id          Container ID (e.g., 10055)
  --ct-hostname    Container hostname
  --ct-desc        Container description
  --ct-net-id      Network ID (default: 10)
  --force          Force delete without confirmation
  --no-dns         Skip DNS registration
  --dry-run        Do not actually create/delete containers
  --export         Export inventory file
  --format         Inventory export format (json or yaml)
  --help           Show this help message

Examples:
  $0 --action create-lxc --ct-hostname mycontainer
  $0 --action delete-lxc --ct-id 10055
  $0 --action list-inventory

EOF
}

# --- Helper Functions ---
generate_mac() {
  printf '02:00:00:%02x:%02x:%02x\n' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
}

generate_root_password() {
  tr -dc 'A-Za-z0-9!@#$%^&*()-_=+' < /dev/urandom | head -c 16
}

add_dns_entry() {
  local hostname="$1"
  local ip="$2"
  local cname_prefix="${hostname#*-}"

  curl -s -X POST "$TECHNITIUM_API_HOST/api/record/add" \
       -H "X-API-Key: $TECHNITIUM_API_KEY" \
       -H "Content-Type: application/json" \
       -d '{"ZoneName":"'"$DNS_ZONE"'","Name":"'"$hostname"'","Type":"A","Address":"'"$ip"'","TTL":3600}' > /dev/null

  curl -s -X POST "$TECHNITIUM_API_HOST/api/record/add" \
       -H "X-API-Key: $TECHNITIUM_API_KEY" \
       -H "Content-Type: application/json" \
       -d '{"ZoneName":"'"$DNS_ZONE"'","Name":"'"$cname_prefix"'","Type":"CNAME","Alias":"'"$hostname.$DNS_ZONE"'","TTL":3600}' > /dev/null

  echo "DNS A record for $hostname and CNAME for $cname_prefix added."
}

append_inventory() {
  echo "- id: $CT_ID" >> inventory.yaml
  echo "  hostname: $CT_HOSTNAME" >> inventory.yaml
  echo "  ip: 192.168.$CT_NET_ID.$CT_IP" >> inventory.yaml
  echo "  mac: $MAC_ADDRESS" >> inventory.yaml
  echo "  created: $(date '+%Y-%m-%dT%H:%M:%S')" >> inventory.yaml
}

find_free_ip() {
  [[ -z "$MAC_ADDRESS" ]] && MAC_ADDRESS=$(generate_mac)
  local leases=$(ssh "$UDM_SSH_USER@$UDM_HOST" "cat $LEASE_FILE" || true)
  local used_ips=$(echo "$leases" | awk '{ if ($1 > systime()) print $3 }')
  local reserved_ips=""
  [[ -f "$RESERVATION_FILE" ]] && reserved_ips=$(<"$RESERVATION_FILE")

  for i in $(seq 21 253); do
    local ip="192.168.$CT_NET_ID.$i"
    [[ "$used_ips" =~ $ip ]] && continue
    [[ "$reserved_ips" =~ $ip ]] && continue
    ping -c1 -W1 "$ip" &>/dev/null && continue

    CT_IP="$i"
    MAC_ADDRESS="$MAC_ADDRESS"
    if [[ -z "$CT_ID" ]]; then
      if (( i < 100 )); then CT_ID="100$i"; else CT_ID="10$i"; fi
    fi
    echo "✅ Free IP found: $ip"
    return 0
  done
  echo "❌ No free IP found in subnet 192.168.$CT_NET_ID.0/24"
  return 1
}

create_lxc_container() {
  [[ -z "$MAC_ADDRESS" ]] && MAC_ADDRESS=$(generate_mac)
  [[ -z "$CT_IP" ]] && find_free_ip
  if [[ -z "$CT_ROOT_PASS" ]]; then
    CT_ROOT_PASS=$(generate_root_password)
    echo "$CT_ROOT_PASS" > "$LOG_DIR/ct-${CT_ID}-rootpass.txt"
    echo "🔐 Password saved to $LOG_DIR/ct-${CT_ID}-rootpass.txt"
  fi

  echo "🚀 Creating container VMID=$CT_ID, IP=192.168.$CT_NET_ID.$CT_IP"

  if [[ "$DRY_RUN" == true ]]; then
    echo "🧪 Dry-run mode enabled. Skipping container creation."
  else
    pct create "$CT_ID" \
      /mnt/pve/nas-software/template/cache/debian-12-standard_12.7-1_amd64.tar.zst \
      --hostname "$CT_HOSTNAME" \
      --description "$CT_DESCRIPTION" \
      --cores 2 \
      --memory 4096 \
      --swap 1024 \
      --net0 name=eth0,bridge="$CT_BRIDGE",firewall=0,gw=192.168."$CT_NET_ID".1,ip=192.168."$CT_NET_ID"."$CT_IP"/24,hwaddr="$MAC_ADDRESS",type=veth \
      --rootfs 8 \
      --storage "$CT_STORAGE" \
      --unprivileged 1 \
      --ostype debian \
      --password="$CT_ROOT_PASS" \
      --onboot 1 \
      --nameserver "$CT_NAME_SERVER" \
      --searchdomain "$DNS_ZONE" \
      --features fuse=1,keyctl=1,nesting=1 \
      --mp5 /lxc_shares/scripts,mp=/opt/scripts_remote \
      --mp6 /lxc_shares/backup,mp=/media/backup \
      --start 0

    echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >> /etc/pve/lxc/${CT_ID}.conf
    echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file" >> /etc/pve/lxc/${CT_ID}.conf
  fi

  [[ "$NO_DNS" != true ]] && append_inventory
  [[ "$NO_DNS" != true ]] && add_dns_entry "$CT_HOSTNAME" "192.168.$CT_NET_ID.$CT_IP"

  echo "📦 Hostname : $CT_HOSTNAME"
  echo "🌐 IP       : 192.168.$CT_NET_ID.$CT_IP"
  echo "🔗 MAC      : $MAC_ADDRESS"
  [[ "$NO_DNS" == true ]] && echo "📎 Note     : DNS registration skipped"
  [[ "$DRY_RUN" == true ]] && echo "🧪 Note     : Dry-run mode enabled — nothing was actually provisioned."
  [[ "$NO_DNS" != true ]] && echo "📡 DNS      : $CT_HOSTNAME → 192.168.$CT_NET_ID.$CT_IP (A), ${CT_HOSTNAME#*-}.$DNS_ZONE (CNAME)"
}

secure_delete_password() {
  local passfile="$LOG_DIR/ct-${CT_ID}-rootpass.txt"
  if [[ -f "$passfile" ]]; then
    shred -u "$passfile"
    echo "🔐 Password file securely deleted: $passfile"
  fi
}

delete_lxc_container() {
  [[ -z "$CT_ID" ]] && read -rp "Container ID: " CT_ID
  [[ -z "$CT_NET_ID" ]] && read -rp "Network ID: " CT_NET_ID
  local VM_ID="$CT_ID"

  local status
  status=$(pct status "$VM_ID" 2>/dev/null | awk '{print $2}')
  if [[ "$status" == "running" && "$FORCE_DELETE" != true ]]; then
    read -rp "⚠️ Container $VM_ID is running. Stop it? [y/N]: " confirm
    [[ "$confirm" =~ ^[Yy]$ ]] && pct stop "$VM_ID" || { echo "Aborted."; return; }
  fi

  pct destroy "$VM_ID"
  secure_delete_password

  if [[ -f "$RESERVATION_FILE" ]]; then
    grep -v "192.168.$CT_NET_ID.$CT_IP" "$RESERVATION_FILE" > "$RESERVATION_FILE.tmp"
    mv "$RESERVATION_FILE.tmp" "$RESERVATION_FILE"
    echo "✅ IP 192.168.$CT_NET_ID.$CT_IP released."
  fi

  echo -e "\n🧹 Summary:"
  echo "🔹 VMID deleted : $VM_ID"
  echo "🔹 IP released  : 192.168.$CT_NET_ID.$CT_IP"

  {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deleted container $VM_ID"
    echo "Released IP: 192.168.$CT_NET_ID.$CT_IP"
  } >> "$LOG_DIR/deletion.log"
}

filter_inventory() {
  local key="$1"
  local value="$2"
  awk -v k="$key" -v v="$value" '
    BEGIN {RS="- id:"; FS="\n"}
    $0 ~ k ":" && $0 ~ v {
      print "- id:" $0
    }' inventory.yaml
}

export_inventory() {
  [[ ! -f inventory.yaml ]] && echo "inventory.yaml not found." && return
  case "$INVENTORY_FORMAT" in
    json)
      yq -o=json . inventory.yaml > inventory.json && echo "📤 Exported as inventory.json" ;;
    yaml)
      cp inventory.yaml inventory-export.yaml && echo "📤 Exported as inventory-export.yaml" ;;
    *)
      echo "❌ Unknown format: $INVENTORY_FORMAT" ;;
  esac
}

list_inventory() {
  [[ ! -f inventory.yaml ]] && echo "No inventory.yaml found." && return
  echo "📋 Inventory"
  awk '/^- id:/ {id=$2} /hostname:/ {h=$2} /ip:/ {ip=$2} /mac:/ {mac=$2} /created:/ {c=$2} \
       {if ($1=="created:") printf "🆔 %-6s 🖥 %-15s 🌐 %-15s 🧬 %s 📅 %s\n", id, h, ip, mac, c}' inventory.yaml
}

# --- Usage/help function ---
usage() {
  cat <<EOF
Usage: $0 --action ACTION [options]

Actions:
  create-lxc       Create a new LXC container
  delete-lxc       Delete an existing LXC container
  list-inventory   List all containers in inventory

Options:
  --ct-id          Container ID (e.g., 10055)
  --ct-hostname    Container hostname
  --ct-desc        Container description
  --ct-net-id      Network ID (default: 10)
  --force          Force delete without confirmation
  --no-dns         Skip DNS registration
  --dry-run        Do not actually create/delete containers
  --export         Export inventory file
  --format         Inventory export format (json or yaml)
  --help           Show this help message

Examples:
  $0 --action create-lxc --ct-hostname mycontainer
  $0 --action delete-lxc --ct-id 10055
  $0 --action list-inventory

EOF
}

# --- Argument Parser ---
if [[ $# -eq 0 ]]; then
  usage
  exit 1
fi

FORCE_DELETE=false
EXPORT_INVENTORY=false
INVENTORY_FORMAT="json"
ACTION=""
for ((i=1; i<=$#; i++)); do
  arg=${!i}
  case "$arg" in
    --ct-id) i=$((i+1)); CT_ID=${!i} ;;
    --ct-hostname) i=$((i+1)); CT_HOSTNAME=${!i} ;;
    --ct-desc) i=$((i+1)); CT_DESCRIPTION=${!i} ;;
    --ct-net-id) i=$((i+1)); CT_NET_ID=${!i} ;;
    --action) i=$((i+1)); ACTION=${!i} ;;
    --force) FORCE_DELETE=true ;;
    --export) EXPORT_INVENTORY=true ;;
    --format) i=$((i+1)); INVENTORY_FORMAT=${!i} ;;
    --no-dns) NO_DNS=true ;;
    --dry-run) DRY_RUN=true ;;
    --help) usage; exit 0 ;;
    *) echo "Unknown option: $arg"; usage; exit 1 ;;
  esac
done

if [[ -z "$ACTION" ]]; then
  usage
  exit 1
fi

# --- Execute export if requested ---
[[ "$EXPORT_INVENTORY" == true ]] && export_inventory

# --- Dispatcher ---
case "$ACTION" in
  create-lxc) create_lxc_container ;;
  delete-lxc) delete_lxc_container ;;
  list-inventory) list_inventory ;;
  *) echo "Unknown action: $ACTION"; usage; exit 1 ;;
esac
