#!/usr/bin/env bash
# =============================================================================
# 🐳 Build Production Docker Images for Plesk Deployment
# =============================================================================
# Features:
#   - Parallel builds with BuildKit
#   - Multi-platform support (amd64/arm64)
#   - Compressed tarballs with SHA256 checksums
#   - Buildx builder for cross-platform builds
#   - Intelligent layer caching
# =============================================================================

set -Eeuo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# =============================================================================
# Configuration (override via env vars or flags)
# =============================================================================
IMAGE_FRONTEND="${IMAGE_FRONTEND_NAME:-portal-frontend}"
IMAGE_BACKEND="${IMAGE_BACKEND_NAME:-portal-backend}"
IMAGE_TAG="${IMAGE_TAG:-}"
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT}"
PUSH_REMOTE="${PUSH_REMOTE:-}"
PARALLEL="${PARALLEL_BUILD:-1}"
COMPRESS="${COMPRESS:-1}"
USE_LATEST_FILENAME="${USE_LATEST_FILENAME:-1}"
SAVE_TARS=1
PLATFORM="${PLATFORM:-linux/amd64}"
PROGRESS="${PROGRESS:-auto}"
USE_BUILDX="${USE_BUILDX:-0}"
CLEAN_CACHE="${CLEAN_CACHE:-0}"
DRY_RUN="${DRY_RUN:-0}"

# =============================================================================
# Colors and Logging
# =============================================================================
if [[ -t 1 ]]; then
  G='\033[0;32m' Y='\033[1;33m' R='\033[0;31m' B='\033[0;34m' C='\033[0;36m' M='\033[0;35m' N='\033[0m'
else
  G='' Y='' R='' B='' C='' M='' N=''
fi

log()  { echo -e "${G}[BUILD]${N} $*"; }
info() { echo -e "${B}[INFO]${N} $*"; }
warn() { echo -e "${Y}[WARN]${N} $*"; }
fail() { echo -e "${R}[ERROR]${N} $*"; exit 1; }
step() { echo -e "${C}[STEP]${N} $*"; }
debug() { [[ "${DEBUG:-0}" -eq 1 ]] && echo -e "${M}[DEBUG]${N} $*" || true; }

# =============================================================================
# Usage
# =============================================================================
usage() {
  cat <<EOF
Build Docker images and save tarballs for Plesk deployment.

Usage: $0 [OPTIONS]

Options:
  --tag <tag>       Version tag for image (default: YYYYMMDD-gitsha)
  --output <dir>    Output directory (default: project root)
  --push <registry> Push to registry after build
  --no-save         Build only, no tar files
  --no-compress     Save as .tar instead of .tar.gz
  --versioned       Name files with version tag (default: .latest.tar.gz)
  --sequential      Build one at a time (default: parallel)
  --platform <p>    Target platform (default: linux/amd64)
  --buildx          Use Docker Buildx for builds (better caching)
  --clean-cache     Clean Docker build cache before building
  --dry-run         Show what would be done without executing
  --ci              CI mode: plain progress output, no colors
  --debug           Enable debug output
  -h, --help        Show this help

Platform Options:
  linux/amd64       Standard x86_64 servers (default)
  linux/arm64       ARM64 servers (Apple Silicon, Graviton)

Examples:
  $0                              # Output: portal-frontend.latest.tar.gz
  $0 --versioned                  # Output: portal-frontend.20251201-abc123.tar.gz
  $0 --tag v1.2.3 --versioned     # Output: portal-frontend.v1.2.3.tar.gz
  $0 --sequential --no-save       # Debug build
  $0 --platform linux/arm64       # Build for ARM64
  $0 --buildx --clean-cache       # Fresh buildx build
  $0 --ci                         # CI-friendly output

Environment Variables:
  IMAGE_FRONTEND_NAME   Override frontend image name (default: portal-frontend)
  IMAGE_BACKEND_NAME    Override backend image name (default: portal-backend)
  PNPM_VERSION          Override PNPM version (default: from package.json)
  NEXT_PUBLIC_API_BASE  Next.js API base path (default: /api)
  DEBUG=1               Enable debug output
EOF
  exit 0
}

