#!/bin/bash
# =============================================================================
# File: 5b_remote_copy_rsync_modified.sh
# Deskripsi: Menyalin file ke server remote menggunakan RSYNC/SCP (Auto-Fallback)
# Author: Modified from original by bil-awal
# =============================================================================

# Konfigurasi default
DEFAULT_PORT=22
DEFAULT_RSYNC_OPTIONS="-ahz --info=progress2"
DEFAULT_SCP_OPTIONS="-r -p -v"
SSH_TIMEOUT=10

# Global variable untuk transfer method
TRANSFER_METHOD=""

# Fungsi untuk menampilkan cara penggunaan
usage() {
    echo "Penggunaan: $0 <file_source> <username> <ip_address> [port] [destination_path]"
    echo ""
    echo "Parameter:"
    echo "  file_source      - File atau direktori yang akan disalin"
    echo "  username         - Username di server remote"
    echo "  ip_address       - IP address server tujuan"
    echo "  port (opsional)  - Port SSH (default: 22)"
    echo "  destination_path (opsional) - Path tujuan (default: /home/username/)"
    echo ""
    echo "Contoh:"
    echo "  $0 /home/user/document.txt admin 192.168.1.100"
    echo "  $0 /var/log/myapp/ deploy 10.0.0.5 2222"
    echo "  $0 backup.tar.gz user 192.168.1.50 22 /tmp/"
    exit 1
}

# Fungsi logging
log() {
    local level="$1"
    shift
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $*"
}

# Validasi jumlah parameter
if [ "$#" -lt 3 ] || [ "$#" -gt 5 ]; then
    echo "Error: Jumlah parameter tidak sesuai!"
    usage
fi

# Variabel
SOURCE_PATH="$1"
REMOTE_USER="$2"
REMOTE_IP="$3"
REMOTE_PORT="${4:-$DEFAULT_PORT}"
REMOTE_DEST="${5:-/home/$REMOTE_USER/}"
RSYNC_OPTIONS="${RSYNC_OPTIONS:-$DEFAULT_RSYNC_OPTIONS}"
SCP_OPTIONS="${SCP_OPTIONS:-$DEFAULT_SCP_OPTIONS}"

# Fungsi untuk validasi input yang lebih baik
validate_inputs() {
    # Validasi source path
    if [ ! -e "$SOURCE_PATH" ]; then
        log "ERROR" "File atau direktori '$SOURCE_PATH' tidak ditemukan!"
        exit 1
    fi
    
    # Validasi username tidak kosong
    if [ -z "$REMOTE_USER" ]; then
        log "ERROR" "Username tidak boleh kosong!"
        exit 1
    fi
    
    # Validasi port (harus numerik dan dalam range valid)
    if ! [[ "$REMOTE_PORT" =~ ^[0-9]+$ ]] || [ "$REMOTE_PORT" -lt 1 ] || [ "$REMOTE_PORT" -gt 65535 ]; then
        log "ERROR" "Port '$REMOTE_PORT' tidak valid! Harus antara 1-65535."
        exit 1
    fi
    
    # Validasi format IP address atau hostname
    if echo "$REMOTE_IP" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then
        # Validasi IP address lebih detail
        IFS='.' read -ra ADDR <<< "$REMOTE_IP"
        for i in "${ADDR[@]}"; do
            if [ "$i" -gt 255 ]; then
                log "ERROR" "IP address '$REMOTE_IP' tidak valid!"
                exit 1
            fi
        done
    else
        log "INFO" "'$REMOTE_IP' terdeteksi sebagai hostname/FQDN."
    fi
}

