#!/usr/bin/env php
<?php
/**
 * Manage multipart uploads - list, resume, or abort interrupted uploads
 */

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

// Parse command line arguments
$options = getopt("", ["list", "resume:", "abort:", "cleanup", "help"]);

if (isset($options['help']) || empty($options)) {
    showHelp();
    exit(0);
}

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

    if (isset($options['list'])) {
        listMultipartUploads($db);
    } elseif (isset($options['resume'])) {
        resumeUpload($db, $options['resume']);
    } elseif (isset($options['abort'])) {
        abortUpload($db, $options['abort']);
    } elseif (isset($options['cleanup'])) {
        cleanupStaleUploads($db);
    }

} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
    exit(1);
}

function showHelp() {
    echo "Multipart Upload Manager\n";
    echo "========================\n\n";
    echo "Usage: php manage-multipart-uploads.php [OPTIONS]\n\n";
    echo "Options:\n";
    echo "  --list              List all multipart uploads\n";
    echo "  --resume <id>       Resume a specific multipart upload by ID\n";
    echo "  --abort <id>        Abort and clean up a multipart upload\n";
    echo "  --cleanup           Clean up stale uploads (older than 7 days)\n";
    echo "  --help              Show this help message\n\n";
    echo "Examples:\n";
    echo "  php manage-multipart-uploads.php --list\n";
    echo "  php manage-multipart-uploads.php --resume 123\n";
    echo "  php manage-multipart-uploads.php --cleanup\n";
}

function listMultipartUploads($db) {
    $stmt = $db->query("
        SELECT
            mu.id,
            mu.backup_id,
            mu.upload_id,
            mu.bucket,
            mu.key,
            mu.file_size,
            mu.total_parts,
            LENGTH(mu.completed_parts) as completed_count,
            mu.status,
            mu.started_at,
            mu.updated_at,
            bh.user,
            bh.backup_name
        FROM multipart_uploads mu
        LEFT JOIN backup_history bh ON mu.backup_id = bh.id
        ORDER BY mu.started_at DESC
        LIMIT 50
    ");

    $uploads = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($uploads)) {
        echo "No multipart uploads found.\n";
        return;
    }

    echo "Multipart Uploads:\n";
    echo str_repeat("-", 120) . "\n";
    printf("%-5s %-10s %-30s %-15s %-10s %-15s %-20s\n",
        "ID", "Backup ID", "File", "Size", "Progress", "Status", "Started");
    echo str_repeat("-", 120) . "\n";

    foreach ($uploads as $upload) {
        $completedParts = 0;
        if (!empty($upload['completed_parts'])) {
            $parts = json_decode($upload['completed_parts'], true);
            $completedParts = is_array($parts) ? count($parts) : 0;
        }

        $progress = $upload['total_parts'] > 0
            ? number_format(($completedParts / $upload['total_parts']) * 100, 1) . '%'
            : '0%';

        $fileName = basename($upload['key']);
        if (strlen($fileName) > 30) {
            $fileName = substr($fileName, 0, 27) . '...';
        }

        printf("%-5s %-10s %-30s %-15s %-10s %-15s %-20s\n",
            $upload['id'],
            $upload['backup_id'],
            $fileName,
            formatBytes($upload['file_size']),
            "{$completedParts}/{$upload['total_parts']} ({$progress})",
            $upload['status'],
            $upload['started_at']
        );
    }
}

