#!/bin/bash

# OpenCrates Production Deployment Script
# Comprehensive deployment automation with health checks and rollback capability

set -euo pipefail

# Configuration
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly LOG_FILE="${PROJECT_ROOT}/deployment.log"

# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'

# Deployment configuration
ENVIRONMENT="${1:-staging}"
FORCE_DEPLOY="${FORCE_DEPLOY:-false}"
SKIP_TESTS="${SKIP_TESTS:-false}"
SKIP_BACKUP="${SKIP_BACKUP:-false}"
ROLLBACK_ON_FAILURE="${ROLLBACK_ON_FAILURE:-true}"
HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-300}"
DEPLOYMENT_TIMEOUT="${DEPLOYMENT_TIMEOUT:-600}"

# Service configuration
readonly SERVICES=(
  "opencrates"
  "postgres"
  "redis"
  "prometheus"
  "grafana"
  "nginx"
)

# Logging functions
log() {
  echo -e "$(date '+%Y-%m-%d %H:%M:%S') - $*" | tee -a "$LOG_FILE"
}

log_info() {
  log "${BLUE}[INFO]${NC} $*"
}

log_success() {
  log "${GREEN}[SUCCESS]${NC} $*"
}

log_warning() {
  log "${YELLOW}[WARNING]${NC} $*"
}

log_error() {
  log "${RED}[ERROR]${NC} $*"
}

log_section() {
  log ""
  log "${BLUE}=== $* ===${NC}"
}

# Error handling
handle_error() {
  local line_number=$1
  local error_code=$2
  log_error "Deployment failed at line $line_number with exit code $error_code"
  
  if [[ "$ROLLBACK_ON_FAILURE" == "true" ]]; then
    log_warning "Initiating rollback procedure..."
    rollback_deployment
  fi
  
  cleanup
  exit "$error_code"
}

trap 'handle_error $LINENO $?' ERR

# Cleanup function
cleanup() {
  log_info "Cleaning up temporary files..."
  # Add cleanup logic here
  log_success "Cleanup completed"
}

# Environment validation
validate_environment() {
  log_section "Validating Environment: $ENVIRONMENT"
  
  case "$ENVIRONMENT" in
    development|dev)
      COMPOSE_FILE="docker-compose.yml"
      COMPOSE_PROFILES="development"
      ;;
    staging|stage)
      COMPOSE_FILE="docker-compose.yml"
      COMPOSE_PROFILES=""
      ;;
    production|prod)
      COMPOSE_FILE="docker-compose.yml"
      COMPOSE_PROFILES=""
      ;;
    *)
      log_error "Invalid environment: $ENVIRONMENT"
      log_info "Valid environments: development, staging, production"
      exit 1
      ;;
  esac
  
  log_success "Environment validated: $ENVIRONMENT"
}

# Prerequisites check
check_prerequisites() {
  log_section "Checking Prerequisites"
  
  local missing_tools=()
  
  # Check required tools
  for tool in docker docker-compose git curl; do
    if ! command -v "$tool" &> /dev/null; then
      missing_tools+=("$tool")
    fi
  done
  
  if [[ ${#missing_tools[@]} -gt 0 ]]; then
    log_error "Missing required tools: ${missing_tools[*]}"
    exit 1
  fi
  
  # Check Docker daemon
  if ! docker info &> /dev/null; then
    log_error "Docker daemon is not running"
    exit 1
  fi
  
  # Check disk space (minimum 5GB)
  local available_space
  available_space=$(df / | awk 'NR==2 {print $4}')
  if [[ $available_space -lt 5242880 ]]; then
    log_warning "Low disk space: $(($available_space / 1024 / 1024))GB available"
  fi
  
  # Check memory (minimum 2GB)
  local available_memory
  available_memory=$(free -m | awk 'NR==2{print $7}')
  if [[ $available_memory -lt 2048 ]]; then
    log_warning "Low memory: ${available_memory}MB available"
  fi
  
  log_success "Prerequisites check completed"
}

# Environment configuration
setup_environment() {
  log_section "Setting Up Environment Configuration"
  
  # Create environment-specific configuration
  case "$ENVIRONMENT" in
    production)
      export OPENCRATES_SECRET_KEY="${OPENCRATES_SECRET_KEY:-$(openssl rand -hex 32)}"
      export POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-$(openssl rand -base64 32)}"
      export GRAFANA_PASSWORD="${GRAFANA_PASSWORD:-$(openssl rand -base64 16)}"
      ;;
    staging)
      export OPENCRATES_SECRET_KEY="${OPENCRATES_SECRET_KEY:-staging-secret-key}"
      export POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-staging-password}"
      export GRAFANA_PASSWORD="${GRAFANA_PASSWORD:-admin}"
      ;;
    development)
      export OPENCRATES_SECRET_KEY="dev-secret-key"
      export POSTGRES_PASSWORD="dev-password"
      export GRAFANA_PASSWORD="admin"
      ;;
  esac
  
  # Validate required environment variables
  local required_vars=(
    "OPENAI_API_KEY"
  )
  
  for var in "${required_vars[@]}"; do
    if [[ -z "${!var:-}" ]]; then
      log_error "Required environment variable $var is not set"
      exit 1
    fi
  done
  
  log_success "Environment configuration completed"
}