# Fungsi untuk cek dependencies dan pilih method
select_transfer_method() {
    # Cek SSH (critical)
    if ! command -v ssh &> /dev/null; then
        log "ERROR" "SSH client tidak ditemukan! Install openssh-client."
        exit 1
    fi
    
    # Cek rsync atau scp
    if command -v rsync &> /dev/null; then
        TRANSFER_METHOD="rsync"
        log "SUCCESS" "RSYNC tersedia - menggunakan rsync"
    elif command -v scp &> /dev/null; then
        TRANSFER_METHOD="scp"
        log "WARNING" "RSYNC tidak tersedia - fallback ke SCP"
    else
        log "ERROR" "Baik rsync maupun scp tidak tersedia!"
        exit 1
    fi
    
    # Check optional commands
    if ! command -v timeout &> /dev/null; then
        log "WARNING" "Command 'timeout' tidak tersedia - menggunakan default timeout"
    fi
}

# Fungsi untuk cek konektivitas SSH yang robust
check_ssh_connectivity() {
    log "INFO" "Memeriksa koneksi SSH ke $REMOTE_USER@$REMOTE_IP:$REMOTE_PORT..."
    
    # Prepare SSH options
    local ssh_opts="-p $REMOTE_PORT -o ConnectTimeout=5 -o BatchMode=yes -o LogLevel=ERROR"
    local ssh_cmd="ssh $ssh_opts $REMOTE_USER@$REMOTE_IP exit"
    
    # Test SSH connectivity dengan atau tanpa timeout
    if command -v timeout &> /dev/null; then
        ssh_cmd="timeout $SSH_TIMEOUT $ssh_cmd"
    fi
    
    if $ssh_cmd 2>/dev/null; then
        log "SUCCESS" "SSH connection berhasil ke $REMOTE_IP:$REMOTE_PORT"
        return 0
    else
        log "WARNING" "SSH key-based authentication gagal. Kemungkinan penyebab:"
        echo "  - SSH service tidak berjalan pada port $REMOTE_PORT"
        echo "  - SSH keys belum dikonfigurasi"
        echo "  - Authentication memerlukan password"
        echo "  - Server tidak dapat dijangkau"
        
        read -p "Lanjutkan dengan password authentication? (y/n): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            exit 1
        fi
        return 1
    fi
}