function resumeUpload($db, $uploadId) {
    // Get upload details
    $stmt = $db->prepare("
        SELECT mu.*, bh.backup_name, bh.backup_path
        FROM multipart_uploads mu
        JOIN backup_history bh ON mu.backup_id = bh.id
        WHERE mu.id = ?
    ");
    $stmt->execute([$uploadId]);
    $upload = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$upload) {
        throw new Exception("Upload ID {$uploadId} not found");
    }

    if ($upload['status'] === 'completed') {
        echo "Upload is already completed.\n";
        return;
    }

    if ($upload['status'] === 'aborted') {
        echo "Upload was aborted. Cannot resume.\n";
        return;
    }

    echo "Resuming upload for: {$upload['backup_name']}\n";
    echo "File: {$upload['file_path']}\n";
    echo "Progress: " . getUploadProgress($upload) . "\n\n";

    // Check if file still exists
    if (!file_exists($upload['file_path'])) {
        echo "Error: Source file not found: {$upload['file_path']}\n";
        return;
    }

    // Update status to in_progress
    $stmt = $db->prepare("UPDATE multipart_uploads SET status = 'in_progress' WHERE id = ?");
    $stmt->execute([$uploadId]);

    // Trigger backup worker to process the upload
    echo "Upload marked for resumption. The backup worker will process it on the next run.\n";

    // Optionally trigger backup worker immediately
    $workerScript = __DIR__ . '/backup-worker.php';
    if (file_exists($workerScript)) {
        echo "Starting backup worker to process upload...\n";
        exec("php " . escapeshellarg($workerScript) . " > /dev/null 2>&1 &");
    }
}

function abortUpload($db, $uploadId) {
    // Get upload details
    $stmt = $db->prepare("SELECT * FROM multipart_uploads WHERE id = ?");
    $stmt->execute([$uploadId]);
    $upload = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$upload) {
        throw new Exception("Upload ID {$uploadId} not found");
    }

    if ($upload['status'] === 'completed') {
        echo "Upload is already completed. Cannot abort.\n";
        return;
    }

    echo "Aborting upload ID {$uploadId}...\n";

    // Load backup target configuration
    require_once(__DIR__ . '/../libs/BackupTarget.php');
    require_once(__DIR__ . '/../libs/BackupStorage.php');

    $stmt = $db->prepare("
        SELECT bt.*
        FROM backup_history bh
        JOIN backup_targets bt ON bh.target_id = bt.id
        WHERE bh.id = ?
    ");
    $stmt->execute([$upload['backup_id']]);
    $targetData = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($targetData) {
        try {
            $target = new WHPBackup\BackupTarget($db, $targetData['id']);
            $storage = new WHPBackup\BackupStorage($target->getCredentials());

            // Abort the multipart upload on S3
            $storage->s3Client->abortMultipartUpload(
                $upload['bucket'],
                $upload['key'],
                $upload['upload_id']
            );

            echo "Multipart upload aborted on S3.\n";
        } catch (Exception $e) {
            echo "Warning: Could not abort on S3: " . $e->getMessage() . "\n";
        }
    }

    // Update database status
    $stmt = $db->prepare("
        UPDATE multipart_uploads
        SET status = 'aborted', completed_at = NOW()
        WHERE id = ?
    ");
    $stmt->execute([$uploadId]);

    echo "Upload marked as aborted in database.\n";
}

function cleanupStaleUploads($db) {
    echo "Cleaning up stale uploads (older than 7 days)...\n";

    // Find stale uploads
    $stmt = $db->query("
        SELECT id, upload_id, bucket, key
        FROM multipart_uploads
        WHERE status IN ('in_progress', 'failed')
        AND updated_at < DATE_SUB(NOW(), INTERVAL 7 DAY)
    ");

    $staleUploads = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($staleUploads)) {
        echo "No stale uploads found.\n";
        return;
    }

    echo "Found " . count($staleUploads) . " stale uploads.\n";

    foreach ($staleUploads as $upload) {
        echo "Aborting upload ID {$upload['id']}...\n";
        try {
            abortUpload($db, $upload['id']);
        } catch (Exception $e) {
            echo "Error aborting upload {$upload['id']}: " . $e->getMessage() . "\n";
        }
    }

    echo "Cleanup completed.\n";
}

function getUploadProgress($upload) {
    $completedParts = 0;
    if (!empty($upload['completed_parts'])) {
        $parts = json_decode($upload['completed_parts'], true);
        $completedParts = is_array($parts) ? count($parts) : 0;
    }

    $percentage = $upload['total_parts'] > 0
        ? ($completedParts / $upload['total_parts']) * 100
        : 0;

    return "{$completedParts}/{$upload['total_parts']} parts (" . number_format($percentage, 1) . "%)";
}

function formatBytes($bytes) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $bytes = max($bytes, 0);
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);
    $bytes /= (1 << (10 * $pow));
    return number_format($bytes, 2) . ' ' . $units[$pow];
}