#!/usr/bin/env bash

# Tralegraffe SD Card Verifier & Fixer - Versione macOS
# Versione 26.04 — Copyright (c) 2026 Tralegraffe Software Solution
# Licenza proprietaria — Tutti i diritti riservati
# Compatibile con macOS 10.13+ (High Sierra e successivi)

# ---- Colori e funzioni di output ----
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
cecho() { echo -e "${2}${1}${NC}"; }
info() { cecho "[ℹ️ ] $1" "$YELLOW"; }
warn() { cecho "[⚠️ ] $1" "$RED"; }
err()  { cecho "[❌] $1" "$RED"; }
ok()   { cecho "[✅] $1" "$GREEN"; }

# ---- Barra di avanzamento ----
progress_bar() {
    local progress=$1
    local total=$2
    local bar_length=30
    local filled=$((progress * bar_length / total))
    local bar=""
    for ((i=0; i<bar_length; i++)); do
        if [[ $i -lt $filled ]]; then
            bar+="█"
        else
            bar+="░"
        fi
    done
    printf "\rProgress: [%s] %d%%" "$bar" $((progress * 100 / total))
    if [[ $progress -eq $total ]]; then
        echo ""
    fi
}

# ---- Schermata di benvenuto ----
welcome_screen() {
    cecho "\n🔍 Tralegraffe Software Solution - SD Card Verifier & Fixer 🔍" "$GREEN"
    cecho "🇮🇹/🇬🇧 SD/USB Card Authenticity & Repair Tool" "$YELLOW"
    cecho "---------------------------------------------" "$YELLOW"
    cecho "✅ Verifica, rileva e corregge capacità false" "$GREEN"
    cecho "♻️  Recupera schede danneggiate con capacità reale" "$GREEN"
    cecho "---------------------------------------------\n" "$YELLOW"
}

# ---- Controllo privilegi root ----
if [[ "$(id -u)" -ne 0 ]]; then
    err "Questo script richiede privilegi di root. Rilancia con sudo."
    exit 1
fi

# ---- Controllo strumenti necessari ----
NEEDED_TOOLS=(diskutil cmp dd curl)
MISSING=()
for t in "${NEEDED_TOOLS[@]}"; do
    if ! command -v "$t" &>/dev/null; then
        MISSING+=("$t")
    fi
done
if [[ ${#MISSING[@]} -gt 0 ]]; then
    err "Strumenti mancanti: ${MISSING[*]}"
    warn "Installa Xcode Command Line Tools con: xcode-select --install"
    exit 1
fi

# ======================================================================
# ---- SISTEMA DI LICENZE ----
# ======================================================================
LICENSE_API="https://tralegraffe.it/license_api.php"
PRODUCT_CODE="SDVERIFIER-MACOS"
APP_VERSION="26.04"
PLATFORM="macos"

# File licenza nella home dell'utente reale (non root)
if [[ -n "$SUDO_USER" ]]; then
    LICENSE_FILE="/Users/$SUDO_USER/.sdverifier_license"
else
    LICENSE_FILE="$HOME/.sdverifier_license"
fi

# Genera un device_id stabile basato sull'hardware del Mac
get_device_id() {
    local serial
    serial=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}')
    if [[ -z "$serial" ]]; then
        serial=$(hostname)
    fi
    echo "MAC-${serial}"
}

DEVICE_ID=$(get_device_id)

# Salva la chiave licenza
save_license_key() {
    echo "$1" > "$LICENSE_FILE"
    chmod 600 "$LICENSE_FILE"
}

# Legge la chiave licenza salvata
load_license_key() {
    if [[ -f "$LICENSE_FILE" ]]; then
        cat "$LICENSE_FILE"
    fi
}

# Chiama l'API licenze
license_api_call() {
    local payload="$1"
    local response
    response=$(curl -s -m 15 -X POST \
        -H "Content-Type: application/json" \
        -d "$payload" \
        "$LICENSE_API" 2>/dev/null)
    echo "$response"
}

# Valida la licenza
validate_license() {
    local key="$1"
    local payload="{\"action\":\"validate\",\"product_code\":\"$PRODUCT_CODE\",\"license_key\":\"$key\",\"device_id\":\"$DEVICE_ID\",\"app_version\":\"$APP_VERSION\",\"platform\":\"$PLATFORM\"}"
    local response
    response=$(license_api_call "$payload")
    if [[ -z "$response" ]]; then
        warn "Impossibile contattare il server licenze. Riprova più tardi."
        return 1
    fi
    local valid code message
    valid=$(echo "$response" | grep -o '"valid" *: *[a-z]*' | head -1 | grep -o 'true\|false')
    code=$(echo "$response" | grep -o '"code" *: *"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
    message=$(echo "$response" | grep -o '"message" *: *"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')

    if [[ "$valid" == "true" ]]; then
        ok "Software attivato. Licenza lifetime valida."
        return 0
    else
        case "$code" in
            invalid_key)
                err "Chiave licenza non valida."
                rm -f "$LICENSE_FILE"
                ;;
            suspended)
                err "Licenza sospesa. Contatta il supporto."
                ;;
            revoked)
                err "Licenza revocata. Contatta il supporto."
                ;;
            activation_limit_reached)
                err "Limite dispositivi raggiunto. Contatta il supporto."
                ;;
            *)
                err "Errore licenza: $message"
                ;;
        esac
        return 1
    fi
}

# Richiede attivazione lifetime gratuita
request_activation() {
    local name="$1"
    local email="$2"
    local payload="{\"action\":\"request_trial\",\"product_code\":\"$PRODUCT_CODE\",\"device_id\":\"$DEVICE_ID\",\"app_version\":\"$APP_VERSION\",\"platform\":\"$PLATFORM\",\"customer_name\":\"$name\",\"customer_email\":\"$email\"}"
    local response
    response=$(license_api_call "$payload")
    if [[ -z "$response" ]]; then
        err "Impossibile contattare il server licenze."
        return 1
    fi
    local valid code issued_key message
    valid=$(echo "$response" | grep -o '"valid" *: *[a-z]*' | head -1 | grep -o 'true\|false')
    code=$(echo "$response" | grep -o '"code" *: *"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
    issued_key=$(echo "$response" | grep -o '"issued_license_key" *: *"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
    message=$(echo "$response" | grep -o '"message" *: *"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')

    if [[ "$valid" == "true" && -n "$issued_key" ]]; then
        save_license_key "$issued_key"
        ok "Software attivato con successo! Licenza lifetime."
        return 0
    elif [[ "$code" == "trial_active" && -n "$issued_key" ]]; then
        save_license_key "$issued_key"
        ok "Software già attivato su questo dispositivo."
        return 0
    else
        err "Attivazione non riuscita: $message"
        return 1
    fi
}

# Flusso principale di controllo licenza
check_license() {
    cecho "\n🔑 Verifica attivazione..." "$YELLOW"

    local saved_key
    saved_key=$(load_license_key)

    if [[ -n "$saved_key" ]]; then
        if validate_license "$saved_key"; then
            return 0
        fi
        saved_key=$(load_license_key)
    fi

    if [[ -z "$saved_key" ]]; then
        echo ""
        cecho "Questo software richiede un'attivazione gratuita (una tantum)." "$YELLOW"
        cecho "È necessaria una connessione internet." "$YELLOW"
        echo ""
        read -rp "Nome e Cognome: " act_name
        read -rp "Email: " act_email
        if [[ -z "$act_name" || -z "$act_email" ]]; then
            err "Nome e email sono obbligatori per l'attivazione."
            exit 1
        fi
        if request_activation "$act_name" "$act_email"; then
            return 0
        else
            exit 1
        fi
    fi

    return 1
}

# ---- Rilevamento dischi esterni ----
get_external_disks() {
    EXTERNAL_DISKS=()
    EXTERNAL_DISKS_INFO=()

    local disks
    disks=$(diskutil list external physical 2>/dev/null | awk '/^\/dev\/disk[0-9]+/ {print $1}')

    while IFS= read -r disk; do
        [[ -z "$disk" ]] && continue
        EXTERNAL_DISKS+=("$disk")

        local info size protocol media
        info=$(diskutil info "$disk" 2>/dev/null)
        size=$(echo "$info" | awk -F': *' '/Disk Size/ {print $2; exit}')
        protocol=$(echo "$info" | awk -F': *' '/Protocol/ {print $2; exit}')
        media=$(echo "$info" | awk -F': *' '/Media Name/ {print $2; exit}')

        [[ -z "$size" ]] && size="sconosciuta"
        [[ -z "$protocol" ]] && protocol="sconosciuto"
        [[ -z "$media" ]] && media="Senza nome"

        EXTERNAL_DISKS_INFO+=("$disk | $media | $protocol | $size")
    done <<< "$disks"
}

# ---- Lista e selezione disco ----
list_and_select_disk() {
    info "Rilevamento schede SD/USB/esterne..."
    get_external_disks
    if [[ ${#EXTERNAL_DISKS[@]} -eq 0 ]]; then
        err "Nessuna scheda SD/USB/esterna trovata. Inserisci una SD o USB."
        exit 1
    fi
    echo ""
    cecho "Schede SD/USB/esterne trovate:" "$GREEN"
    for i in "${!EXTERNAL_DISKS_INFO[@]}"; do
        info "$((i+1)). ${EXTERNAL_DISKS_INFO[$i]}"
    done
    echo "0. Annulla"
    while true; do
        read -rp "Seleziona il disco da testare (1-${#EXTERNAL_DISKS[@]}): " sel
        if [[ "$sel" == "0" ]]; then
            err "Operazione annullata."
            exit 0
        elif [[ "$sel" =~ ^[1-9][0-9]*$ ]] && [[ "$sel" -le ${#EXTERNAL_DISKS[@]} ]]; then
            SELECTED_DISK="${EXTERNAL_DISKS[$((sel-1))]}"
            ok "Selezionato: $SELECTED_DISK"
            break
        else
            warn "Selezione non valida. Riprova."
        fi
    done
}

# ---- Smontaggio disco ----
unmount_disk() {
    info "Smontaggio di tutte le partizioni su $SELECTED_DISK..."
    diskutil unmountDisk force "$SELECTED_DISK" || true
}

# ---- Selezione filesystem ----
select_filesystem() {
    echo ""
    cecho "Scegli il filesystem per la formattazione:" "$YELLOW"
    echo "1. FAT32 (compatibilità massima)"
    echo "2. exFAT (schede >32GB)"
    echo "3. Annulla"
    read -rp "Scelta (1/2/3): " fs_choice
    case "$fs_choice" in
        1) FILESYSTEM="fat32" ;;
        2) FILESYSTEM="exfat" ;;
        *) cecho "Annullato." "$RED"; exit 0 ;;
    esac
}

# ---- Test ultra-veloce con progress bar ----
# Strategia anti-cache: scrive TUTTI i pattern prima, poi rilegge TUTTI.
# I controller delle schede fake non possono imbrogliare restituendo la cache.
# $1 = dimensione opzionale in MB (se omesso, usa dimensione fisica del disco)
ultra_fast_test() {
    local size_mb
    if [[ -n "$1" ]]; then
        size_mb=$1
    else
        local disk_info
        disk_info=$(diskutil info "$SELECTED_DISK")
        local size_bytes
        size_bytes=$(echo "$disk_info" | grep 'Disk Size' | head -1 | sed -E 's/.*\(([0-9]+) Bytes\).*/\1/')
        size_mb=$((size_bytes / 1048576))
    fi
    local test_size_mb=10
    if [[ $size_mb -lt 30 ]]; then
        test_size_mb=1
    fi
    # 5 punti strategici: inizio, 25%, 50%, 75%, fine
    local positions=()
    positions+=($test_size_mb)
    positions+=($((size_mb / 4)))
    positions+=($((size_mb / 2)))
    positions+=($((size_mb * 3 / 4)))
    local end_pos=$((size_mb - test_size_mb))
    if [[ $end_pos -lt 0 ]]; then end_pos=0; fi
    positions+=($end_pos)

    # Genera un pattern unico per ogni posizione
    local pattern_files=()
    for pos in "${positions[@]}"; do
        local pf="/tmp/test_pattern_${pos}_$$"
        dd if=/dev/urandom of="$pf" bs=1m count=$test_size_mb 2>/dev/null
        pattern_files+=("$pf")
    done

    local total=${#positions[@]}

    # FASE 1: Scrivi TUTTI i pattern
    info "Fase 1: Scrittura pattern su $total punti..."
    for i in "${!positions[@]}"; do
        local pos=${positions[$i]}
        info "  Scrittura a ${pos}MB..."
        dd if="${pattern_files[$i]}" of="$SELECTED_DISK" bs=1m seek=$pos count=$test_size_mb conv=notrunc 2>/dev/null
    done
    sync
    sleep 2  # Attesa flush cache controller

    # FASE 2: Rileggi TUTTI e confronta
    info "Fase 2: Rilettura e verifica $total punti..."
    local errors=0
    local first_fail_idx=-1
    for i in "${!positions[@]}"; do
        local pos=${positions[$i]}
        local read_file="/tmp/read_test_${pos}_$$"
        dd if="$SELECTED_DISK" of="$read_file" bs=1m skip=$pos count=$test_size_mb 2>/dev/null
        if ! cmp -s "${pattern_files[$i]}" "$read_file"; then
            err "Dati corrotti a ${pos}MB!"
            errors=$((errors+1))
            if [[ $first_fail_idx -eq -1 ]]; then
                first_fail_idx=$i
            fi
        else
            ok "OK a ${pos}MB"
        fi
        rm -f "$read_file"
        progress_bar $((i+1)) $total
    done

    # Pulizia pattern
    for pf in "${pattern_files[@]}"; do rm -f "$pf"; done

    if [[ $errors -gt 0 ]]; then
        TEST_RESULT="FAKE"
        # Stima capacità reale: il primo punto fallito indica il limite
        if [[ $first_fail_idx -gt 0 ]]; then
            REAL_SIZE_MB=${positions[$first_fail_idx]}
        else
            REAL_SIZE_MB=$((size_mb / 4))  # Fallback conservativo
        fi
        REAL_SIZE_GB=$((REAL_SIZE_MB / 1024))
        err "Dispositivo NON affidabile! Errori: $errors su $total punti"
        warn "Capacità reale stimata: ${REAL_SIZE_GB} GB (${REAL_SIZE_MB} MB)"
    else
        TEST_RESULT="AUTENTICA"
        REAL_SIZE_MB=$size_mb
        REAL_SIZE_GB=$((size_mb / 1024))
        ok "Dispositivo autentico e funzionante."
    fi
}

# ---- Test completo con progress bar ----
# Strategia anti-cache: scrive a blocchi, poi rilegge e confronta
full_test() {
    cecho "Test completo: scrittura/lettura su tutta la scheda (può richiedere molto tempo)..." "$YELLOW"
    local disk_info
    disk_info=$(diskutil info "$SELECTED_DISK")
    local size_bytes
    size_bytes=$(echo "$disk_info" | grep 'Disk Size' | head -1 | sed -E 's/.*\(([0-9]+) Bytes\).*/\1/')
    local size_mb=$((size_bytes / 1048576))
    local chunk=100
    local total=$((size_mb / chunk))
    if [[ $total -eq 0 ]]; then total=1; fi

    # FASE 1: Scrivi pattern unici su tutta la scheda
    info "Fase 1: Scrittura dati di test su tutta la scheda..."
    for ((i=0; i<size_mb; i+=chunk)); do
        local idx=$((i / chunk + 1))
        local pf="/tmp/test_pattern_full_${i}_$$"
        dd if=/dev/urandom of="$pf" bs=1m count=$chunk 2>/dev/null
        dd if="$pf" of="$SELECTED_DISK" bs=1m seek=$i count=$chunk conv=notrunc 2>/dev/null
        progress_bar $idx $((total * 2))
    done
    sync
    sleep 2  # Flush cache controller

    # FASE 2: Rileggi e confronta
    info "Fase 2: Rilettura e verifica..."
    local errors=0
    local first_fail_mb=-1
    for ((i=0; i<size_mb; i+=chunk)); do
        local idx=$((i / chunk + 1))
        local pf="/tmp/test_pattern_full_${i}_$$"
        local read_file="/tmp/read_test_full_$$"
        dd if="$SELECTED_DISK" of="$read_file" bs=1m skip=$i count=$chunk 2>/dev/null
        if ! cmp -s "$pf" "$read_file"; then
            err "Dati corrotti nel blocco ${i}MB!"
            errors=$((errors+1))
            if [[ $first_fail_mb -eq -1 ]]; then
                first_fail_mb=$i
            fi
        fi
        rm -f "$read_file" "$pf"
        progress_bar $((total + idx)) $((total * 2))
    done
    if [[ $errors -gt 0 ]]; then
        TEST_RESULT="FAKE"
        if [[ $first_fail_mb -gt 0 ]]; then
            REAL_SIZE_MB=$first_fail_mb
        else
            REAL_SIZE_MB=$((size_mb / 4))
        fi
        REAL_SIZE_GB=$((REAL_SIZE_MB / 1024))
        err "Dispositivo NON affidabile! Errori: $errors"
        warn "Capacità reale stimata: ${REAL_SIZE_GB} GB (${REAL_SIZE_MB} MB)"
    else
        TEST_RESULT="AUTENTICA"
        REAL_SIZE_MB=$size_mb
        REAL_SIZE_GB=$((size_mb / 1024))
        ok "Dispositivo autentico."
    fi
}

# ---- Analisi capacità reale ----
analyze_capacity() {
    # La capacità reale è già stimata da ultra_fast_test/full_test
    if [[ -z "$REAL_SIZE_GB" ]]; then
        REAL_SIZE_GB=$DISK_SIZE_GB
    fi
}

# ---- Report dettagliato ----
write_report() {
    local report_file="sd_verifier_report_$(date +%Y%m%d_%H%M%S).txt"
    {
        echo "Dispositivo: $SELECTED_DISK"
        echo "Capacità dichiarata: $DISK_SIZE_GB GB"
        echo "Capacità reale stimata: $REAL_SIZE_GB GB"
        echo "Test: $TEST_TYPE"
        echo "Risultato: $TEST_RESULT"
        echo "Data: $(date)"
    } > "$report_file"
    ok "Report salvato in $report_file"
}

# ---- Correzione scheda fake ----
fix_fake_card() {
    if [[ "$TEST_RESULT" != "FAKE" ]]; then
        ok "La scheda non necessita correzioni."
        return 0
    fi
    # Margine di sicurezza 5%
    local safe_mb=$((REAL_SIZE_MB * 95 / 100))
    cecho "Correzione scheda: riformattazione a ${safe_mb} MB (capacità reale sicura)..." "$YELLOW"
    # IMPORTANTE: Con MBR e 1 sola partizione, diskutil espande a tutto il disco.
    # Soluzione: creare 2 partizioni — la nostra FAT32/exFAT + "Free Space" per il resto.
    if [[ "$FILESYSTEM" == "fat32" ]]; then
        diskutil partitionDisk "$SELECTED_DISK" 2 MBRFormat \
            "MS-DOS FAT32" "SD_FIXED" "${safe_mb}M" \
            "Free Space" "%noformat%" R 2>/dev/null || true
    else
        diskutil partitionDisk "$SELECTED_DISK" 2 MBRFormat \
            ExFAT "SD_FIXED" "${safe_mb}M" \
            "Free Space" "%noformat%" R 2>/dev/null || true
    fi
    sync
    # Salva la dimensione effettiva della partizione creata
    SAFE_SIZE_MB=$safe_mb
    ok "Scheda corretta e riformattata a ${safe_mb} MB."
}

# ---- Verifica integrità post-formattazione ----
verify_post_format() {
    local test_mb=${SAFE_SIZE_MB:-$REAL_SIZE_MB}
    local attempt=0
    while true; do
        attempt=$((attempt + 1))
        cecho "\nVerifica integrità (tentativo $attempt, ${test_mb} MB)..." "$YELLOW"
        unmount_disk
        ultra_fast_test "$test_mb"
        if [[ "$TEST_RESULT" != "FAKE" ]]; then
            ok "Verifica superata a ${test_mb} MB!"
            REAL_SIZE_MB=$test_mb
            REAL_SIZE_GB=$((test_mb / 1024))
            # Formattazione finale alla dimensione verificata
            cecho "Formattazione finale a ${test_mb} MB..." "$YELLOW"
            local final_mb=$((test_mb * 95 / 100))
            if [[ "$FILESYSTEM" == "fat32" ]]; then
                diskutil partitionDisk "$SELECTED_DISK" 2 MBRFormat \
                    "MS-DOS FAT32" "SD_FIXED" "${final_mb}M" \
                    "Free Space" "%noformat%" R 2>/dev/null || true
            else
                diskutil partitionDisk "$SELECTED_DISK" 2 MBRFormat \
                    ExFAT "SD_FIXED" "${final_mb}M" \
                    "Free Space" "%noformat%" R 2>/dev/null || true
            fi
            sync
            ok "Scheda pronta all'uso: ${final_mb} MB (${REAL_SIZE_GB} GB)"
            return 0
        fi
        # Dimezza la capacità
        test_mb=$((test_mb / 2))
        if [[ $test_mb -lt 128 ]]; then
            err "Capacità scesa sotto 128 MB. Scheda inutilizzabile."
            REAL_SIZE_MB=0
            REAL_SIZE_GB=0
            return 1
        fi
        warn "Errori rilevati. Riduco a ${test_mb} MB e riprovo..."
        REAL_SIZE_MB=$test_mb
        REAL_SIZE_GB=$((test_mb / 1024))
        fix_fake_card
    done
}

# ---- Funzione principale ----
main() {
    welcome_screen
    check_license
    list_and_select_disk
    unmount_disk

    # Calcola dimensione disco (solo interi)
    local disk_info
    disk_info=$(diskutil info "$SELECTED_DISK")
    local size_bytes
    size_bytes=$(echo "$disk_info" | grep 'Disk Size' | head -1 | sed -E 's/.*\(([0-9]+) Bytes\).*/\1/')
    DISK_SIZE_GB=$((size_bytes / 1073741824))
    DISK_SIZE_MB=$((size_bytes / 1048576))

    cecho "\n📏 Dettagli dispositivo selezionato: $SELECTED_DISK" "$YELLOW"
    cecho "Capacità dichiarata: ${DISK_SIZE_GB} GB (${DISK_SIZE_MB} MB)" "$YELLOW"

    select_filesystem

    cecho "\n⚠️  ATTENZIONE: Tutti i dati sul dispositivo saranno cancellati!" "$RED"
    read -rp "Confermi di voler procedere? (s/N): " confirm
    if [[ "$confirm" != "s" && "$confirm" != "S" ]]; then
        cecho "Operazione annullata." "$RED"
        exit 0
    fi

    echo ""
    cecho "Scegli il tipo di test:" "$YELLOW"
    echo "1. ⚡ Ultra-veloce (test punti strategici)"
    echo "2. 🔬 Completo (scrittura/lettura completa)"
    read -rp "Scelta (1/2): " test_choice

    if [[ "$test_choice" == "2" ]]; then
        TEST_TYPE="Completo"
        cecho "\nInizio test completo..." "$YELLOW"
        full_test
    else
        TEST_TYPE="Ultra-veloce"
        cecho "\nInizio test ultra-veloce..." "$YELLOW"
        ultra_fast_test
    fi

    analyze_capacity
    write_report

    # Ricorda se il test iniziale ha rilevato una scheda fake
    CARD_IS_FAKE=false
    if [[ "$TEST_RESULT" == "FAKE" ]]; then
        CARD_IS_FAKE=true
    fi

    fix_fake_card
    verify_post_format

    echo ""
    cecho "🎉 VERIFICA COMPLETATA" "$GREEN"
    cecho "Dispositivo: $SELECTED_DISK" "$YELLOW"
    cecho "Capacità dichiarata: ${DISK_SIZE_GB} GB" "$YELLOW"
    cecho "Capacità reale verificata: ${REAL_SIZE_GB} GB (${REAL_SIZE_MB} MB)" "$YELLOW"
    if [[ "$CARD_IS_FAKE" == "true" ]]; then
        cecho "Stato: CONTRAFFATTA — Corretta e funzionante a ${REAL_SIZE_GB} GB" "$RED"
    else
        cecho "Stato: AUTENTICA E FUNZIONANTE" "$GREEN"
    fi
}

# ---- Avvio script ----
main "$@"