# Fungsi untuk cek SSH key dengan lebih detail
check_ssh_auth() {
    local key_types=("rsa" "ed25519" "ecdsa" "dsa")
    local found_keys=()
    
    for key_type in "${key_types[@]}"; do
        local key_file="$HOME/.ssh/id_$key_type"
        if [ -f "$key_file" ]; then
            found_keys+=("$key_type")
        fi
    done
    
    if [ ${#found_keys[@]} -gt 0 ]; then
        log "SUCCESS" "SSH keys ditemukan: ${found_keys[*]}"
    else
        log "INFO" "SSH key tidak ditemukan. Password authentication akan digunakan."
        echo "Tips: Gunakan 'ssh-keygen -t ed25519' untuk membuat SSH key pair."
    fi
    
    # Test SSH agent
    if ssh-add -l &>/dev/null; then
        log "INFO" "SSH agent aktif dengan $(ssh-add -l | wc -l) key(s) loaded."
    fi
}

# Fungsi untuk mendapatkan info file/direktori yang detail
get_source_info() {
    local size type file_count dir_count
    
    if [ -f "$SOURCE_PATH" ]; then
        type="File"
        size=$(du -h "$SOURCE_PATH" | cut -f1)
        file_count="1"
        dir_count="0"
    elif [ -d "$SOURCE_PATH" ]; then
        type="Direktori"
        size=$(du -sh "$SOURCE_PATH" 2>/dev/null | cut -f1)
        file_count=$(find "$SOURCE_PATH" -type f 2>/dev/null | wc -l)
        dir_count=$(find "$SOURCE_PATH" -type d 2>/dev/null | wc -l)
        dir_count=$((dir_count - 1))  # Exclude source directory itself
    else
        type="Unknown"
        size="N/A"
        file_count="N/A"
        dir_count="N/A"
    fi
    
    echo "Tipe: $type"
    echo "Ukuran: $size"
    echo "Files: $file_count"
    if [ "$dir_count" != "0" ] && [ "$dir_count" != "N/A" ]; then
        echo "Directories: $dir_count"
    fi
    echo "Path: $SOURCE_PATH"
}

# Fungsi untuk memilih mode transfer (adapted untuk SCP)
select_transfer_mode() {
    echo ""
    echo "Method: $TRANSFER_METHOD"
    
    if [ "$TRANSFER_METHOD" = "rsync" ]; then
        echo "Pilih mode sinkronisasi RSYNC:"
        echo "1. Normal     - Copy file baru dan update yang berubah"
        echo "2. Mirror     - Sinkronisasi penuh (hapus file di tujuan yang tidak ada di source)"
        echo "3. Backup     - Normal + backup file yang akan dioverwrite"
        echo "4. Checksum   - Validasi dengan checksum (lebih akurat, lebih lambat)"
        echo "5. Dry-run    - Simulasi saja, tidak melakukan transfer"
        echo "6. Custom     - Masukkan opsi rsync sendiri"
        echo ""
        read -p "Pilih mode (1-6) [default: 1]: " mode_choice
        
        case "$mode_choice" in
            2)
                RSYNC_OPTIONS="$RSYNC_OPTIONS --delete"
                log "INFO" "Mode: Mirror (dengan --delete)"
                ;;
            3)
                local backup_dir="backup_$(date +%Y%m%d_%H%M%S)"
                RSYNC_OPTIONS="$RSYNC_OPTIONS --backup --backup-dir=$backup_dir"
                log "INFO" "Mode: Backup (backup dir: $backup_dir)"
                ;;
            4)
                RSYNC_OPTIONS="$RSYNC_OPTIONS --checksum"
                log "INFO" "Mode: Checksum validation"
                ;;
            5)
                RSYNC_OPTIONS="$RSYNC_OPTIONS --dry-run"
                log "INFO" "Mode: Dry-run (simulasi)"
                ;;
            6)
                echo "Opsi rsync saat ini: $RSYNC_OPTIONS"
                read -p "Masukkan opsi tambahan: " custom_opts
                if [ -n "$custom_opts" ]; then
                    RSYNC_OPTIONS="$RSYNC_OPTIONS $custom_opts"
                fi
                log "INFO" "Mode: Custom"
                ;;
            *)
                log "INFO" "Mode: Normal"
                ;;
        esac
    else
        echo "Mode SCP: Normal copy (SCP tidak mendukung advanced sync modes)"
        echo "Options yang tersedia untuk SCP:"
        echo "  -r: Recursive (untuk direktori)"
        echo "  -p: Preserve timestamps dan permissions"  
        echo "  -v: Verbose output"
        log "INFO" "Mode: SCP Normal"
    fi
}

# Fungsi untuk bandwidth limiting (works untuk both rsync dan scp)
set_bandwidth_limit() {
    echo ""
    read -p "Apakah ingin membatasi bandwidth? (y/n): " -n 1 -r
    echo
    
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        if [ "$TRANSFER_METHOD" = "rsync" ]; then
            echo "Contoh: 1000 (KB/s), 5M (MB/s)"
            read -p "Masukkan limit bandwidth: " bw_limit
            if [ -n "$bw_limit" ]; then
                RSYNC_OPTIONS="$RSYNC_OPTIONS --bwlimit=$bw_limit"
                log "INFO" "RSYNC Bandwidth limit: $bw_limit"
            fi
        else
            echo "Contoh: 1000 (KB/s)"
            read -p "Masukkan limit bandwidth (KB/s): " bw_limit
            if [ -n "$bw_limit" ]; then
                SCP_OPTIONS="$SCP_OPTIONS -l $bw_limit"
                log "INFO" "SCP Bandwidth limit: ${bw_limit} KB/s"
            fi
        fi
    fi
}

