#!/bin/bash
# Desk-Moloni v3.0 OAuth Token Refresh Script
# 
# Automatically refreshes OAuth tokens before expiration to maintain
# continuous API connectivity without manual intervention.

set -euo pipefail

# Script configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODULE_DIR="$(dirname "$SCRIPT_DIR")"
CLI_DIR="$MODULE_DIR/cli"
LOG_DIR="$MODULE_DIR/logs"
LOCK_FILE="$MODULE_DIR/locks/token_refresh.lock"

# Configuration
REFRESH_THRESHOLD=300  # Refresh 5 minutes before expiry
MAX_ATTEMPTS=3
RETRY_DELAY=60

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

# Logging functions
log_info() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [INFO] $1"
    echo -e "${BLUE}$message${NC}"
    echo "$message" >> "$LOG_DIR/token_refresh.log" 2>/dev/null || true
}

log_success() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [SUCCESS] $1"
    echo -e "${GREEN}$message${NC}"
    echo "$message" >> "$LOG_DIR/token_refresh.log" 2>/dev/null || true
}

log_warning() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [WARNING] $1"
    echo -e "${YELLOW}$message${NC}"
    echo "$message" >> "$LOG_DIR/token_refresh.log" 2>/dev/null || true
}

log_error() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [ERROR] $1"
    echo -e "${RED}$message${NC}"
    echo "$message" >> "$LOG_DIR/token_refresh.log" 2>/dev/null || true
}

# Help function
show_help() {
    cat << EOF
Desk-Moloni v3.0 OAuth Token Refresh Script

Usage: $0 [OPTIONS]

Options:
    -h, --help                    Show this help message
    -t, --threshold SECONDS       Refresh threshold in seconds (default: $REFRESH_THRESHOLD)
    -a, --attempts COUNT          Maximum retry attempts (default: $MAX_ATTEMPTS)
    -d, --delay SECONDS          Retry delay in seconds (default: $RETRY_DELAY)
    --dry-run                    Show what would be done without changes
    --force                      Force refresh even if not needed
    --check-only                 Only check token status, don't refresh

Description:
    This script automatically checks OAuth token expiration and refreshes
    tokens when they are close to expiring. It's designed to run as a cron
    job to maintain continuous API connectivity.

    The script will:
    1. Check current token expiration time
    2. Compare against refresh threshold
    3. Attempt to refresh if needed
    4. Retry on failures with exponential backoff
    5. Log all activities for monitoring

Examples:
    $0                          # Normal token refresh check
    $0 --force                  # Force refresh regardless of expiration
    $0 --check-only             # Just check status, don't refresh
    $0 --dry-run                # Preview what would be done

EOF
}

# Parse command line arguments
DRY_RUN=false
FORCE_REFRESH=false
CHECK_ONLY=false

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -t|--threshold)
            REFRESH_THRESHOLD="$2"
            shift 2
            ;;
        -a|--attempts)
            MAX_ATTEMPTS="$2"
            shift 2
            ;;
        -d|--delay)
            RETRY_DELAY="$2"
            shift 2
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --force)
            FORCE_REFRESH=true
            shift
            ;;
        --check-only)
            CHECK_ONLY=true
            shift
            ;;
        *)
            log_error "Unknown option: $1"
            show_help
            exit 1
            ;;
    esac
done

# Ensure required directories exist
ensure_directories() {
    local dirs=("$LOG_DIR" "$(dirname "$LOCK_FILE")")
    
    for dir in "${dirs[@]}"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir" 2>/dev/null || true
        fi
    done
}