# Pre-deployment tests
run_tests() {
  if [[ "$SKIP_TESTS" == "true" ]]; then
    log_warning "Skipping tests as requested"
    return 0
  fi
  
  log_section "Running Pre-deployment Tests"
  
  # Build and test
  log_info "Building and testing application..."
  cargo test --release --all-features
  
  # Security audit
  log_info "Running security audit..."
  if command -v cargo-audit &> /dev/null; then
    cargo audit
  else
    log_warning "cargo-audit not available, skipping security audit"
  fi
  
  # Integration tests
  if [[ -f "$PROJECT_ROOT/scripts/test-all.sh" ]]; then
    log_info "Running comprehensive test suite..."
    bash "$PROJECT_ROOT/scripts/test-all.sh" --skip-docker
  fi
  
  log_success "Tests completed successfully"
}

# Database backup
backup_database() {
  if [[ "$SKIP_BACKUP" == "true" ]]; then
    log_warning "Skipping database backup as requested"
    return 0
  fi
  
  log_section "Creating Database Backup"
  
  local backup_dir="${PROJECT_ROOT}/backups"
  local backup_file="${backup_dir}/backup-$(date +%Y%m%d-%H%M%S).sql"
  
  mkdir -p "$backup_dir"
  
  # Check if database is running
  if docker-compose ps postgres | grep -q "Up"; then
    log_info "Creating database backup..."
    docker-compose exec -T postgres pg_dump -U opencrates opencrates > "$backup_file"
    gzip "$backup_file"
    log_success "Database backup created: ${backup_file}.gz"
  else
    log_warning "Database not running, skipping backup"
  fi
}

# Build application
build_application() {
  log_section "Building Application"
  
  log_info "Building Docker images..."
  
  # Build with build cache
  docker-compose build \
    --parallel \
    --pull \
    ${FORCE_DEPLOY:+--no-cache}
  
  # Tag images for rollback
  local timestamp=$(date +%Y%m%d-%H%M%S)
  docker tag opencrates:latest "opencrates:backup-$timestamp" || true
  
  log_success "Application build completed"
}

# Deploy services
deploy_services() {
  log_section "Deploying Services"
  
  log_info "Stopping existing services..."
  docker-compose down --remove-orphans
  
  log_info "Starting new services..."
  if [[ -n "$COMPOSE_PROFILES" ]]; then
    COMPOSE_PROFILES="$COMPOSE_PROFILES" docker-compose up -d
  else
    docker-compose up -d
  fi
  
  log_success "Services deployment initiated"
}

# Health checks
health_checks() {
  log_section "Running Health Checks"
  
  local max_attempts=$((HEALTH_CHECK_TIMEOUT / 10))
  local attempt=0
  
  # Check service health
  for service in "${SERVICES[@]}"; do
    log_info "Checking health of $service..."
    attempt=0
    
    while [[ $attempt -lt $max_attempts ]]; do
      if docker-compose ps "$service" | grep -q "Up (healthy)\|Up [0-9]"; then
        log_success "$service is healthy"
        break
      fi
      
      if [[ $attempt -eq $((max_attempts - 1)) ]]; then
        log_error "$service failed health check"
        return 1
      fi
      
      ((attempt++))
      sleep 10
    done
  done
  
  # Application-specific health checks
  log_info "Checking application endpoints..."
  
  local endpoints=(
    "http://localhost:8080/health"
    "http://localhost:8080/status"
    "http://localhost:9091/-/healthy"  # Prometheus
    "http://localhost:3000/api/health" # Grafana
  )
  
  for endpoint in "${endpoints[@]}"; do
    attempt=0
    while [[ $attempt -lt $max_attempts ]]; do
      if curl -sf "$endpoint" > /dev/null 2>&1; then
        log_success "Endpoint $endpoint is healthy"
        break
      fi
      
      if [[ $attempt -eq $((max_attempts - 1)) ]]; then
        log_error "Endpoint $endpoint failed health check"
        return 1
      fi
      
      ((attempt++))
      sleep 10
    done
  done
  
  log_success "All health checks passed"
}

# Load testing
run_load_tests() {
  if [[ "$ENVIRONMENT" == "production" ]]; then
    log_warning "Skipping load tests in production environment"
    return 0
  fi
  
  log_section "Running Load Tests"
  
  if [[ -f "$PROJECT_ROOT/scripts/k6/load-test.js" ]]; then
    log_info "Running k6 load tests..."
    docker run --rm -i \
      --network="$(basename "$PROJECT_ROOT")_opencrates-network" \
      -v "$PROJECT_ROOT/scripts/k6:/scripts" \
      grafana/k6:latest run /scripts/load-test.js \
      --env BASE_URL=http://opencrates:8080 \
      --env TEST_ENV="$ENVIRONMENT"
    
    log_success "Load tests completed"
  else
    log_warning "Load test script not found, skipping"
  fi
}