# Fungsi untuk exclude patterns (hanya untuk rsync)
create_exclude_patterns() {
    if [ "$TRANSFER_METHOD" != "rsync" ]; then
        log "INFO" "Exclude patterns hanya tersedia untuk RSYNC"
        return
    fi
    
    local exclude_file="/tmp/rsync_exclude_$$"
    
    echo ""
    read -p "Apakah ada file/direktori yang ingin di-exclude? (y/n): " -n 1 -r
    echo
    
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        echo ""
        echo "Pilih exclude method:"
        echo "1. Manual input"
        echo "2. Common patterns (logs, cache, tmp files)"
        echo "3. Development patterns (node_modules, .git, build dirs)"
        read -p "Pilih (1-3): " exclude_method
        
        case "$exclude_method" in
            2)
                cat > "$exclude_file" << EOF
*.log
*.tmp
*.cache
*~
.DS_Store
Thumbs.db
EOF
                log "INFO" "Common exclude patterns applied"
                ;;
            3)
                cat > "$exclude_file" << EOF
node_modules/
.git/
.svn/
.hg/
__pycache__/
*.pyc
*.pyo
.pytest_cache/
target/
build/
dist/
.gradle/
.idea/
.vscode/
*.swp
*.swo
EOF
                log "INFO" "Development exclude patterns applied"
                ;;
            *)
                echo "Masukkan pattern yang ingin di-exclude (satu per baris, akhiri dengan CTRL+D):"
                echo "Contoh: *.log, node_modules/, .git/, __pycache__/"
                cat > "$exclude_file"
                ;;
        esac
        
        if [ -s "$exclude_file" ]; then
            RSYNC_OPTIONS="$RSYNC_OPTIONS --exclude-from=$exclude_file"
            echo "✓ Exclude patterns tersimpan."
            echo "Preview exclude patterns:"
            cat "$exclude_file" | sed 's/^/  - /'
        fi
    fi
}

# Fungsi untuk melakukan transfer dengan fallback
perform_transfer() {
    local source="$1"
    local destination="$2"
    local start_time=$(date +%s)
    
    echo ""
    log "INFO" "Memulai transfer menggunakan $TRANSFER_METHOD..."
    echo "Dari: $source"
    echo "Ke: $destination"
    echo "Port: $REMOTE_PORT"
    
    case "$TRANSFER_METHOD" in
        "rsync")
            # Tambahkan trailing slash untuk direktori (rsync behavior)
            if [ -d "$source" ]; then
                source="${source%/}/"
            fi
            
            echo "Options: $RSYNC_OPTIONS"
            echo ""
            
            # Build rsync command
            local rsync_cmd
            if [ "$REMOTE_PORT" != "22" ]; then
                rsync_cmd="rsync $RSYNC_OPTIONS -e \"ssh -p $REMOTE_PORT\" \"$source\" \"$destination\""
            else
                rsync_cmd="rsync $RSYNC_OPTIONS \"$source\" \"$destination\""
            fi
            
            if eval $rsync_cmd; then
                local end_time=$(date +%s)
                local duration=$((end_time - start_time))
                
                echo ""
                log "SUCCESS" "RSYNC transfer berhasil!"
                echo "Waktu transfer: ${duration} detik"
                
                # Cleanup exclude file jika ada
                if [[ "$RSYNC_OPTIONS" == *"--exclude-from"* ]]; then
                    rm -f /tmp/rsync_exclude_$$
                fi
                
                return 0
            else
                local exit_code=$?
                log "ERROR" "RSYNC transfer gagal dengan exit code: $exit_code"
                return $exit_code
            fi
            ;;
        "scp")
            echo "Options: $SCP_OPTIONS"
            echo ""
            
            # Build SCP command
            local scp_cmd
            if [ "$REMOTE_PORT" != "22" ]; then
                SCP_OPTIONS="$SCP_OPTIONS -P $REMOTE_PORT"
            fi
            
            scp_cmd="scp $SCP_OPTIONS \"$source\" \"$destination\""
            
            if eval $scp_cmd; then
                local end_time=$(date +%s)
                local duration=$((end_time - start_time))
                
                echo ""
                log "SUCCESS" "SCP transfer berhasil!"
                echo "Waktu transfer: ${duration} detik"
                return 0
            else
                local exit_code=$?
                log "ERROR" "SCP transfer gagal dengan exit code: $exit_code"
                return $exit_code
            fi
            ;;
    esac
}

