#!/usr/bin/env bash
##############################################################
# GMLM Platform — Blue-Green Deployment Script
#
# Deploys a new version with zero downtime by:
# 1. Starting the new version in the standby slot
# 2. Running health checks on the standby
# 3. Switching Nginx upstream to standby
# 4. Old version becomes new standby
# 5. On failure: stays on current, new slot is destroyed
#
# Usage:
#   bash blue-green-deploy.sh \
#     --image "ghcr.io/org/gmlm-platform:sha-abc123" \
#     --env production \
#     [--rollback-on-fail]
#
# Environments:
#   Each environment uses two Docker Compose projects: "blue" and "green"
#   A symlink "current" points to whichever is active.
#   Nginx upstream is updated to point at the active project.
##############################################################

set -euo pipefail

# ── Parse arguments ────────────────────────────────────────────
IMAGE=""
ENV="production"
ROLLBACK_ON_FAIL=false
INSTALL_DIR="/var/www/gmlm"
HEALTH_CHECK_URL=""
HEALTH_CHECK_RETRIES=12
HEALTH_CHECK_INTERVAL=10

while [[ "$#" -gt 0 ]]; do
    case $1 in
        --image)              IMAGE="$2";            shift ;;
        --env)                ENV="$2";              shift ;;
        --rollback-on-fail)   ROLLBACK_ON_FAIL=true  ;;
        --health-url)         HEALTH_CHECK_URL="$2"; shift ;;
        *) echo "Unknown arg: $1"; exit 1 ;;
    esac
    shift
done

if [[ -z "$IMAGE" ]]; then
    echo "Error: --image is required"
    exit 1
fi

# ── Resolve current/standby slots ─────────────────────────────
CURRENT_SLOT_FILE="${INSTALL_DIR}/.current-slot"
if [[ -f "$CURRENT_SLOT_FILE" ]]; then
    CURRENT_SLOT=$(cat "$CURRENT_SLOT_FILE")
else
    CURRENT_SLOT="blue"
fi

if [[ "$CURRENT_SLOT" == "blue" ]]; then
    STANDBY_SLOT="green"
else
    STANDBY_SLOT="blue"
fi

echo ""
echo "╔══════════════════════════════════════════╗"
echo "║   GMLM Blue-Green Deployment             ║"
echo "╚══════════════════════════════════════════╝"
echo ""
echo "  Current slot: ${CURRENT_SLOT} (receiving traffic)"
echo "  Standby slot: ${STANDBY_SLOT} (deploying here)"
echo "  Image:        ${IMAGE}"
echo "  Environment:  ${ENV}"
echo ""

cd "${INSTALL_DIR}"

# ── Step 1: Pull the new image ────────────────────────────────
echo "▶ Pulling image: ${IMAGE}"
docker pull "${IMAGE}"
echo "✓ Image pulled."

# ── Step 2: Update standby environment ────────────────────────
echo "▶ Starting standby slot (${STANDBY_SLOT})..."

