#!/bin/bash
# =============================================================
# Cavisson Multi-Instance Auto Deploy Script
# -------------------------------------------------------------
# Secure, Reliable, and Automated Multi-Instance Deployment
# Usage:
#   ./cav_auto_deploy.sh <image_id> <host_ip> <instance_count> [hostname]
#
# Example:
#   ./cav_auto_deploy.sh de22039704cc 192.168.1.10 2 cavGen
#
# Key Features:
#   ✅ Dynamic port allocation (no conflicts)
#   ✅ Hostname customization
#   ✅ Input validation & logging
#   ✅ Secure error handling
#   ✅ Compatible with Docker Compose v2+
# =============================================================

set -euo pipefail  # Safe Bash: exit on error, unset var, or failed pipe

# ---------------- CONFIGURATION ----------------
LOG_FILE="deploy_$(date +%Y%m%d_%H%M%S).log"
COMPOSE_FILE="docker-compose.yml"
MIN_DOCKER_VERSION="20.10.0"

# ---------------- LOGGING ----------------
log() {
  echo -e "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"
}

error_exit() {
  log "❌ ERROR: $1"
  exit 1
}

# ---------------- VALIDATION ----------------
if [[ $# -lt 2 ]]; then
  echo "Usage: $0 <image_id> <host_ip> <instance_count> [hostname]"
  exit 1
fi

IMAGE_ID=$1
HOST_ADDR=$2
COUNT=${3:-1}
HOST_NAME=${4:-}

# Validate IP format
if ! [[ $HOST_ADDR =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  error_exit "Invalid IP address format: $HOST_ADDR"
fi

# Validate Docker installation
if ! command -v docker &>/dev/null; then
  error_exit "Docker not found. Please install Docker first."
fi

# Validate Docker Compose availability
if ! docker compose version &>/dev/null; then
  error_exit "Docker Compose plugin not found. Install with: apt install docker-compose-plugin"
fi

# Check Docker version
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}')
if [[ "$(printf '%s\n' "$MIN_DOCKER_VERSION" "$DOCKER_VERSION" | sort -V | head -n1)" != "$MIN_DOCKER_VERSION" ]]; then
  log "⚠️ Docker version is below recommended minimum ($MIN_DOCKER_VERSION)"
fi

# ---------------- FUNCTION: PORT CHECKER ----------------
is_port_free() {
  local ip=$1
  local port=$2
  ss -ltnH | awk '{print $4}' | grep -q "${ip}:${port}" && return 1 || return 0
}

# ---------------- COMPOSE GENERATION ----------------
log "🧩 Generating Docker Compose file for $COUNT instance(s)..."
cat > "$COMPOSE_FILE" <<EOF
version: "3.9"
services:
EOF

# Common container ports
declare -a CONTAINER_PORTS=(80 443 7891 7901 7908 27001 4443 4444)
RANGE_CONTAINER_PORT_START=7914
RANGE_CONTAINER_PORT_END=7917
BASE_HOST_PORT=8000

for i in $(seq 1 "$COUNT"); do
  SERVICE_NAME="cavnode_$i"
  CONTAINER_HOSTNAME="${HOST_NAME:-$SERVICE_NAME}"

  log "  ➜ Configuring service: $SERVICE_NAME"

  HOST_PORTS=()
  for cport in "${CONTAINER_PORTS[@]}"; do
    while true; do
      ((BASE_HOST_PORT++))
      if is_port_free "$HOST_ADDR" "$BASE_HOST_PORT"; then
        HOST_PORTS+=("${HOST_ADDR}:${BASE_HOST_PORT}:${cport}")
        break
      fi
    done
  done

  # Port range mapping 7914–7917
  while true; do
    RANGE_START=$((BASE_HOST_PORT + 1))
    RANGE_END=$((RANGE_START + RANGE_CONTAINER_PORT_END - RANGE_CONTAINER_PORT_START))
    conflict=false

    for hport in $(seq "$RANGE_START" "$RANGE_END"); do
      if ! is_port_free "$HOST_ADDR" "$hport"; then
        conflict=true
        BASE_HOST_PORT=$((hport))
        break
      fi
    done

    [[ "$conflict" == false ]] && break
  done

  RANGE_MAPPING="${HOST_ADDR}:${RANGE_START}-${RANGE_END}:${RANGE_CONTAINER_PORT_START}-${RANGE_CONTAINER_PORT_END}"
  BASE_HOST_PORT=$RANGE_END

  cat >> "$COMPOSE_FILE" <<EOF
  $SERVICE_NAME:
    image: ${IMAGE_ID}
    container_name: ${SERVICE_NAME}
    hostname: ${CONTAINER_HOSTNAME}
    privileged: true
    shm_size: 1g
    command: ["/usr/sbin/init"]
    ports:
EOF

  for mapping in "${HOST_PORTS[@]}"; do
    echo "      - \"$mapping\"" >> "$COMPOSE_FILE"
  done
  echo "      - \"$RANGE_MAPPING\"" >> "$COMPOSE_FILE"

  cat >> "$COMPOSE_FILE" <<EOF
    restart: unless-stopped
EOF

done

log "✅ Docker Compose file created: $COMPOSE_FILE"

# ---------------- DEPLOYMENT ----------------
log "🚀 Starting all container(s)..."
docker compose up -d | tee -a "$LOG_FILE"

log "------------------------------------------------------------"
log "✅ Containers deployed successfully on host: $HOST_ADDR"
log "📜 Logs: $LOG_FILE"
log "🔍 To check containers: docker ps"
log "🧹 To stop: docker compose down"
log "🧾 Compose file: $COMPOSE_FILE"
log "------------------------------------------------------------"

