#!/usr/bin/env php
<?php
/**
 * Emergency Backup Worker Cleanup Script
 *
 * This script safely kills rogue backup worker processes and cleans up
 * any stuck backup records in the database.
 *
 * Usage: php emergency-backup-cleanup.php [--dry-run]
 */

// Load configuration
require_once(__DIR__ . '/../web-files/configs/config.php');
require_once('/docker/whp/web/libs/mysqlmgmt.php');

function log_message($message, $level = 'INFO') {
    $timestamp = date('Y-m-d H:i:s');
    echo "[$timestamp] [$level] $message\n";
}

function find_backup_worker_processes() {
    $output = [];
    exec("ps aux | grep 'backup-worker.php' | grep -v grep", $output);

    $processes = [];
    foreach ($output as $line) {
        if (preg_match('/^\s*(\S+)\s+(\d+)\s+.*backup-worker\.php/', $line, $matches)) {
            $processes[] = [
                'user' => $matches[1],
                'pid' => (int)$matches[2],
                'line' => $line
            ];
        }
    }

    return $processes;
}

function kill_backup_workers($dryRun = false) {
    $processes = find_backup_worker_processes();

    if (empty($processes)) {
        log_message("No backup worker processes found");
        return 0;
    }

    log_message("Found " . count($processes) . " backup worker processes:");

    foreach ($processes as $process) {
        log_message("  PID {$process['pid']} (user: {$process['user']})");
        echo "    {$process['line']}\n";
    }

    if ($dryRun) {
        log_message("DRY RUN: Would kill " . count($processes) . " processes");
        return count($processes);
    }

    $killed = 0;
    foreach ($processes as $process) {
        log_message("Killing PID {$process['pid']}...");

        // First try SIGTERM (graceful)
        if (posix_kill($process['pid'], SIGTERM)) {
            sleep(2);

            // Check if process is still running
            if (posix_kill($process['pid'], 0)) {
                log_message("Process {$process['pid']} still running, using SIGKILL");
                posix_kill($process['pid'], SIGKILL);
            }

            $killed++;
            log_message("Killed PID {$process['pid']}");
        } else {
            log_message("Failed to kill PID {$process['pid']}", 'WARNING');
        }
    }

    return $killed;
}

function cleanup_stuck_backups($dryRun = false) {
    try {
        $mysql = new mysqlmgmt();
        $db = $mysql->getMySQLConnection();
        $db->exec("USE whp");

        // Find backups stuck in 'running' status for more than 1 hour
        $stmt = $db->prepare("
            SELECT id, backup_name, started_at, status
            FROM backup_history
            WHERE status = 'running'
            AND started_at < DATE_SUB(NOW(), INTERVAL 1 HOUR)
        ");
        $stmt->execute();
        $stuckBackups = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (empty($stuckBackups)) {
            log_message("No stuck backup records found");
            return 0;
        }

        log_message("Found " . count($stuckBackups) . " stuck backup records:");
        foreach ($stuckBackups as $backup) {
            log_message("  ID {$backup['id']}: {$backup['backup_name']} (started: {$backup['started_at']})");
        }

        if ($dryRun) {
            log_message("DRY RUN: Would reset " . count($stuckBackups) . " stuck backups to 'failed'");
            return count($stuckBackups);
        }

        // Reset stuck backups to 'failed' status
        $backupIds = array_column($stuckBackups, 'id');
        $placeholders = str_repeat('?,', count($backupIds) - 1) . '?';

        $stmt = $db->prepare("
            UPDATE backup_history
            SET status = 'failed',
                error_message = 'Process killed by emergency cleanup script'
            WHERE id IN ($placeholders)
        ");
        $stmt->execute($backupIds);

        log_message("Reset " . count($stuckBackups) . " stuck backups to 'failed' status");
        return count($stuckBackups);

    } catch (Exception $e) {
        log_message("Error cleaning up database: " . $e->getMessage(), 'ERROR');
        return 0;
    }
}

function cleanup_lock_files($dryRun = false) {
    $lockFile = '/tmp/backup-worker.lock';

    if (!file_exists($lockFile)) {
        log_message("No lock file found");
        return false;
    }

    $pid = trim(file_get_contents($lockFile));
    log_message("Found lock file with PID: $pid");

    // Check if the process is still running
    if ($pid && posix_kill($pid, 0)) {
        log_message("Lock file PID $pid is still running", 'WARNING');
        return false;
    }

    if ($dryRun) {
        log_message("DRY RUN: Would remove stale lock file");
        return true;
    }

    if (unlink($lockFile)) {
        log_message("Removed stale lock file");
        return true;
    } else {
        log_message("Failed to remove lock file", 'ERROR');
        return false;
    }
}

// Parse command line arguments
$dryRun = in_array('--dry-run', $argv);

if ($dryRun) {
    log_message("=== DRY RUN MODE - NO CHANGES WILL BE MADE ===");
} else {
    log_message("=== EMERGENCY BACKUP CLEANUP ===");
}

log_message("Starting backup worker cleanup...");

// Step 1: Kill rogue backup worker processes
$killedProcesses = kill_backup_workers($dryRun);

// Step 2: Clean up stuck backup records
$resetBackups = cleanup_stuck_backups($dryRun);

// Step 3: Clean up stale lock files
$removedLock = cleanup_lock_files($dryRun);

// Summary
log_message("=== CLEANUP SUMMARY ===");
log_message("Processes killed: $killedProcesses");
log_message("Backup records reset: $resetBackups");
log_message("Lock file removed: " . ($removedLock ? "yes" : "no"));

if ($dryRun) {
    log_message("This was a dry run. Run without --dry-run to actually perform cleanup.");
} else {
    log_message("Emergency cleanup completed!");
}