#!/bin/bash
#
# Migrate MySQL Tables to InnoDB
# Copyright (c) 2026 Nagios Enterprises, LLC. All rights reserved.
#
# This script ensures all tables in the nagiosxi database are using InnoDB engine
# before applying schema changes that require foreign key constraints (e.g., schema_80002.sql).
# It checks disk space, backs up tables, and converts non-InnoDB tables to InnoDB.
#
# Usage:
#   migrate_tables_to_innodb.sh

set -e

BASEDIR=$(dirname $(readlink -f $0))

# Import Nagios XI and xi-sys.cfg config vars (if not already set by parent script)
if [ -z "$cfg__db_info__nagiosxi__dbserver" ] && [ -z "$mysqlpass" ]; then
    if [ -f "$BASEDIR/../var/xi-sys.cfg" ]; then
        . "$BASEDIR/../var/xi-sys.cfg"
    elif [ -f "/usr/local/nagiosxi/var/xi-sys.cfg" ]; then
        . "/usr/local/nagiosxi/var/xi-sys.cfg"
    else
        echo "ERROR: Cannot find xi-sys.cfg file and required variables are not set"
        exit 1
    fi
fi

# Set defaults if not set
if [ "$cfg__db_info__nagiosxi__dbserver" == "" ]; then
    cfg__db_info__nagiosxi__dbserver="localhost"
fi

# Validate required variables
if [ -z "$mysqlpass" ]; then
    echo "ERROR: mysqlpass variable is not set"
    exit 1
fi

if [ -z "$cfg__db_info__nagiosxi__db" ]; then
    echo "ERROR: cfg__db_info__nagiosxi__db variable is not set"
    exit 1
fi

# Parse database server and port
if [[ "$cfg__db_info__nagiosxi__dbserver" == *":"* ]]; then
    db_port=$(echo "$cfg__db_info__nagiosxi__dbserver" | cut -f2 -d":")
    db_server=$(echo "$cfg__db_info__nagiosxi__dbserver" | cut -f1 -d":")
else
    db_port='3306'
    db_server="$cfg__db_info__nagiosxi__dbserver"
fi

db_name="$cfg__db_info__nagiosxi__db"
db_user="root"
db_pass="$mysqlpass"

# Setup mysql connection options
mysql_opts="-h $db_server -P $db_port -u $db_user --password=$db_pass"
mysqldump_opts="$mysql_opts"

# Handle column-statistics option for mysqldump
if mysqldump --help 2>&1 | grep -q "column-statistics"; then
    mysqldump_opts="$mysqldump_opts --column-statistics=0"
fi

# Setup backup directory
backup_base="/store/backups/mysql"
backup_dir="$backup_base/innodb_migration_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"

log_file="$backup_dir/migration.log"
exec > >(tee -a "$log_file")
exec 2>&1

echo "=========================================="
echo "InnoDB Migration Script"
echo "Started: $(date)"
echo "=========================================="
echo ""

# Function to log messages
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# Function to check if MySQL connection works
check_mysql_connection() {
    log "Checking MySQL connection..."
    if ! mysql $mysql_opts -e "SELECT 1;" >/dev/null 2>&1; then
        log "ERROR: Cannot connect to MySQL server"
        log "Server: $db_server:$db_port"
        log "User: $db_user"
        exit 1
    fi
    log "MySQL connection successful"
}

# Function to get MySQL data directory
get_datadir() {
    mysql $mysql_opts -BNe "SELECT @@datadir;" 2>/dev/null | head -n1
}

# Function to check disk space
check_disk_space() {
    log "Checking disk space..."
    
    # Get MySQL data directory
    datadir=$(get_datadir)
    if [ -z "$datadir" ]; then
        log "WARNING: Could not determine MySQL data directory, skipping disk space check"
        return 0
    fi
    
    # Get filesystem for data directory
    filesystem=$(df -P "$datadir" | tail -n1 | awk '{print $6}')
    
    # Get available space in bytes
    available_bytes=$(df -B1 "$datadir" | tail -n1 | awk '{print $4}')
    available_gb=$(awk "BEGIN {printf \"%.2f\", $available_bytes / 1024 / 1024 / 1024}")
    
    log "MySQL data directory: $datadir"
    log "Filesystem: $filesystem"
    log "Available space: ${available_gb} GB"
    
    # Get total size of non-InnoDB tables
    log "Calculating size of tables to convert..."
    
    # Query to get total data+index length for non-InnoDB tables
    size_query="SELECT COALESCE(SUM(data_length + index_length), 0) as total_size 
                FROM information_schema.tables 
                WHERE table_schema = '$db_name' 
                AND engine != 'InnoDB' 
                AND engine IS NOT NULL"
    
    total_size_bytes=$(mysql $mysql_opts -BNe "$size_query" 2>/dev/null)
    
    if [ -z "$total_size_bytes" ] || [ "$total_size_bytes" = "NULL" ] || [ "$total_size_bytes" = "0" ]; then
        log "No non-InnoDB tables found - nothing to convert"
        return 0
    fi
    
    total_size_gb=$(awk "BEGIN {printf \"%.2f\", $total_size_bytes / 1024 / 1024 / 1024}")
    
    # Estimate space needed: MyISAM to InnoDB typically needs 2-3x space during conversion
    # We'll use 3x as a safety margin
    estimated_needed_bytes=$(awk "BEGIN {printf \"%.0f\", $total_size_bytes * 3}")
    estimated_needed_gb=$(awk "BEGIN {printf \"%.2f\", $estimated_needed_bytes / 1024 / 1024 / 1024}")
    
    log "Total size of non-InnoDB tables: ${total_size_gb} GB"
    log "Estimated space needed for conversion: ${estimated_needed_gb} GB"
    
    # Check if we have enough space (with 10% buffer)
    buffer_needed=$(awk "BEGIN {printf \"%.0f\", $estimated_needed_bytes * 1.1}")
    if [ "$available_bytes" -lt "$buffer_needed" ]; then
        log "ERROR: Insufficient disk space!"
        log "Available: ${available_gb} GB"
        log "Estimated needed: ${estimated_needed_gb} GB (with 10% buffer)"
        log ""
        log "Please free up disk space before running this migration."
        log "You can:"
        log "  1. Clean up old backups"
        log "  2. Remove unnecessary files"
        log "  3. Expand the filesystem if possible"
        exit 1
    fi
    
    log "Disk space check passed"
}

# Function to backup tables
backup_tables() {
    log "Backing up non-InnoDB tables..."
    
    # Get list of non-InnoDB tables
    tables_query="SELECT table_name 
                  FROM information_schema.tables 
                  WHERE table_schema = '$db_name' 
                  AND engine != 'InnoDB' 
                  AND engine IS NOT NULL"
    
    tables=$(mysql $mysql_opts -BNe "$tables_query" 2>/dev/null)
    
    if [ -z "$tables" ]; then
        log "No non-InnoDB tables found - skipping backup"
        return 0
    fi
    
    log "Found tables to backup:"
    echo "$tables" | while read table; do
        if [ -n "$table" ]; then
            log "  - $table"
        fi
    done
    
    # Backup each table
    backup_count=0
    for table in $tables; do
        if [ -n "$table" ]; then
            log "Backing up table: $table"
            backup_file="$backup_dir/${table}.sql"
            
            if mysqldump $mysqldump_opts "$db_name" "$table" > "$backup_file" 2>&1; then
                log "  Successfully backed up $table"
                backup_count=$((backup_count + 1))
            else
                log "  ERROR: Failed to backup $table"
                exit 1
            fi
        fi
    done
    
    log "Backup complete. Backed up $backup_count table(s). Backups stored in: $backup_dir"
}

# Function to convert tables to InnoDB
convert_tables() {
    log "Converting tables to InnoDB..."
    
    # Get list of non-InnoDB tables
    tables_query="SELECT table_name 
                  FROM information_schema.tables 
                  WHERE table_schema = '$db_name' 
                  AND engine != 'InnoDB' 
                  AND engine IS NOT NULL"
    
    tables=$(mysql $mysql_opts -BNe "$tables_query" 2>/dev/null)
    
    if [ -z "$tables" ]; then
        log "No non-InnoDB tables found - nothing to convert"
        return 0
    fi
    
    # Convert each table
    converted_count=0
    failed_tables=""
    
    for table in $tables; do
        if [ -n "$table" ]; then
            log "Converting table: $table"
            
            if mysql $mysql_opts "$db_name" -e "ALTER TABLE \`$table\` ENGINE=InnoDB;" 2>&1; then
                log "  Successfully converted $table to InnoDB"
                converted_count=$((converted_count + 1))
            else
                log "  ERROR: Failed to convert $table"
                failed_tables="$failed_tables $table"
            fi
        fi
    done
    
    # Verify conversion
    log "Verifying conversion..."
    remaining_query="SELECT COUNT(*) 
                     FROM information_schema.tables 
                     WHERE table_schema = '$db_name' 
                     AND engine != 'InnoDB' 
                     AND engine IS NOT NULL"
    
    remaining=$(mysql $mysql_opts -BNe "$remaining_query" 2>/dev/null)
    
    if [ "$remaining" = "0" ]; then
        log "All tables successfully converted to InnoDB ($converted_count table(s))"
    else
        log "WARNING: $remaining table(s) still not using InnoDB"
        if [ -n "$failed_tables" ]; then
            log "Failed tables: $failed_tables"
        fi
        exit 1
    fi
}

# Main execution
main() {
    check_mysql_connection
    check_disk_space
    backup_tables
    convert_tables
    
    log ""
    log "=========================================="
    log "Migration completed successfully!"
    log "Finished: $(date)"
    log "Backups stored in: $backup_dir"
    log "=========================================="
}

# Run main function
main