# Fungsi untuk menampilkan tips optimasi
suggest_optimization() {
    echo ""
    if [ "$TRANSFER_METHOD" = "rsync" ]; then
        echo "💡 Tips RSYNC Optimization:"
        echo "📁 File besar: --partial --append-verify --inplace"
        echo "🌐 Network: --compress-level=6 --bwlimit=RATE"
        echo "💾 Backup: --backup --backup-dir=backup_\$(date +%Y%m%d)"
        echo "🔍 Validasi: --checksum (akurat) atau --size-only (cepat)"
        echo "📊 Monitoring: --progress --stats -v"
        echo "🚀 Performance: --whole-file (LAN) atau --no-whole-file (WAN)"
    else
        echo "💡 Tips SCP Optimization:"
        echo "📁 Compression: -C (enable compression)"
        echo "🌐 Network: -l LIMIT (bandwidth limit)"
        echo "🔐 Security: -o (SSH options)"
        echo "📊 Monitoring: -v (verbose output)"
    fi
}

# Fungsi untuk menampilkan transfer summary
show_transfer_summary() {
    echo ""
    echo "=========================================="
    echo "RINGKASAN TRANSFER ($TRANSFER_METHOD)"
    echo "=========================================="
    echo "Source: $SOURCE_PATH"
    echo "Destination: $DESTINATION"
    echo "Port: $REMOTE_PORT"
    echo "Method: $TRANSFER_METHOD"
    if [ "$TRANSFER_METHOD" = "rsync" ]; then
        echo "Options: $RSYNC_OPTIONS"
    else
        echo "Options: $SCP_OPTIONS"
    fi
    echo "=========================================="
}

# Main execution
main() {
    echo "=========================================="
    echo "Remote Copy (RSYNC/SCP Hybrid)"
    echo "=========================================="
    
    # Select transfer method
    select_transfer_method
    
    # Validasi input
    validate_inputs
    
    # Tampilkan informasi transfer
    echo ""
    echo "Informasi Transfer:"
    echo "-------------------"
    get_source_info
    echo "Tujuan: $REMOTE_USER@$REMOTE_IP:$REMOTE_DEST"
    echo "Port: $REMOTE_PORT"
    echo "Method: $TRANSFER_METHOD"
    
    # Cek konektivitas SSH
    check_ssh_connectivity
    
    # Cek SSH authentication
    check_ssh_auth
    
    # Pilih mode transfer
    select_transfer_mode
    
    # Set bandwidth limit
    set_bandwidth_limit
    
    # Tanyakan exclude patterns (hanya untuk rsync)
    create_exclude_patterns
    
    # Tampilkan tips optimasi
    suggest_optimization
    
    # Buat destination path
    DESTINATION="$REMOTE_USER@$REMOTE_IP:$REMOTE_DEST"
    
    # Tampilkan summary
    show_transfer_summary
    
    # Konfirmasi sebelum transfer
    echo ""
    read -p "Mulai transfer? (y/n): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        log "INFO" "Transfer dibatalkan oleh user."
        exit 0
    fi
    
    # Lakukan transfer
    if perform_transfer "$SOURCE_PATH" "$DESTINATION"; then
        echo ""
        echo "=========================================="
        log "SUCCESS" "File/direktori berhasil ditransfer!"
        echo "Lokasi: $DESTINATION"
        echo "=========================================="
        exit 0
    else
        echo ""
        echo "=========================================="
        log "ERROR" "Gagal melakukan transfer."
        echo "=========================================="
        exit 1
    fi
}

# Handle Ctrl+C gracefully
trap 'echo ""; log "INFO" "Transfer dibatalkan oleh user."; exit 1' INT

# Jalankan main function
main