# =============================================================================
# Argument Parsing
# =============================================================================
while [[ $# -gt 0 ]]; do
  case "$1" in
    --tag)        IMAGE_TAG="${2:-}"; shift 2 ;;
    --output)     OUTPUT_DIR="${2:-}"; shift 2 ;;
    --push)       PUSH_REMOTE="${2:-}"; shift 2 ;;
    --no-save)    SAVE_TARS=0; shift ;;
    --no-compress) COMPRESS=0; shift ;;
    --versioned)  USE_LATEST_FILENAME=0; shift ;;
    --sequential) PARALLEL=0; shift ;;
    --platform)   PLATFORM="${2:-linux/amd64}"; shift 2 ;;
    --buildx)     USE_BUILDX=1; shift ;;
    --clean-cache) CLEAN_CACHE=1; shift ;;
    --dry-run)    DRY_RUN=1; shift ;;
    --ci)         PROGRESS="plain"; G=''; Y=''; R=''; B=''; C=''; M=''; N=''; shift ;;
    --debug)      DEBUG=1; shift ;;
    -h|--help)    usage ;;
    *)            fail "Unknown option: $1" ;;
  esac
done

# =============================================================================
# Validation
# =============================================================================
command -v docker >/dev/null 2>&1 || fail "Docker is required but not installed"

cd "$PROJECT_ROOT"

[[ -f apps/portal/Dockerfile ]] || fail "Missing apps/portal/Dockerfile"
[[ -f apps/bff/Dockerfile ]] || fail "Missing apps/bff/Dockerfile"
[[ -f package.json ]] || fail "Missing package.json"

# Verify Docker daemon is running
docker info >/dev/null 2>&1 || fail "Docker daemon is not running"

# =============================================================================
# Setup
# =============================================================================

# Auto-generate tag if not provided
if [[ -z "$IMAGE_TAG" ]]; then
  GIT_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo 'local')
  IMAGE_TAG="$(date +%Y%m%d)-${GIT_SHA}"
fi

# Enable BuildKit
export DOCKER_BUILDKIT=1

# Extract PNPM version from package.json (packageManager field)
PNPM_VERSION_FROM_PKG=$(grep -oP '"packageManager":\s*"pnpm@\K[0-9.]+' package.json 2>/dev/null || echo "")
PNPM_VERSION="${PNPM_VERSION:-${PNPM_VERSION_FROM_PKG:-10.25.0}}"

# Build args
NEXT_PUBLIC_API_BASE="${NEXT_PUBLIC_API_BASE:-/api}"
NEXT_PUBLIC_APP_NAME="${NEXT_PUBLIC_APP_NAME:-Customer Portal}"
GIT_SOURCE="$(git config --get remote.origin.url 2>/dev/null || echo unknown)"
GIT_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

# Log directory
LOG_DIR="${OUTPUT_DIR}/.build-logs"
mkdir -p "$LOG_DIR"

# =============================================================================
# Buildx Setup
# =============================================================================
BUILDER_NAME="portal-builder"

setup_buildx() {
  if [[ "$USE_BUILDX" -eq 1 ]]; then
    step "Setting up Docker Buildx..."
    
    # Check if buildx is available
    if ! docker buildx version >/dev/null 2>&1; then
      warn "Docker Buildx not available, falling back to standard build"
      USE_BUILDX=0
      return
    fi
    
    # Create or use existing builder
    if ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
      docker buildx create --name "$BUILDER_NAME" --driver docker-container --bootstrap
      info "Created buildx builder: $BUILDER_NAME"
    else
      docker buildx use "$BUILDER_NAME"
      debug "Using existing buildx builder: $BUILDER_NAME"
    fi
  fi
}

# =============================================================================
# Clean Cache
# =============================================================================
clean_cache() {
  if [[ "$CLEAN_CACHE" -eq 1 ]]; then
    step "Cleaning Docker build cache..."
    docker builder prune -f --filter type=exec.cachemount 2>/dev/null || true
    docker builder prune -f --filter unused-for=24h 2>/dev/null || true
    log "✅ Build cache cleaned"
  fi
}

# =============================================================================
# Build Functions
# =============================================================================
# Build functions moved to build_frontend and build_backend for better argument handling

build_frontend() {
  local logfile="$LOG_DIR/frontend.log"
  
  step "Building frontend image..."
  
  if [[ "$DRY_RUN" -eq 1 ]]; then
    info "[DRY-RUN] Would build frontend"
    return 0
  fi
  
  local exit_code=0
  
  docker build \
    --load \
    -f apps/portal/Dockerfile \
    --platform "${PLATFORM}" \
    --progress "${PROGRESS}" \
    --build-arg "PNPM_VERSION=${PNPM_VERSION}" \
    --build-arg "NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE}" \
    --build-arg "NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}" \
    --build-arg "NEXT_PUBLIC_APP_VERSION=${IMAGE_TAG}" \
    -t "${IMAGE_FRONTEND}:latest" \
    -t "${IMAGE_FRONTEND}:${IMAGE_TAG}" \
    --label "org.opencontainers.image.version=${IMAGE_TAG}" \
    --label "org.opencontainers.image.source=${GIT_SOURCE}" \
    --label "org.opencontainers.image.revision=${GIT_COMMIT}" \
    --label "org.opencontainers.image.created=${BUILD_DATE}" \
    . > "$logfile" 2>&1 || exit_code=$?
  
  if [[ $exit_code -eq 0 ]]; then
    local size
    size=$(docker image inspect "${IMAGE_FRONTEND}:latest" --format='{{.Size}}' 2>/dev/null | numfmt --to=iec 2>/dev/null || echo "?")
    log "✅ Frontend built (${size})"
    return 0
  else
    warn "❌ Frontend FAILED - see $logfile"
    tail -50 "$logfile" || true
    return 1
  fi
}