# Get token information using PHP
get_token_info() {
    local token_info
    
    if ! token_info=$(php -r "
        require_once '$MODULE_DIR/config/bootstrap.php';
        
        try {
            // Load configuration service
            require_once '$MODULE_DIR/src/Services/ConfigService.php';
            \$configService = new DeskMoloni\Services\ConfigService();
            
            // Get token information
            \$accessToken = \$configService->get('oauth_access_token');
            \$refreshToken = \$configService->get('oauth_refresh_token');
            \$expiresAt = \$configService->get('oauth_expires_at');
            
            if (empty(\$accessToken)) {
                echo 'NO_TOKEN';
                exit(0);
            }
            
            \$currentTime = time();
            \$expiryTime = \$expiresAt ? (int)\$expiresAt : 0;
            \$timeUntilExpiry = \$expiryTime - \$currentTime;
            
            // Output format: STATUS|EXPIRES_IN|HAS_REFRESH_TOKEN
            echo 'VALID|' . \$timeUntilExpiry . '|' . (!empty(\$refreshToken) ? '1' : '0');
            
        } catch (Exception \$e) {
            echo 'ERROR|' . \$e->getMessage();
        }
    " 2>/dev/null); then
        log_error "Failed to get token information"
        return 1
    fi
    
    echo "$token_info"
}

# Check if token needs refresh
needs_refresh() {
    local token_info
    token_info=$(get_token_info)
    
    if [[ "$token_info" == "NO_TOKEN" ]]; then
        log_warning "No OAuth token found"
        return 2  # Special case: no token at all
    fi
    
    if [[ "$token_info" =~ ^ERROR\| ]]; then
        log_error "Error checking token: ${token_info#ERROR|}"
        return 1
    fi
    
    IFS='|' read -r status expires_in has_refresh <<< "$token_info"
    
    if [[ "$status" != "VALID" ]]; then
        log_error "Token is not valid: $status"
        return 1
    fi
    
    log_info "Token expires in ${expires_in} seconds"
    
    # Check if we have a refresh token
    if [[ "$has_refresh" != "1" ]]; then
        log_error "No refresh token available"
        return 1
    fi
    
    # Check if refresh is needed
    if [[ "$FORCE_REFRESH" == true ]]; then
        log_info "Force refresh requested"
        return 0
    fi
    
    if [[ "$expires_in" -le "$REFRESH_THRESHOLD" ]]; then
        log_info "Token needs refresh (expires in ${expires_in}s, threshold: ${REFRESH_THRESHOLD}s)"
        return 0
    fi
    
    log_info "Token refresh not needed (expires in ${expires_in}s)"
    return 3  # No refresh needed
}

# Perform token refresh
refresh_token() {
    local attempt=1
    
    while [[ $attempt -le $MAX_ATTEMPTS ]]; do
        log_info "Token refresh attempt $attempt/$MAX_ATTEMPTS"
        
        if [[ "$DRY_RUN" == true ]]; then
            log_info "Would attempt to refresh OAuth token"
            return 0
        fi
        
        # Attempt refresh using PHP
        local refresh_result
        if refresh_result=$(php -r "
            require_once '$MODULE_DIR/config/bootstrap.php';
            
            try {
                require_once '$MODULE_DIR/src/Services/AuthService.php';
                \$authService = new DeskMoloni\Services\AuthService();
                
                \$result = \$authService->refreshToken();
                
                if (\$result['success']) {
                    echo 'SUCCESS|New token expires in ' . \$result['expires_in'] . ' seconds';
                } else {
                    echo 'FAILED|' . (\$result['error'] ?? 'Unknown error');
                }
                
            } catch (Exception \$e) {
                echo 'ERROR|' . \$e->getMessage();
            }
        " 2>/dev/null); then
            
            IFS='|' read -r result_status result_message <<< "$refresh_result"
            
            case "$result_status" in
                SUCCESS)
                    log_success "Token refreshed successfully: $result_message"
                    return 0
                    ;;
                FAILED)
                    log_error "Token refresh failed: $result_message"
                    ;;
                ERROR)
                    log_error "Error during token refresh: $result_message"
                    ;;
                *)
                    log_error "Unexpected refresh result: $refresh_result"
                    ;;
            esac
        else
            log_error "Failed to execute token refresh"
        fi
        
        # Increment attempt counter
        ((attempt++))
        
        # Wait before retry (exponential backoff)
        if [[ $attempt -le $MAX_ATTEMPTS ]]; then
            local wait_time=$((RETRY_DELAY * attempt))
            log_info "Retrying in ${wait_time} seconds..."
            sleep "$wait_time"
        fi
    done
    
    log_error "Token refresh failed after $MAX_ATTEMPTS attempts"
    return 1
}

# Send notification about token issues
send_notification() {
    local subject="$1"
    local message="$2"
    
    # Log the notification
    log_warning "NOTIFICATION: $subject - $message"
    
    # Try to send notification via PHP if notification system is configured
    php -r "
        require_once '$MODULE_DIR/config/bootstrap.php';
        
        try {
            require_once '$MODULE_DIR/src/Services/NotificationService.php';
            \$notificationService = new DeskMoloni\Services\NotificationService();
            \$notificationService->sendAlert('$subject', '$message');
        } catch (Exception \$e) {
            // Notification service may not be configured, that's OK
        }
    " 2>/dev/null || true
}

