#!/bin/bash

################################################################################
# Nuclear Plex Database Recovery Script
################################################################################
#
# Author: Peter Wood <peter@peterwood.dev>
# Description: Last-resort database recovery script that completely replaces
#              corrupted Plex databases with known good backups. This script
#              is used when all other repair methods have failed and a complete
#              database replacement is the only remaining option.
#
# ⚠️  WARNING: This script will completely replace existing databases!
#     All data since the backup was created will be lost.
#     Use only when standard repair methods have failed.
#
# Features:
#   - Complete database replacement from backups
#   - Automatic backup of current (corrupted) databases
#   - Service management and safety checks
#   - Comprehensive logging of all operations
#   - Rollback capability if replacement fails
#   - Verification of restored database integrity
#
# Related Scripts:
#   - backup-plex.sh: Creates backups used by this recovery script
#   - icu-aware-recovery.sh: ICU-specific recovery methods
#   - restore-plex.sh: Standard restoration procedures
#   - validate-plex-recovery.sh: Validates recovery results
#   - plex.sh: General Plex service management
#
# Usage:
#   ./nuclear-plex-recovery.sh                    # Interactive recovery
#   ./nuclear-plex-recovery.sh --auto             # Automated recovery
#   ./nuclear-plex-recovery.sh --dry-run          # Show what would be done
#   ./nuclear-plex-recovery.sh --verify-only      # Verify backup integrity
#
# Dependencies:
#   - Valid Plex backup files
#   - sqlite3 or Plex SQLite binary
#   - systemctl (for service management)
#   - tar (for backup extraction)
#
# Exit Codes:
#   0 - Recovery successful
#   1 - General error
#   2 - Backup file issues
#   3 - Database replacement failure
#   4 - Service management failure
#   5 - Rollback performed due to failure
#
################################################################################

# Nuclear Plex Database Recovery Script
# This script completely replaces corrupted databases with known good backups
# Use this when standard repair methods have failed

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

# Configuration
PLEX_DB_DIR="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
PLEX_SQLITE="/usr/lib/plexmediaserver/Plex SQLite"
PLEX_USER="plex"
PLEX_GROUP="plex"
BACKUP_TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
RECOVERY_LOG="/home/acedanger/shell/plex/logs/nuclear-recovery-${BACKUP_TIMESTAMP}.log"

# Ensure log directory exists
mkdir -p "$(dirname "$RECOVERY_LOG")"

# Function to log messages
log_message() {
    local level="$1"
    local message="$2"
    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $message" | tee -a "$RECOVERY_LOG"
}

# Function to print colored output
print_status() {
    local color="$1"
    local message="$2"
    echo -e "${color}${message}${NC}"
    log_message "INFO" "$message"
}

# Function to check if running as root
check_root() {
    if [[ $EUID -ne 0 ]]; then
        print_status "$RED" "This script must be run as root or with sudo"
        exit 1
    fi
}

# Function to stop Plex service
stop_plex() {
    print_status "$YELLOW" "Stopping Plex Media Server..."

    if systemctl is-active --quiet plexmediaserver; then
        systemctl stop plexmediaserver
        sleep 5

        # Verify it's stopped
        if systemctl is-active --quiet plexmediaserver; then
            print_status "$RED" "Failed to stop Plex service"
            exit 1
        fi
        print_status "$GREEN" "Plex service stopped successfully"
    else
        print_status "$YELLOW" "Plex service was already stopped"
    fi
}

# Function to start Plex service
start_plex() {
    print_status "$YELLOW" "Starting Plex Media Server..."

    systemctl start plexmediaserver
    sleep 10

    # Verify it's running
    if systemctl is-active --quiet plexmediaserver; then
        print_status "$GREEN" "Plex service started successfully"

        # Check if it's actually responding
        local max_attempts=30
        local attempt=1

        while [[ $attempt -le $max_attempts ]]; do
            if curl -s -f "http://localhost:32400/web/index.html" > /dev/null 2>&1; then
                print_status "$GREEN" "Plex web interface is responding"
                return 0
            fi

            print_status "$YELLOW" "Waiting for Plex to fully start... (attempt $attempt/$max_attempts)"
            sleep 5
            ((attempt++))
        done

        print_status "$YELLOW" "Plex service is running but web interface may still be starting"
    else
        print_status "$RED" "Failed to start Plex service"
        return 1
    fi
}