build_backend() {
  local logfile="$LOG_DIR/backend.log"
  
  step "Building backend image..."
  
  if [[ "$DRY_RUN" -eq 1 ]]; then
    info "[DRY-RUN] Would build backend"
    return 0
  fi
  
  local exit_code=0
  
  docker build \
    --load \
    -f apps/bff/Dockerfile \
    --platform "${PLATFORM}" \
    --progress "${PROGRESS}" \
    --build-arg "PNPM_VERSION=${PNPM_VERSION}" \
    -t "${IMAGE_BACKEND}:latest" \
    -t "${IMAGE_BACKEND}:${IMAGE_TAG}" \
    --label "org.opencontainers.image.version=${IMAGE_TAG}" \
    --label "org.opencontainers.image.source=${GIT_SOURCE}" \
    --label "org.opencontainers.image.revision=${GIT_COMMIT}" \
    --label "org.opencontainers.image.created=${BUILD_DATE}" \
    . > "$logfile" 2>&1 || exit_code=$?
  
  if [[ $exit_code -eq 0 ]]; then
    local size
    size=$(docker image inspect "${IMAGE_BACKEND}:latest" --format='{{.Size}}' 2>/dev/null | numfmt --to=iec 2>/dev/null || echo "?")
    log "✅ Backend built (${size})"
    return 0
  else
    warn "❌ Backend FAILED - see $logfile"
    tail -50 "$logfile" || true
    return 1
  fi
}

# =============================================================================
# Save Tarballs
# =============================================================================
save_tarballs() {
  if [[ "$SAVE_TARS" -eq 0 ]] || [[ "$DRY_RUN" -eq 1 ]]; then
    return 0
  fi
  
  mkdir -p "$OUTPUT_DIR"
  local save_start
  save_start=$(date +%s)
  
  # Determine filename suffix
  local file_tag
  if [[ "$USE_LATEST_FILENAME" -eq 1 ]]; then
    file_tag="latest"
  else
    file_tag="$IMAGE_TAG"
  fi
  
  local fe_tar be_tar
  
  if [[ "$COMPRESS" -eq 1 ]]; then
    # Pick fastest available compressor: pigz (parallel) > gzip
    local compressor comp_name
    if command -v pigz >/dev/null 2>&1; then
      compressor="pigz -p $(nproc)"
      comp_name="pigz"
    else
      compressor="gzip -1"
      comp_name="gzip"
    fi
    
    fe_tar="$OUTPUT_DIR/${IMAGE_FRONTEND}.${file_tag}.tar.gz"
    be_tar="$OUTPUT_DIR/${IMAGE_BACKEND}.${file_tag}.tar.gz"
    log "💾 Compressing with $comp_name..."
    
    (docker save "${IMAGE_FRONTEND}:latest" | $compressor > "$fe_tar") &
    (docker save "${IMAGE_BACKEND}:latest" | $compressor > "$be_tar") &
    wait
  else
    fe_tar="$OUTPUT_DIR/${IMAGE_FRONTEND}.${file_tag}.tar"
    be_tar="$OUTPUT_DIR/${IMAGE_BACKEND}.${file_tag}.tar"
    log "💾 Saving uncompressed tarballs..."
    docker save -o "$fe_tar" "${IMAGE_FRONTEND}:latest" &
    docker save -o "$be_tar" "${IMAGE_BACKEND}:latest" &
    wait
  fi
  
  local save_time
  save_time=$(($(date +%s) - save_start))
  
  # Generate checksums
  sha256sum "$fe_tar" > "${fe_tar}.sha256"
  sha256sum "$be_tar" > "${be_tar}.sha256"
  
  log "✅ Saved in ${save_time}s:"
  printf "   %-50s %s\n" "$fe_tar" "$(du -h "$fe_tar" | cut -f1)"
  printf "   %-50s %s\n" "$be_tar" "$(du -h "$be_tar" | cut -f1)"
}

# =============================================================================
# Push to Registry
# =============================================================================
push_images() {
  if [[ -z "$PUSH_REMOTE" ]] || [[ "$DRY_RUN" -eq 1 ]]; then
    return 0
  fi
  
  log "📤 Pushing to ${PUSH_REMOTE}..."
  
  for img in "${IMAGE_FRONTEND}" "${IMAGE_BACKEND}"; do
    for tag in "latest" "${IMAGE_TAG}"; do
      docker tag "${img}:${tag}" "${PUSH_REMOTE}/${img}:${tag}"
      docker push "${PUSH_REMOTE}/${img}:${tag}" &
    done
  done
  wait
  
  log "✅ Pushed to registry"
}

# =============================================================================
# Main Execution
# =============================================================================
main() {
  local start_time
  start_time=$(date +%s)
  
  echo ""
  log "🐳 Customer Portal Docker Build"
  log "================================"
  info "🏷️  Tag: ${IMAGE_TAG}"
  info "📦 PNPM: ${PNPM_VERSION} | Platform: ${PLATFORM}"
  info "📁 Build logs: $LOG_DIR/"
  [[ "$DRY_RUN" -eq 1 ]] && warn "🔍 DRY-RUN MODE - no actual builds"
  echo ""
  
  # Setup
  setup_buildx
  clean_cache
  
  # Build images
  log "🚀 Starting Docker builds..."
  
  if [[ "$PARALLEL" -eq 1 ]]; then
    log "Building frontend & backend in parallel..."
    
    build_frontend & FE_PID=$!
    build_backend & BE_PID=$!
    
    # Track progress
    local elapsed=0
    while kill -0 $FE_PID 2>/dev/null || kill -0 $BE_PID 2>/dev/null; do
      sleep 10
      elapsed=$((elapsed + 10))
      info "⏳ Building... (${elapsed}s elapsed)"
    done
    
    # Check results
    local fe_exit=0 be_exit=0
    wait $FE_PID || fe_exit=$?
    wait $BE_PID || be_exit=$?
    
    [[ $fe_exit -ne 0 ]] && fail "Frontend build failed (exit $fe_exit) - check $LOG_DIR/frontend.log"
    [[ $be_exit -ne 0 ]] && fail "Backend build failed (exit $be_exit) - check $LOG_DIR/backend.log"
  else
    log "🔧 Sequential build mode..."
    build_frontend || fail "Frontend build failed - check $LOG_DIR/frontend.log"
    build_backend || fail "Backend build failed - check $LOG_DIR/backend.log"
  fi
  
  local build_time
  build_time=$(($(date +%s) - start_time))
  log "⏱️  Build completed in ${build_time}s"
  
  # Save and push
  save_tarballs
  push_images
  
  # Summary
  local total_time
  total_time=$(($(date +%s) - start_time))
  
  echo ""
  log "🎉 Complete in ${total_time}s"
  echo ""
  
  # Show next steps
  if [[ "$SAVE_TARS" -eq 1 ]] && [[ "$DRY_RUN" -eq 0 ]]; then
    local file_tag
    [[ "$USE_LATEST_FILENAME" -eq 1 ]] && file_tag="latest" || file_tag="$IMAGE_TAG"
    
    info "📋 Next steps for Plesk deployment:"
    echo ""
    echo "  1. Upload tarballs to your server:"
    echo "     scp ${IMAGE_FRONTEND}.${file_tag}.tar.gz* ${IMAGE_BACKEND}.${file_tag}.tar.gz* user@server:/path/"
    echo ""
    echo "  2. Load images on the server:"
    if [[ "$COMPRESS" -eq 1 ]]; then
      echo "     gunzip -c ${IMAGE_FRONTEND}.${file_tag}.tar.gz | docker load"
      echo "     gunzip -c ${IMAGE_BACKEND}.${file_tag}.tar.gz | docker load"
    else
      echo "     docker load -i ${IMAGE_FRONTEND}.${file_tag}.tar"
      echo "     docker load -i ${IMAGE_BACKEND}.${file_tag}.tar"
    fi
    echo ""
    echo "  3. Verify checksums:"
    echo "     sha256sum -c ${IMAGE_FRONTEND}.${file_tag}.tar.gz.sha256"
    echo "     sha256sum -c ${IMAGE_BACKEND}.${file_tag}.tar.gz.sha256"
    echo ""
    if [[ "$USE_LATEST_FILENAME" -eq 0 ]]; then
      echo "  4. Update Portainer with tag: ${IMAGE_TAG}"
      echo ""
    fi
  fi
}

# Run main
main