# Check token status and report
check_token_status() {
    local token_info
    token_info=$(get_token_info)
    
    log_info "=== TOKEN STATUS REPORT ==="
    
    case "$token_info" in
        NO_TOKEN)
            log_error "❌ No OAuth token configured"
            log_info "Please configure OAuth credentials in the admin panel"
            return 1
            ;;
        ERROR*)
            log_error "❌ Error checking token: ${token_info#ERROR|}"
            return 1
            ;;
        VALID*)
            IFS='|' read -r status expires_in has_refresh <<< "$token_info"
            
            local hours=$((expires_in / 3600))
            local minutes=$(((expires_in % 3600) / 60))
            
            if [[ "$expires_in" -gt "$REFRESH_THRESHOLD" ]]; then
                log_success "✅ Token is valid and fresh"
                log_info "   Expires in: ${hours}h ${minutes}m"
                log_info "   Refresh token: $([ "$has_refresh" == "1" ] && echo "Available" || echo "Missing")"
            elif [[ "$expires_in" -gt 0 ]]; then
                log_warning "⚠️ Token expires soon"
                log_info "   Expires in: ${hours}h ${minutes}m"
                log_info "   Refresh token: $([ "$has_refresh" == "1" ] && echo "Available" || echo "Missing")"
            else
                log_error "❌ Token has expired"
                log_info "   Expired: $((-expires_in)) seconds ago"
                log_info "   Refresh token: $([ "$has_refresh" == "1" ] && echo "Available" || echo "Missing")"
            fi
            
            return 0
            ;;
        *)
            log_error "❌ Unknown token status: $token_info"
            return 1
            ;;
    esac
}

# Main execution function
main() {
    log_info "Starting OAuth token refresh check"
    
    # Check if only status check is requested
    if [[ "$CHECK_ONLY" == true ]]; then
        check_token_status
        exit $?
    fi
    
    # Check if token needs refresh
    local refresh_needed=0
    needs_refresh || refresh_needed=$?
    
    case $refresh_needed in
        0)  # Needs refresh
            if refresh_token; then
                log_success "Token refresh completed successfully"
                
                # Verify the new token
                local new_token_info
                new_token_info=$(get_token_info)
                if [[ "$new_token_info" =~ ^VALID\|([0-9]+)\| ]]; then
                    local new_expires_in="${BASH_REMATCH[1]}"
                    local new_hours=$((new_expires_in / 3600))
                    log_success "New token expires in ${new_hours} hours"
                fi
            else
                log_error "Token refresh failed"
                send_notification "OAuth Token Refresh Failed" "Failed to refresh Moloni API token after $MAX_ATTEMPTS attempts. Manual intervention may be required."
                exit 1
            fi
            ;;
        1)  # Error
            log_error "Error checking token refresh requirements"
            exit 1
            ;;
        2)  # No token
            log_warning "No OAuth token configured - skipping refresh"
            send_notification "OAuth Token Missing" "No OAuth token is configured for Moloni API. Please configure OAuth credentials."
            exit 0
            ;;
        3)  # No refresh needed
            log_info "Token refresh not required at this time"
            exit 0
            ;;
        *)
            log_error "Unexpected error code: $refresh_needed"
            exit 1
            ;;
    esac
}

# Cleanup function
cleanup() {
    # Remove lock file if it exists and we created it
    if [[ -f "$LOCK_FILE" ]] && [[ "${LOCK_ACQUIRED:-0}" == "1" ]]; then
        rm -f "$LOCK_FILE"
    fi
}

# Set up cleanup trap
trap cleanup EXIT

# Acquire lock to prevent concurrent execution
acquire_lock() {
    if [[ -f "$LOCK_FILE" ]]; then
        local lock_pid
        lock_pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
        
        if [[ -n "$lock_pid" ]] && kill -0 "$lock_pid" 2>/dev/null; then
            log_warning "Another token refresh process is running (PID: $lock_pid)"
            exit 0
        else
            log_info "Removing stale lock file"
            rm -f "$LOCK_FILE"
        fi
    fi
    
    echo $$ > "$LOCK_FILE"
    export LOCK_ACQUIRED=1
    log_info "Lock acquired (PID: $$)"
}

# Initialize and run
initialize() {
    ensure_directories
    acquire_lock
    main "$@"
}

# Execute if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    initialize "$@"
fi