# Function to backup current corrupted databases
backup_corrupted_databases() {
    print_status "$YELLOW" "Backing up current corrupted databases..."

    local backup_dir="${PLEX_DB_DIR}/corrupted-${BACKUP_TIMESTAMP}"
    mkdir -p "$backup_dir"

    # Backup main database if it exists
    if [[ -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" ]]; then
        cp "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" "$backup_dir/"
        print_status "$GREEN" "Backed up corrupted main database"
    fi

    # Backup blobs database if it exists
    if [[ -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" ]]; then
        cp "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" "$backup_dir/"
        print_status "$GREEN" "Backed up corrupted blobs database"
    fi

    print_status "$GREEN" "Corrupted databases backed up to: $backup_dir"
}

# Function to find best backup
find_best_backup() {
    local backup_type="$1"
    local latest_backup=""

    # Find the most recent backup that exists and has reasonable size
    for backup_file in "${PLEX_DB_DIR}/${backup_type}"-????-??-??*; do
        if [[ -f "$backup_file" ]]; then
            local file_size
            file_size=$(stat -f%z "$backup_file" 2>/dev/null || stat -c%s "$backup_file" 2>/dev/null)

            # Check if file size is reasonable (> 100MB for main DB, > 500MB for blobs)
            if [[ "$backup_type" == "com.plexapp.plugins.library.db" && $file_size -gt 104857600 ]] || \
               [[ "$backup_type" == "com.plexapp.plugins.library.blobs.db" && $file_size -gt 524288000 ]]; then
                latest_backup="$backup_file"
            fi
        fi
    done

    echo "$latest_backup"
}

# Function to restore from backup
restore_from_backup() {
    print_status "$YELLOW" "Finding and restoring from best available backups..."

    # Find best main database backup
    local main_backup
    main_backup=$(find_best_backup "com.plexapp.plugins.library.db")
    if [[ -n "$main_backup" ]]; then
        print_status "$GREEN" "Found main database backup: $(basename "$main_backup")"

        # Remove corrupted main database
        rm -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.db"

        # Copy backup to main location
        cp "$main_backup" "${PLEX_DB_DIR}/com.plexapp.plugins.library.db"

        # Set proper ownership and permissions
        chown "$PLEX_USER:$PLEX_GROUP" "${PLEX_DB_DIR}/com.plexapp.plugins.library.db"
        chmod 644 "${PLEX_DB_DIR}/com.plexapp.plugins.library.db"

        print_status "$GREEN" "Main database restored successfully"
    else
        print_status "$RED" "No suitable main database backup found!"
        exit 1
    fi

    # Find best blobs database backup
    local blobs_backup
    blobs_backup=$(find_best_backup "com.plexapp.plugins.library.blobs.db")
    if [[ -n "$blobs_backup" ]]; then
        print_status "$GREEN" "Found blobs database backup: $(basename "$blobs_backup")"

        # Remove corrupted blobs database
        rm -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"

        # Copy backup to main location
        cp "$blobs_backup" "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"

        # Set proper ownership and permissions
        chown "$PLEX_USER:$PLEX_GROUP" "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"
        chmod 644 "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"

        print_status "$GREEN" "Blobs database restored successfully"
    else
        print_status "$RED" "No suitable blobs database backup found!"
        exit 1
    fi
}

# Function to verify restored databases (structural + FTS)
verify_databases() {
    print_status "$YELLOW" "Verifying restored databases..."

    # Use Plex's bundled SQLite for ICU compatibility; fall back to system sqlite3
    local sqlite_bin="sqlite3"
    if [[ -x "$PLEX_SQLITE" ]]; then
        sqlite_bin="$PLEX_SQLITE"
    fi

    local overall_ok=true

    for db_file in \
        "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" \
        "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"; do

        local db_name
        db_name=$(basename "$db_file")

        if [[ ! -f "$db_file" ]]; then
            print_status "$RED" "$db_name: NOT FOUND"
            overall_ok=false
            continue
        fi

        # Structural integrity
        local result
        result=$("$sqlite_bin" "$db_file" "PRAGMA integrity_check;" 2>&1)
        if [[ "$result" == "ok" ]]; then
            print_status "$GREEN" "$db_name structural integrity: PASSED"
        else
            print_status "$RED" "$db_name structural integrity: FAILED"
            overall_ok=false
        fi

        # FTS index integrity
        local fts_tables
        fts_tables=$("$sqlite_bin" "$db_file" \
            "SELECT name FROM sqlite_master WHERE type='table' AND sql LIKE '%fts%';" 2>/dev/null) || true
        if [[ -n "$fts_tables" ]]; then
            while IFS= read -r table; do
                [[ -z "$table" ]] && continue
                local fts_result
                fts_result=$("$sqlite_bin" "$db_file" \
                    "INSERT INTO ${table}(${table}) VALUES('integrity-check');" 2>&1) || true
                if [[ -n "$fts_result" ]]; then
                    print_status "$RED" "$db_name FTS index '$table': DAMAGED"
                    overall_ok=false
                fi
            done <<< "$fts_tables"
        fi
    done

    if [[ "$overall_ok" == true ]]; then
        print_status "$GREEN" "All database integrity checks passed!"
        return 0
    else
        print_status "$RED" "One or more database checks failed!"
        return 1
    fi
}

# Function to fix ownership issues
fix_ownership() {
    print_status "$YELLOW" "Fixing file ownership in Plex database directory..."

    # Fix ownership of all files in the database directory
    chown -R "$PLEX_USER:$PLEX_GROUP" "$PLEX_DB_DIR"

    # Verify critical files have correct ownership
    local main_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.db"
    local blobs_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"

    if [[ -f "$main_db" ]]; then
        local main_owner
        main_owner=$(stat -f%Su:%Sg "$main_db" 2>/dev/null || stat -c%U:%G "$main_db" 2>/dev/null)
        if [[ "$main_owner" == "$PLEX_USER:$PLEX_GROUP" ]]; then
            print_status "$GREEN" "Main database ownership: CORRECT ($main_owner)"
        else
            print_status "$RED" "Main database ownership: INCORRECT ($main_owner)"
            chown "$PLEX_USER:$PLEX_GROUP" "$main_db"
        fi
    fi

    if [[ -f "$blobs_db" ]]; then
        local blobs_owner=$(stat -f%Su:%Sg "$blobs_db" 2>/dev/null || stat -c%U:%G "$blobs_db" 2>/dev/null)
        if [[ "$blobs_owner" == "$PLEX_USER:$PLEX_GROUP" ]]; then
            print_status "$GREEN" "Blobs database ownership: CORRECT ($blobs_owner)"
        else
            print_status "$RED" "Blobs database ownership: INCORRECT ($blobs_owner)"
            chown "$PLEX_USER:$PLEX_GROUP" "$blobs_db"
        fi
    fi
}

# Function to clean up temporary files
cleanup_temp_files() {
    print_status "$YELLOW" "Cleaning up temporary and lock files..."

    # Remove any SQLite temporary files
    rm -f "${PLEX_DB_DIR}"/*.db-shm
    rm -f "${PLEX_DB_DIR}"/*.db-wal
    rm -f "${PLEX_DB_DIR}"/*.tmp

    print_status "$GREEN" "Temporary files cleaned up"
}

# Main recovery function
main() {
    print_status "$BLUE" "=== NUCLEAR PLEX DATABASE RECOVERY STARTED ==="
    print_status "$BLUE" "Timestamp: $(date)"
    print_status "$BLUE" "Log file: $RECOVERY_LOG"

    # Pre-flight checks
    check_root

    # Confirm with user
    print_status "$YELLOW" "WARNING: This will completely replace your Plex databases with backups!"
    print_status "$YELLOW" "This will result in some data loss (recent changes since last backup)."
    read -p "Are you sure you want to continue? (yes/no): " -r

    if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then
        print_status "$YELLOW" "Recovery cancelled by user"
        exit 0
    fi

    # Stop Plex service
    stop_plex

    # Backup current corrupted databases
    backup_corrupted_databases

    # Restore from backup
    restore_from_backup

    # Fix ownership issues
    fix_ownership

    # Clean up temporary files
    cleanup_temp_files

    # Verify databases
    verify_databases

    # Start Plex service
    start_plex

    print_status "$GREEN" "=== NUCLEAR RECOVERY COMPLETED SUCCESSFULLY ==="
    print_status "$GREEN" "Your Plex Media Server should now be functional."
    print_status "$GREEN" "Check the web interface at: http://localhost:32400"
    print_status "$YELLOW" "Note: You may need to re-scan your libraries to pick up recent changes."
    print_status "$BLUE" "Recovery log saved to: $RECOVERY_LOG"
}

# Script usage
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi
