#!/bin/bash

# Rustelo Application Deployment Script
# This script handles deployment of the Rustelo application in various environments

set -e

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

# Default values
ENVIRONMENT="production"
COMPOSE_FILE="docker-compose.yml"
BUILD_ARGS=""
MIGRATE_DB=false
BACKUP_DB=false
HEALTH_CHECK=true
TIMEOUT=300
PROJECT_NAME="rustelo"
DOCKER_REGISTRY=""
IMAGE_TAG="latest"
FORCE_RECREATE=false
SCALE_REPLICAS=1
FEATURES="production"
USE_DEFAULT_FEATURES=false

# Function to print colored output
print_status() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

print_debug() {
    if [[ "$DEBUG" == "true" ]]; then
        echo -e "${BLUE}[DEBUG]${NC} $1"
    fi
}

# Function to show usage
show_usage() {
    cat << EOF
Usage: $0 [OPTIONS] COMMAND

Commands:
    deploy          Deploy the application
    stop            Stop the application
    restart         Restart the application
    status          Show deployment status
    logs            Show application logs
    scale           Scale application replicas
    backup          Create database backup
    migrate         Run database migrations
    rollback        Rollback to previous version
    health          Check application health
    update          Update application to latest version
    clean           Clean up unused containers and images

Options:
    -e, --env ENV              Environment (dev|staging|production) [default: production]
    -f, --file FILE            Docker compose file [default: docker-compose.yml]
    -p, --project PROJECT      Project name [default: rustelo]
    -t, --tag TAG              Docker image tag [default: latest]
    -r, --registry REGISTRY    Docker registry URL
    -s, --scale REPLICAS       Number of replicas [default: 1]
    --migrate                  Run database migrations before deployment
    --backup                   Create database backup before deployment
    --no-health-check          Skip health check after deployment
    --force-recreate           Force recreation of containers
    --timeout SECONDS          Deployment timeout [default: 300]
    --build-arg ARG            Docker build arguments
    --features FEATURES        Cargo features to enable [default: production]
    --default-features         Use default features instead of custom
    --debug                    Enable debug output
    -h, --help                 Show this help message

Examples:
    $0 deploy                                    # Deploy production
    $0 deploy -e staging                         # Deploy staging
    $0 deploy --migrate --backup                 # Deploy with migration and backup
    $0 scale -s 3                               # Scale to 3 replicas
    $0 logs -f                                  # Follow logs
    $0 health                                   # Check health status
    $0 deploy --features "auth,metrics"         # Deploy with specific features
    $0 deploy --default-features               # Deploy with all default features

Environment Variables:
    DOCKER_REGISTRY            Docker registry URL
    RUSTELO_ENV               Environment override
    COMPOSE_PROJECT_NAME      Docker compose project name
    DATABASE_URL              Database connection string
    DEBUG                     Enable debug mode
EOF
}

# Function to parse command line arguments
parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -e|--env)
                ENVIRONMENT="$2"
                shift 2
                ;;
            -f|--file)
                COMPOSE_FILE="$2"
                shift 2
                ;;
            -p|--project)
                PROJECT_NAME="$2"
                shift 2
                ;;
            -t|--tag)
                IMAGE_TAG="$2"
                shift 2
                ;;
            -r|--registry)
                DOCKER_REGISTRY="$2"
                shift 2
                ;;
            -s|--scale)
                SCALE_REPLICAS="$2"
                shift 2
                ;;
            --migrate)
                MIGRATE_DB=true
                shift
                ;;
            --backup)
                BACKUP_DB=true
                shift
                ;;
            --no-health-check)
                HEALTH_CHECK=false
                shift
                ;;
            --force-recreate)
                FORCE_RECREATE=true
                shift
                ;;
            --timeout)
                TIMEOUT="$2"
                shift 2
                ;;
            --build-arg)
                BUILD_ARGS="$BUILD_ARGS --build-arg $2"
                shift 2
                ;;
            --features)
                FEATURES="$2"
                shift 2
                ;;
            --default-features)
                USE_DEFAULT_FEATURES=true
                shift
                ;;
            --debug)
                DEBUG=true
                shift
                ;;
            -h|--help)
                show_usage
                exit 0
                ;;
            -*)
                print_error "Unknown option: $1"
                show_usage
                exit 1
                ;;
            *)
                COMMAND="$1"
                shift
                ;;
        esac
    done
}

