#!/bin/bash

# Deploy Molecules - Molecular Operations Library
# Combined operations that use atomic functions
#
# This file contains functions that combine multiple atoms into cohesive
# operations. Each molecule performs a complete, testable operation.
#
# Usage:
#   source "$(dirname "$0")/_deploy-atoms.sh"
#   source "$(dirname "$0")/_deploy-molecules.sh"

# ==============================================================================
# BACKUP OPERATIONS
# ==============================================================================

# Create database backup with validation
# Returns: 0 on success, 1 on failure
backup_database() {
    log_info "Creating database backup..."

    # Check if backup script exists
    if ! file_exists "scripts/backup-db.sh"; then
        log_error "Backup script not found"
        return 1
    fi

    # Run backup
    if ./scripts/backup-db.sh > /tmp/backup.log 2>&1; then
        log_success "Database backup created successfully"
        return 0
    else
        log_error "Database backup failed"
        cat /tmp/backup.log
        return 1
    fi
}

# Verify backup exists and is recent
# Args: $1 = max age in minutes (default: 5)
# Returns: 0 if valid backup exists, 1 otherwise
verify_recent_backup() {
    local max_age_minutes="${1:-5}"
    local db_type=$(get_db_type)
    local backup_dir="database/backups"

    if [[ "$db_type" == "mysql" ]]; then
        backup_dir="database/backups/mysql"
    fi

    if ! dir_exists "$backup_dir"; then
        log_error "Backup directory not found: $backup_dir"
        return 1
    fi

    # Find most recent backup
    local latest_backup=$(find "$backup_dir" -name "backup_*.sql.gz" -o -name "backup_*.sqlite" 2>/dev/null | sort -r | head -1)

    if is_empty "$latest_backup"; then
        log_error "No backup files found"
        return 1
    fi

    # Check backup age
    local backup_age=$(($(date +%s) - $(stat -f %m "$latest_backup" 2>/dev/null || stat -c %Y "$latest_backup")))
    local max_age_seconds=$((max_age_minutes * 60))

    if [[ $backup_age -gt $max_age_seconds ]]; then
        log_error "Latest backup is too old ($(($backup_age / 60)) minutes)"
        return 1
    fi

    log_success "Valid backup found: $(basename "$latest_backup") ($(($backup_age / 60)) minutes old)"
    return 0
}

# ==============================================================================
# GIT OPERATIONS
# ==============================================================================

# Pull latest code from specified branch
# Args: $1 = branch name (default: dev)
# Returns: 0 on success, 1 on failure
git_pull_branch() {
    local branch="${1:-dev}"

    log_info "Pulling latest code from $branch..."

    # Check if we're on the correct branch
    local current_branch=$(get_git_branch)
    if [[ "$current_branch" != "$branch" ]]; then
        log_warning "Currently on branch '$current_branch', switching to '$branch'"
        if ! git checkout "$branch" 2>&1; then
            log_error "Failed to switch to branch $branch"
            return 1
        fi
    fi

    # Pull latest changes
    if git pull origin "$branch" 2>&1; then
        log_success "Code updated successfully"
        return 0
    else
        log_error "Failed to pull latest code"
        return 1
    fi
}

# Check for uncommitted changes
# Returns: 0 if clean, 1 if dirty
git_is_clean() {
    if [[ -z $(git status --porcelain) ]]; then
        return 0
    else
        return 1
    fi
}

# ==============================================================================
# DEPENDENCY MANAGEMENT
# ==============================================================================

# Install PHP dependencies with Composer
# Returns: 0 on success, 1 on failure
install_php_dependencies() {
    log_info "Installing PHP dependencies..."

    require_command composer

    # Determine composer flags based on environment.
    # --no-dev strips dev packages (Telescope, IdeHelper, etc.) — these are
    # conditionally registered in AppServiceProvider::register() via
    # class_exists(..., false) guards, so stripping them is safe as long as
    # bootstrap/cache/packages.php is cleared first (deploy_code_workflow
    # handles that via a direct rm before this step runs).
    local composer_flags="--no-dev --optimize-autoloader --no-interaction"

    # Add platform requirements workaround for cPanel. Prefer targeted
    # ignores over --ignore-platform-reqs (which hides other real problems).
    if is_cpanel_server; then
        composer_flags="$composer_flags --ignore-platform-req=ext-exif"
        log_info "cPanel detected: ignoring ext-exif platform requirement"
    fi

    # Run composer install
    if composer install $composer_flags 2>&1; then
        log_success "PHP dependencies installed"
        return 0
    else
        log_error "Composer install failed"
        return 1
    fi
}

# ==============================================================================
# CACHE OPERATIONS
# ==============================================================================

# Clear all Laravel caches
# Returns: 0 on success, 1 on failure
clear_caches() {
    log_info "Clearing application caches..."

    local failed=0

    # Clear each cache type
    php artisan cache:clear 2>&1 || ((failed++))
    php artisan config:clear 2>&1 || ((failed++))
    php artisan route:clear 2>&1 || ((failed++))
    php artisan view:clear 2>&1 || ((failed++))

    if [[ $failed -eq 0 ]]; then
        log_success "All caches cleared"
        return 0
    else
        log_warning "$failed cache clear operations failed"
        return 1
    fi
}

# Rebuild config cache
# Returns: 0 on success, 1 on failure
rebuild_config_cache() {
    log_info "Rebuilding config cache..."

    if php artisan config:cache 2>&1; then
        log_success "Config cache rebuilt"
        return 0
    else
        log_error "Failed to rebuild config cache"
        return 1
    fi
}

# ==============================================================================
# DATABASE OPERATIONS
# ==============================================================================

# Run database migrations
# Returns: 0 on success, 1 on failure
run_migrations() {
    log_info "Running database migrations..."

    if php artisan migrate --force 2>&1; then
        log_success "Migrations completed"
        return 0
    else
        log_error "Migration failed"
        return 1
    fi
}

# Check if migrations are pending
# Returns: 0 if pending migrations exist, 1 otherwise
has_pending_migrations() {
    local status=$(php artisan migrate:status 2>/dev/null)
    if echo "$status" | grep -q "Pending"; then
        return 0
    else
        return 1
    fi
}

# Seed database with specific seeder
# Args: $1 = seeder class name
# Returns: 0 on success, 1 on failure
run_seeder() {
    local seeder="$1"

    log_info "Running seeder: $seeder..."

    if php artisan db:seed --class="$seeder" --force 2>&1; then
        log_success "Seeder completed: $seeder"
        return 0
    else
        log_warning "Seeder failed or not found: $seeder"
        return 1
    fi
}

# Check if roles table is empty
# Returns: 0 if empty, 1 if has data
roles_table_empty() {
    local count=$(php artisan tinker --execute="echo DB::table('roles')->count();" 2>/dev/null | grep -o '[0-9]*' | tail -1)

    if is_empty "$count" || [[ "$count" -eq 0 ]]; then
        return 0
    else
        return 1
    fi
}

# ==============================================================================
# QUEUE WORKER OPERATIONS
# ==============================================================================

# Stop queue worker (screen or supervisor)
# Returns: 0 on success
stop_queue_worker() {
    log_info "Stopping queue worker..."

    # Get environment-specific session name
    local app_env=$(get_app_env)
    local session_name="queue-worker-${app_env}"

    if has_supervisor; then
        # Try Supervisor first
        if sudo supervisorctl stop showprima-queue-worker:* 2>/dev/null; then
            log_success "Queue worker stopped (Supervisor)"
            return 0
        fi
    fi

    # Try screen session with environment-specific name
    if screen_session_exists "$session_name"; then
        screen -S "$session_name" -X quit 2>/dev/null || true
        sleep 1
        log_success "Queue worker stopped (screen: $session_name)"
        return 0
    fi

    # Kill running processes (only from this project)
    if is_queue_worker_running; then
        pkill -f "${PROJECT_ROOT:-$(pwd)}.*queue:work" || true
        sleep 1
        log_success "Queue worker stopped (pkill)"
        return 0
    fi

    log_info "No queue worker was running"
    return 0
}

# Start queue worker (screen or supervisor)
# Returns: 0 on success, 1 on failure
start_queue_worker() {
    log_info "Starting queue worker..."

    # Get environment-specific session name (prod, staging, local, etc.)
    local app_env=$(get_app_env)
    local session_name="queue-worker-${app_env}"

    # Detect correct PHP binary (CLI, not CGI)
    local php_bin="php"
    if [ -f "/opt/cpanel/ea-php82/root/usr/bin/php" ]; then
        php_bin="/opt/cpanel/ea-php82/root/usr/bin/php"
    elif [ -f "/opt/cpanel/ea-php81/root/usr/bin/php" ]; then
        php_bin="/opt/cpanel/ea-php81/root/usr/bin/php"
    fi

    # Queue configuration with all queues including emails and bulk
    local queue_config="--queue=critical,emails,default,tickets,bulk"
    local worker_cmd="$php_bin artisan queue:work database $queue_config --tries=3 --timeout=600 --sleep=3 --max-time=3600 --max-jobs=500 --verbose"

    if has_supervisor; then
        # Try Supervisor first
        if sudo supervisorctl start showprima-queue-worker:* 2>/dev/null; then
            sleep 1
            if is_queue_worker_running; then
                log_success "Queue worker started (Supervisor)"
                return 0
            fi
        fi
    fi

    # Fall back to screen session
    if has_screen; then
        local project_root=$(pwd)
        screen -dmS "$session_name" bash -c "cd $project_root && $worker_cmd"
        sleep 2

        if screen_session_exists "$session_name"; then
            log_success "Queue worker started (screen: $session_name)"
            log_info "View with: screen -r $session_name"
            return 0
        else
            log_error "Failed to start queue worker in screen"
            return 1
        fi
    else
        log_error "Neither Supervisor nor screen is available"
        return 1
    fi
}

# Restart queue worker
# Returns: 0 on success, 1 on failure
restart_queue_worker() {
    stop_queue_worker
    sleep 2
    start_queue_worker
}

# ==============================================================================
# ENV VALIDATION
# ==============================================================================

# Validate seeder passwords exist and are secure
# Returns: 0 if valid, 1 if invalid
validate_seeder_passwords() {
    log_info "Validating seeder passwords..."

    local failed=0
    local min_length=12

    # Check super admin password
    local super_admin_pw=$(read_env "SEED_SUPER_ADMIN_PASSWORD")
    if is_empty "$super_admin_pw"; then
        log_error "SEED_SUPER_ADMIN_PASSWORD not set in .env"
        ((failed++))
    elif [[ ${#super_admin_pw} -lt $min_length ]]; then
        log_error "SEED_SUPER_ADMIN_PASSWORD too short (minimum $min_length characters)"
        ((failed++))
    fi

    # Check staff password
    local staff_pw=$(read_env "SEED_STAFF_PASSWORD")
    if is_empty "$staff_pw"; then
        log_error "SEED_STAFF_PASSWORD not set in .env"
        ((failed++))
    elif [[ ${#staff_pw} -lt $min_length ]]; then
        log_error "SEED_STAFF_PASSWORD too short (minimum $min_length characters)"
        ((failed++))
    fi

    # Check MBS admin password
    local mbs_pw=$(read_env "SEED_MBS_ADMIN_PASSWORD")
    if is_empty "$mbs_pw"; then
        log_error "SEED_MBS_ADMIN_PASSWORD not set in .env"
        ((failed++))
    elif [[ ${#mbs_pw} -lt $min_length ]]; then
        log_error "SEED_MBS_ADMIN_PASSWORD too short (minimum $min_length characters)"
        ((failed++))
    fi

    if [[ $failed -eq 0 ]]; then
        log_success "All seeder passwords are valid"
        return 0
    else
        log_error "$failed seeder password validation(s) failed"
        log_info "Add these to your .env file:"
        log_info "  SEED_SUPER_ADMIN_PASSWORD=YourSecurePassword123!"
        log_info "  SEED_STAFF_PASSWORD=YourSecurePassword456!"
        log_info "  SEED_MBS_ADMIN_PASSWORD=YourSecurePassword789!"
        return 1
    fi
}

# ==============================================================================
# PHP ENVIRONMENT SETUP
# ==============================================================================

# Setup PHP 8.1 on cPanel servers
# Returns: 0 on success
setup_php_environment() {
    if is_cpanel_server && [[ -f "/opt/cpanel/ea-php81/root/usr/bin/php" ]]; then
        export PATH="/opt/cpanel/ea-php81/root/usr/bin:$PATH"
        local php_version=$(get_php_version)
        log_success "Using PHP $php_version"
        return 0
    fi

    local php_version=$(get_php_version)
    log_info "Using PHP $php_version"
    return 0
}

# ==============================================================================
# EXPORT FUNCTIONS
# ==============================================================================

export -f backup_database verify_recent_backup
export -f git_pull_branch git_is_clean
export -f install_php_dependencies
export -f clear_caches rebuild_config_cache
export -f run_migrations has_pending_migrations run_seeder roles_table_empty
export -f stop_queue_worker start_queue_worker restart_queue_worker
export -f validate_seeder_passwords setup_php_environment
