#!/usr/bin/env bash
set -euo pipefail

ENVIRONMENT="${1:-staging}"
VALID_ENVS=("staging" "production")

if [[ ! " ${VALID_ENVS[@]} " =~ " ${ENVIRONMENT} " ]]; then
    echo "Usage: $0 [staging|production]"
    exit 1
fi

DEPLOY_DIR="${DEPLOY_DIR:-/opt/raseel}"
LOG_FILE="${LOG_FILE:-storage/logs/deploy.log}"
COMPOSE_FILE="docker-compose.prod.yml"
IMAGE_TAG="${IMAGE_TAG:-latest}"
PREVIOUS_TAG_FILE="${DEPLOY_DIR}/.previous-tag"

mkdir -p "$(dirname "$LOG_FILE")"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

cleanup() {
    EXIT_CODE=$?
    if [ $EXIT_CODE -ne 0 ]; then
        log "Deployment failed with exit code ${EXIT_CODE}"
        log "To rollback run: $0 rollback"
    fi
    exit $EXIT_CODE
}

trap cleanup EXIT

save_previous_tag() {
    local current_tag
    current_tag=$(docker images --format '{{.Tag}}' \
        $(grep -oP 'image: \K\S+' "$COMPOSE_FILE" 2>/dev/null | head -1) 2>/dev/null | head -1 || echo "")
    if [ -n "$current_tag" ] && [ "$current_tag" != "$IMAGE_TAG" ]; then
        echo "$current_tag" > "$PREVIOUS_TAG_FILE"
        log "Saved previous image tag: $current_tag"
    fi
}

pull_images() {
    log "Pulling latest Docker images (tag: ${IMAGE_TAG})"
    docker compose -f "$COMPOSE_FILE" pull
}

backup_database() {
    log "Running pre-deployment database backup"
    if [ -x "./scripts/backup.sh" ]; then
        ./scripts/backup.sh || log "WARNING: Pre-deployment backup failed, continuing..."
    else
        log "WARNING: backup.sh not found, skipping pre-deployment backup"
    fi
}

run_migrations() {
    log "Running database migrations"
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan migrate --force
}

clear_caches() {
    log "Clearing application caches"
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan cache:clear || true
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan config:clear || true
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan route:clear || true
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan view:clear || true
}

rebuild_caches() {
    log "Rebuilding application caches"
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan config:cache
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan route:cache
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan view:cache
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan event:cache
}

restart_services() {
    log "Restarting application services"
    docker compose -f "$COMPOSE_FILE" up -d --no-deps --force-recreate app worker scheduler

    log "Restarting queue workers"
    docker compose -f "$COMPOSE_FILE" exec -T app php artisan queue:restart || true

    log "Reloading nginx"
    docker compose -f "$COMPOSE_FILE" exec -T nginx nginx -s reload || true
}

run_seeders() {
    if [ "${RUN_SEEDERS:-false}" = "true" ]; then
        log "Running database seeders"
        docker compose -f "$COMPOSE_FILE" exec -T app php artisan db:seed --force
    else
        log "Skipping seeders (set RUN_SEEDERS=true to enable)"
    fi
}

health_check() {
    local max_attempts=10
    local attempt=1
    local url="${HEALTH_CHECK_URL:-http://localhost/api/health}"

    log "Running health check against ${url}"

    while [ $attempt -le $max_attempts ]; do
        local status
        status=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")

        if [ "$status" = "200" ]; then
            log "Health check passed (attempt ${attempt})"
            curl -s "$url" | tee -a "$LOG_FILE"
            echo "" >> "$LOG_FILE"
            return 0
        fi

        log "Health check attempt ${attempt}/${max_attempts} returned ${status}, retrying in 10s..."
        sleep 10
        attempt=$((attempt+1))
    done

    log "ERROR: Health check failed after ${max_attempts} attempts"
    return 1
}

rollback() {
    log "=== Starting rollback ==="

    if [ ! -f "$PREVIOUS_TAG_FILE" ]; then
        log "ERROR: No previous tag found at ${PREVIOUS_TAG_FILE}, cannot rollback"
        exit 1
    fi

    local previous_tag
    previous_tag=$(cat "$PREVIOUS_TAG_FILE")
    log "Rolling back to image tag: ${previous_tag}"

    export IMAGE_TAG="$previous_tag"
    pull_images
    restart_services

    if ! health_check; then
        log "ERROR: Health check failed after rollback"
        exit 1
    fi

    log "=== Rollback completed successfully ==="
    exit 0
}

deploy() {
    log "=== Starting deployment to ${ENVIRONMENT} (tag: ${IMAGE_TAG}) ==="

    cd "$DEPLOY_DIR" 2>/dev/null || {
        log "ERROR: Deploy directory ${DEPLOY_DIR} not found"
        exit 1
    }

    save_previous_tag

    if [ "$ENVIRONMENT" = "production" ]; then
        backup_database
    fi

    pull_images
    restart_services
    run_migrations
    clear_caches
    rebuild_caches
    run_seeders
    health_check

    log "=== Deployment to ${ENVIRONMENT} completed successfully ==="
    exit 0
}

if [ "$ENVIRONMENT" = "rollback" ]; then
    rollback
else
    deploy
fi