# Function to validate environment
validate_environment() {
    case $ENVIRONMENT in
        dev|development)
            ENVIRONMENT="development"
            COMPOSE_FILE="docker-compose.yml"
            ;;
        staging)
            ENVIRONMENT="staging"
            COMPOSE_FILE="docker-compose.staging.yml"
            ;;
        prod|production)
            ENVIRONMENT="production"
            COMPOSE_FILE="docker-compose.yml"
            ;;
        *)
            print_error "Invalid environment: $ENVIRONMENT"
            print_error "Valid environments: dev, staging, production"
            exit 1
            ;;
    esac
}

# Function to check prerequisites
check_prerequisites() {
    print_status "Checking prerequisites..."

    # Check if Docker is installed and running
    if ! command -v docker &> /dev/null; then
        print_error "Docker is not installed or not in PATH"
        exit 1
    fi

    if ! docker info &> /dev/null; then
        print_error "Docker daemon is not running"
        exit 1
    fi

    # Check if Docker Compose is installed
    if ! command -v docker-compose &> /dev/null; then
        print_error "Docker Compose is not installed or not in PATH"
        exit 1
    fi

    # Check if compose file exists
    if [[ ! -f "$COMPOSE_FILE" ]]; then
        print_error "Compose file not found: $COMPOSE_FILE"
        exit 1
    fi

    print_status "Prerequisites check passed"
}

# Function to set environment variables
set_environment_vars() {
    export COMPOSE_PROJECT_NAME="${PROJECT_NAME}"
    export DOCKER_REGISTRY="${DOCKER_REGISTRY}"
    export IMAGE_TAG="${IMAGE_TAG}"
    export ENVIRONMENT="${ENVIRONMENT}"

    # Source environment-specific variables
    if [[ -f ".env.${ENVIRONMENT}" ]]; then
        print_status "Loading environment variables from .env.${ENVIRONMENT}"
        source ".env.${ENVIRONMENT}"
    elif [[ -f ".env" ]]; then
        print_status "Loading environment variables from .env"
        source ".env"
    fi

    print_debug "Environment variables set:"
    print_debug "  COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}"
    print_debug "  DOCKER_REGISTRY=${DOCKER_REGISTRY}"
    print_debug "  IMAGE_TAG=${IMAGE_TAG}"
    print_debug "  ENVIRONMENT=${ENVIRONMENT}"
    print_debug "  FEATURES=${FEATURES}"
    print_debug "  USE_DEFAULT_FEATURES=${USE_DEFAULT_FEATURES}"
}

# Function to build Docker images
build_images() {
    print_status "Building Docker images..."

    local build_cmd="docker-compose -f $COMPOSE_FILE build"

    if [[ -n "$BUILD_ARGS" ]]; then
        build_cmd="$build_cmd $BUILD_ARGS"
    fi

    # Add feature arguments to build args
    if [[ "$USE_DEFAULT_FEATURES" == "false" ]]; then
        build_cmd="$build_cmd --build-arg CARGO_FEATURES=\"$FEATURES\" --build-arg NO_DEFAULT_FEATURES=\"true\""
    else
        build_cmd="$build_cmd --build-arg CARGO_FEATURES=\"\" --build-arg NO_DEFAULT_FEATURES=\"false\""
    fi

    if [[ "$DEBUG" == "true" ]]; then
        print_debug "Build command: $build_cmd"
    fi

    if ! $build_cmd; then
        print_error "Failed to build Docker images"
        exit 1
    fi

    print_status "Docker images built successfully"
}

# Function to create database backup
create_backup() {
    if [[ "$BACKUP_DB" == "true" ]]; then
        print_status "Creating database backup..."

        local backup_file="backup_$(date +%Y%m%d_%H%M%S).sql"

        if docker-compose -f "$COMPOSE_FILE" exec -T db pg_dump -U postgres rustelo_prod > "$backup_file"; then
            print_status "Database backup created: $backup_file"
        else
            print_error "Failed to create database backup"
            exit 1
        fi
    fi
}

# Function to run database migrations
run_migrations() {
    if [[ "$MIGRATE_DB" == "true" ]]; then
        print_status "Running database migrations..."

        if docker-compose -f "$COMPOSE_FILE" run --rm migrate; then
            print_status "Database migrations completed successfully"
        else
            print_error "Database migrations failed"
            exit 1
        fi
    fi
}

# Function to deploy application
deploy_application() {
    print_status "Deploying application..."

    local compose_cmd="docker-compose -f $COMPOSE_FILE up -d"

    if [[ "$FORCE_RECREATE" == "true" ]]; then
        compose_cmd="$compose_cmd --force-recreate"
    fi

    if [[ "$SCALE_REPLICAS" -gt 1 ]]; then
        compose_cmd="$compose_cmd --scale app=$SCALE_REPLICAS"
    fi

    if [[ "$DEBUG" == "true" ]]; then
        print_debug "Deploy command: $compose_cmd"
    fi

    if ! $compose_cmd; then
        print_error "Failed to deploy application"
        exit 1
    fi

    print_status "Application deployed successfully"
}