# Write slot-specific compose override
cat > "docker-compose.${STANDBY_SLOT}.yml" << EOF
services:
  app:
    image: ${IMAGE}
    container_name: gmlm-app-${STANDBY_SLOT}
    environment:
      GMLM_VERSION: ${IMAGE##*:}
    ports:
      - "${STANDBY_SLOT == 'blue' ? '8001' : '8002'}:80"
  horizon:
    image: ${IMAGE}
    container_name: gmlm-horizon-${STANDBY_SLOT}
EOF

# Start standby slot
COMPOSE_PROJECT_NAME="gmlm-${STANDBY_SLOT}" \
    docker compose \
        -f docker-compose.yml \
        -f "docker-compose.${STANDBY_SLOT}.yml" \
    up -d --no-deps app

echo "✓ Standby slot started."

# ── Step 3: Run migrations on standby ────────────────────────
echo "▶ Running pending migrations on standby..."
COMPOSE_PROJECT_NAME="gmlm-${STANDBY_SLOT}" \
    docker compose exec -T app \
    php artisan migrate --force --no-interaction

echo "✓ Migrations applied."

# ── Step 4: Health check standby ─────────────────────────────
echo "▶ Running health checks on standby..."

STANDBY_PORT=$([[ "$STANDBY_SLOT" == "blue" ]] && echo "8001" || echo "8002")

HEALTHY=false
for i in $(seq 1 ${HEALTH_CHECK_RETRIES}); do
    echo "  Attempt ${i}/${HEALTH_CHECK_RETRIES}..."

    HTTP_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" \
        "http://localhost:${STANDBY_PORT}/api/v1/health" 2>/dev/null || echo "000")

    if [[ "$HTTP_STATUS" == "200" ]]; then
        HEALTH_BODY=$(curl -sf "http://localhost:${STANDBY_PORT}/api/v1/health" 2>/dev/null || echo "{}")
        HEALTH_STATUS=$(echo "$HEALTH_BODY" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('status','unknown'))" 2>/dev/null || echo "unknown")

        if [[ "$HEALTH_STATUS" == "healthy" ]]; then
            HEALTHY=true
            echo "  ✓ Health check passed (attempt ${i})"
            break
        fi
    fi

    echo "  HTTP ${HTTP_STATUS} — retrying in ${HEALTH_CHECK_INTERVAL}s..."
    sleep ${HEALTH_CHECK_INTERVAL}
done

if [[ "$HEALTHY" != "true" ]]; then
    echo ""
    echo "✗ Health check failed after ${HEALTH_CHECK_RETRIES} attempts."

    # Clean up failed standby
    echo "  Removing failed standby..."
    COMPOSE_PROJECT_NAME="gmlm-${STANDBY_SLOT}" \
        docker compose stop app 2>/dev/null || true

    if [[ "$ROLLBACK_ON_FAIL" == "true" ]]; then
        echo "  Rollback flag set — current slot (${CURRENT_SLOT}) remains active."
    fi

    exit 1
fi

# ── Step 5: Warm up standby ────────────────────────────────────
echo "▶ Warming standby caches..."
COMPOSE_PROJECT_NAME="gmlm-${STANDBY_SLOT}" \
    docker compose exec -T app \
    php artisan gmlm:warm-cache || true

# ── Step 6: Switch Nginx upstream ─────────────────────────────
echo "▶ Switching traffic from ${CURRENT_SLOT} to ${STANDBY_SLOT}..."

# Update the Nginx upstream configuration
cat > /etc/nginx/conf.d/gmlm-upstream.conf << EOF
upstream gmlm_backend {
    server 127.0.0.1:${STANDBY_PORT};
    keepalive 32;
}
EOF

# Test and reload Nginx
nginx -t && nginx -s reload

echo "✓ Traffic switched to ${STANDBY_SLOT}."

# ── Step 7: Verify traffic is flowing ─────────────────────────
sleep 5
VERIFY=$(curl -sf "http://localhost/api/v1/health" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('status','unknown'))" 2>/dev/null || echo "unknown")

if [[ "$VERIFY" != "healthy" ]]; then
    echo "✗ Traffic verification failed! Switching back to ${CURRENT_SLOT}..."

    # Revert Nginx
    CURRENT_PORT=$([[ "$CURRENT_SLOT" == "blue" ]] && echo "8001" || echo "8002")
    cat > /etc/nginx/conf.d/gmlm-upstream.conf << EOF
upstream gmlm_backend {
    server 127.0.0.1:${CURRENT_PORT};
    keepalive 32;
}
EOF
    nginx -s reload
    exit 1
fi

# ── Step 8: Record new slot and start Horizon on new slot ────
echo "${STANDBY_SLOT}" > "${CURRENT_SLOT_FILE}"

# Start Horizon on new slot (was only running app container)
COMPOSE_PROJECT_NAME="gmlm-${STANDBY_SLOT}" \
    docker compose -f docker-compose.yml up -d horizon scheduler

# Gracefully stop Horizon on old slot (let jobs finish)
COMPOSE_PROJECT_NAME="gmlm-${CURRENT_SLOT}" \
    docker compose exec -T app \
    php artisan horizon:terminate 2>/dev/null || true

# Wait for old Horizon to finish its jobs (max 60s)
sleep 60

# Stop old slot (keep it for 10 minutes as rollback option)
COMPOSE_PROJECT_NAME="gmlm-${CURRENT_SLOT}" \
    docker compose stop 2>/dev/null || true

# ── Summary ────────────────────────────────────────────────────
echo ""
echo "╔══════════════════════════════════════════╗"
echo "║   ✓ Deployment Complete                  ║"
echo "╚══════════════════════════════════════════╝"
echo ""
echo "  Active slot: ${STANDBY_SLOT}"
echo "  Image:       ${IMAGE}"
echo "  Standby:     ${CURRENT_SLOT} (stopped, available for rollback)"
echo ""
echo "  To rollback: echo '${CURRENT_SLOT}' > ${CURRENT_SLOT_FILE} && nginx -s reload"
echo ""