# Rollback function
rollback_deployment() {
  log_section "Rolling Back Deployment"
  
  log_warning "Attempting to restore previous version..."
  
  # Restore database from backup if available
  local latest_backup
  latest_backup=$(find "$PROJECT_ROOT/backups" -name "*.sql.gz" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
  
  if [[ -n "$latest_backup" && -f "$latest_backup" ]]; then
    log_info "Restoring database from backup: $latest_backup"
    gunzip -c "$latest_backup" | docker-compose exec -T postgres psql -U opencrates opencrates
  fi
  
  # Restore previous Docker images
  if docker images | grep -q "opencrates:backup-"; then
    local backup_image
    backup_image=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | grep "opencrates:backup-" | head -1 | awk '{print $1}')
    
    if [[ -n "$backup_image" ]]; then
      log_info "Restoring previous image: $backup_image"
      docker tag "$backup_image" opencrates:latest
      docker-compose up -d opencrates
    fi
  fi
  
  log_warning "Rollback completed. Please verify system functionality."
}

# Monitoring setup
setup_monitoring() {
  log_section "Setting Up Monitoring"
  
  # Configure Grafana dashboards
  if docker-compose ps grafana | grep -q "Up"; then
    log_info "Importing Grafana dashboards..."
    # Dashboard import logic would go here
  fi
  
  # Configure Prometheus alerts
  if docker-compose ps prometheus | grep -q "Up"; then
    log_info "Setting up Prometheus alerts..."
    # Alert configuration logic would go here
  fi
  
  log_success "Monitoring setup completed"
}

# Post-deployment verification
post_deployment_verification() {
  log_section "Post-deployment Verification"
  
  # Verify database migration
  log_info "Verifying database migrations..."
  # Add migration verification logic
  
  # Verify configuration
  log_info "Verifying application configuration..."
  # Add configuration verification logic
  
  # Verify integrations
  log_info "Verifying external integrations..."
  # Add integration verification logic
  
  log_success "Post-deployment verification completed"
}

# Generate deployment report
generate_report() {
  log_section "Generating Deployment Report"
  
  local report_file="${PROJECT_ROOT}/deployment-report-$(date +%Y%m%d-%H%M%S).md"
  
  cat > "$report_file" << EOF
# OpenCrates Deployment Report

**Environment:** $ENVIRONMENT  
**Date:** $(date)  
**Version:** $(git rev-parse --short HEAD)  

## Deployment Summary

- **Status:** SUCCESS
- **Duration:** $((SECONDS / 60)) minutes
- **Services Deployed:** ${SERVICES[*]}

## Health Check Results

$(for service in "${SERVICES[@]}"; do
  echo "- **$service:** $(docker-compose ps "$service" | grep -q "Up" && echo "Running" || echo "Failed")"
done)

## Service URLs

- **Application:** http://localhost:8080
- **Grafana:** http://localhost:3000
- **Prometheus:** http://localhost:9091

## Next Steps

1. Monitor application metrics
2. Verify user functionality
3. Check logs for any errors
4. Review security settings

---
*Generated by OpenCrates deployment script*
EOF
  
  log_success "Deployment report generated: $report_file"
}

# Main deployment function
main() {
  log_section "OpenCrates Deployment Started"
  log_info "Environment: $ENVIRONMENT"
  log_info "Force deploy: $FORCE_DEPLOY"
  log_info "Skip tests: $SKIP_TESTS"
  
  # Deployment steps
  validate_environment
  check_prerequisites
  setup_environment
  run_tests
  backup_database
  build_application
  deploy_services
  health_checks
  setup_monitoring
  post_deployment_verification
  run_load_tests
  
  log_section "Deployment Completed Successfully"
  log_success "OpenCrates $ENVIRONMENT deployment completed in $((SECONDS / 60)) minutes"
  
  generate_report
}

# Help function
show_help() {
  cat << EOF
OpenCrates Deployment Script

Usage: $0 [ENVIRONMENT] [OPTIONS]

ENVIRONMENTS:
  development, dev    Deploy to development environment
  staging, stage      Deploy to staging environment  
  production, prod    Deploy to production environment

ENVIRONMENT VARIABLES:
  FORCE_DEPLOY=true          Force rebuild without cache
  SKIP_TESTS=true           Skip pre-deployment tests
  SKIP_BACKUP=true          Skip database backup
  ROLLBACK_ON_FAILURE=false Disable automatic rollback
  HEALTH_CHECK_TIMEOUT=300   Health check timeout in seconds
  DEPLOYMENT_TIMEOUT=600     Overall deployment timeout

EXAMPLES:
  $0 staging
  FORCE_DEPLOY=true $0 production
  SKIP_TESTS=true $0 development

EOF
}

# Command line argument parsing
case "${1:-}" in
  -h|--help|help)
    show_help
    exit 0
    ;;
  *)
    main "$@"
    ;;
esac 