# Function to wait for application to be ready
wait_for_health() {
    if [[ "$HEALTH_CHECK" == "true" ]]; then
        print_status "Waiting for application to be healthy..."

        local start_time=$(date +%s)
        local health_url="http://localhost:3030/health"

        while true; do
            local current_time=$(date +%s)
            local elapsed=$((current_time - start_time))

            if [[ $elapsed -gt $TIMEOUT ]]; then
                print_error "Health check timeout after ${TIMEOUT} seconds"
                exit 1
            fi

            if curl -f -s "$health_url" > /dev/null 2>&1; then
                print_status "Application is healthy"
                break
            fi

            print_debug "Health check failed, retrying in 5 seconds... (${elapsed}s elapsed)"
            sleep 5
        done
    fi
}

# Function to show deployment status
show_status() {
    print_status "Deployment status:"
    docker-compose -f "$COMPOSE_FILE" ps

    print_status "Container resource usage:"
    docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
}

# Function to show logs
show_logs() {
    local follow_flag=""
    if [[ "$1" == "-f" ]]; then
        follow_flag="-f"
    fi

    docker-compose -f "$COMPOSE_FILE" logs $follow_flag
}

# Function to scale application
scale_application() {
    print_status "Scaling application to $SCALE_REPLICAS replicas..."

    if docker-compose -f "$COMPOSE_FILE" up -d --scale app="$SCALE_REPLICAS"; then
        print_status "Application scaled successfully"
    else
        print_error "Failed to scale application"
        exit 1
    fi
}

# Function to stop application
stop_application() {
    print_status "Stopping application..."

    if docker-compose -f "$COMPOSE_FILE" down; then
        print_status "Application stopped successfully"
    else
        print_error "Failed to stop application"
        exit 1
    fi
}

# Function to restart application
restart_application() {
    print_status "Restarting application..."

    if docker-compose -f "$COMPOSE_FILE" restart; then
        print_status "Application restarted successfully"
    else
        print_error "Failed to restart application"
        exit 1
    fi
}

# Function to check application health
check_health() {
    print_status "Checking application health..."

    local health_url="http://localhost:3030/health"

    if curl -f -s "$health_url" | jq '.status' | grep -q "healthy"; then
        print_status "Application is healthy"

        # Show detailed health information
        curl -s "$health_url" | jq .
    else
        print_error "Application is not healthy"
        exit 1
    fi
}

# Function to update application
update_application() {
    print_status "Updating application..."

    # Pull latest images
    docker-compose -f "$COMPOSE_FILE" pull

    # Restart with new images
    docker-compose -f "$COMPOSE_FILE" up -d --force-recreate

    print_status "Application updated successfully"
}

# Function to rollback application
rollback_application() {
    print_warning "Rollback functionality not implemented yet"
    print_warning "Please manually specify the desired image tag and redeploy"
}

# Function to clean up
cleanup() {
    print_status "Cleaning up unused containers and images..."

    # Remove stopped containers
    docker container prune -f

    # Remove unused images
    docker image prune -f

    # Remove unused volumes
    docker volume prune -f

    # Remove unused networks
    docker network prune -f

    print_status "Cleanup completed"
}

# Main function
main() {
    # Parse command line arguments
    parse_args "$@"

    # Validate command
    if [[ -z "$COMMAND" ]]; then
        print_error "No command specified"
        show_usage
        exit 1
    fi

    # Validate environment
    validate_environment

    # Check prerequisites
    check_prerequisites

    # Set environment variables
    set_environment_vars

    # Execute command
    case $COMMAND in
        deploy)
            build_images
            create_backup
            run_migrations
            deploy_application
            wait_for_health
            show_status
            ;;
        stop)
            stop_application
            ;;
        restart)
            restart_application
            wait_for_health
            ;;
        status)
            show_status
            ;;
        logs)
            show_logs "$@"
            ;;
        scale)
            scale_application
            ;;
        backup)
            create_backup
            ;;
        migrate)
            run_migrations
            ;;
        rollback)
            rollback_application
            ;;
        health)
            check_health
            ;;
        update)
            update_application
            wait_for_health
            ;;
        clean)
            cleanup
            ;;
        *)
            print_error "Unknown command: $COMMAND"
            show_usage
            exit 1
            ;;
    esac
}

# Run main function
main "$@"
