<?php

declare(strict_types=1);

require_once __DIR__ . '/../bootstrap.php';

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Filesystem\FileStorage;
use App\Framework\Queue\FileQueue;
use App\Framework\Queue\ValueObjects\JobPayload;
use App\Framework\Queue\ValueObjects\QueuePriority;

// Example Job Classes for Tests
class FileSystemTestJob
{
    public function __construct(
        public string $id,
        public string $description
    ) {
    }
}

class FileCriticalTask
{
    public function __construct(
        public string $taskId,
        public string $urgency
    ) {
    }
}

echo "🗂️ Testing FileQueue with Filesystem Module Integration\n";
echo "=====================================================\n\n";

// Test 1: Basic FileQueue with Filesystem Module
echo "1️⃣  Testing Basic FileQueue Operations with Filesystem Module...\n";

// Create temporary queue directory
$queuePath = __DIR__ . '/../tmp/file_queue_test_' . uniqid();
mkdir($queuePath, 0777, true);

$storage = new FileStorage();
$queue = new FileQueue($queuePath, storage: $storage);

$testJob = new FileSystemTestJob('job-1', 'Test filesystem integration');
$payload = JobPayload::create($testJob, QueuePriority::normal());

$queue->push($payload);

echo "   📊 Queue size after push: " . $queue->size() . "\n";

// Debug: Check directory contents
$priorityFiles = $queue->getPriorityJobFiles();
echo "   📁 Priority files count: " . count($priorityFiles) . "\n";

$delayedFiles = $queue->getDelayedJobFiles();
echo "   ⏱️ Delayed files count: " . count($delayedFiles) . "\n";

assert($queue->size() === 1, "❌ Queue should have 1 job");

$poppedPayload = $queue->pop();
assert($poppedPayload !== null, "❌ Should pop a payload");
assert($poppedPayload->job instanceof FileSystemTestJob, "❌ Should be FileSystemTestJob");
assert($poppedPayload->job->id === 'job-1', "❌ Job ID should match");

assert($queue->size() === 0, "❌ Queue should be empty after pop");

echo "✅ Basic FileQueue operations with Filesystem module work correctly\n\n";

// Test 2: Priority-based Job Processing
echo "2️⃣  Testing Priority-based Job Processing...\n";

$queue->clear();

$lowJob = JobPayload::create(new FileSystemTestJob('low', 'Low priority'), QueuePriority::low());
$normalJob = JobPayload::create(new FileSystemTestJob('normal', 'Normal priority'), QueuePriority::normal());
$highJob = JobPayload::create(new FileSystemTestJob('high', 'High priority'), QueuePriority::high());
$criticalJob = JobPayload::create(new FileCriticalTask('critical', 'Critical task'), QueuePriority::critical());

// Push in random order
$queue->push($normalJob);
$queue->push($lowJob);
$queue->push($criticalJob);
$queue->push($highJob);

assert($queue->size() === 4, "❌ Queue should have 4 jobs");

// Should pop in priority order: critical > high > normal > low
$first = $queue->pop();
$second = $queue->pop();
$third = $queue->pop();
$fourth = $queue->pop();

assert($first !== null && $first->job instanceof FileCriticalTask, "❌ First should be critical task");
assert($second !== null && $second->job->id === 'high', "❌ Second should be high priority");
assert($third !== null && $third->job->id === 'normal', "❌ Third should be normal priority");
assert($fourth !== null && $fourth->job->id === 'low', "❌ Fourth should be low priority");

echo "✅ Priority-based job processing works correctly\n\n";

// Test 3: Delayed Jobs with Filesystem
echo "3️⃣  Testing Delayed Jobs with Filesystem Module...\n";

$queue->clear();

$immediateJob = JobPayload::create(new FileSystemTestJob('immediate', 'Process now'));
$delayedJob = JobPayload::create(
    new FileSystemTestJob('delayed', 'Process later'),
    QueuePriority::normal(),
    Duration::fromSeconds(2) // 2 second delay
);

$queue->push($delayedJob);
$queue->push($immediateJob);

// Should only get immediate job initially
$popped = $queue->pop();
assert($popped !== null, "❌ Should pop immediate job");
assert($popped->job->id === 'immediate', "❌ Should be immediate job");

// Delayed job should still be in queue but not poppable yet
assert($queue->size() === 1, "❌ Should have 1 delayed job remaining");

// Wait for delay to pass and try again
echo "   ⏳ Waiting 3 seconds for delayed job to become ready...\n";
sleep(3);

$delayedPopped = $queue->pop();
assert($delayedPopped !== null, "❌ Should pop delayed job after delay");
assert($delayedPopped->job->id === 'delayed', "❌ Should be delayed job");

echo "✅ Delayed jobs with filesystem work correctly\n\n";

// Test 4: Peek Operations
echo "4️⃣  Testing Peek Operations...\n";

$queue->clear();

$peekJob = JobPayload::create(new FileSystemTestJob('peek', 'Test peek'));
$queue->push($peekJob);

$originalSize = $queue->size();
$peeked = $queue->peek();

assert($peeked !== null, "❌ Should peek a payload");
assert($peeked->job->id === 'peek', "❌ Peeked job should have correct data");
assert($queue->size() === $originalSize, "❌ Queue size should not change after peek");

echo "✅ Peek operations work correctly\n\n";

// Test 5: Queue Statistics with Priority Breakdown
echo "5️⃣  Testing Queue Statistics...\n";

$queue->clear();

$payload1 = JobPayload::create(new FileSystemTestJob('stats-1', 'test'), QueuePriority::high());
$payload2 = JobPayload::create(new FileSystemTestJob('stats-2', 'test'), QueuePriority::normal());
$payload3 = JobPayload::create(new FileSystemTestJob('stats-3', 'test'), QueuePriority::high());

$queue->push($payload1);
$queue->push($payload2);
$queue->push($payload3);

$stats = $queue->getStats();

assert($stats['total_size'] === 3, "❌ Stats should show 3 total jobs");
assert($stats['priority_queue_size'] === 3, "❌ Stats should show 3 priority jobs");
assert($stats['delayed_queue_size'] === 0, "❌ Stats should show 0 delayed jobs");
assert(isset($stats['priority_breakdown']), "❌ Stats should include priority breakdown");

$breakdown = $stats['priority_breakdown'];
assert(isset($breakdown['high']), "❌ Should have high priority breakdown");
assert(isset($breakdown['normal']), "❌ Should have normal priority breakdown");
assert($breakdown['high'] === 2, "❌ Should have 2 high priority jobs");
assert($breakdown['normal'] === 1, "❌ Should have 1 normal priority job");

echo "✅ Queue statistics work correctly\n\n";

// Test 6: Directory Structure Verification
echo "6️⃣  Testing Directory Structure...\n";

// Verify that the filesystem module created the correct directory structure
$priorityDir = $queuePath . '/priority';
$delayedDir = $queuePath . '/delayed';

assert(is_dir($priorityDir), "❌ Priority directory should exist");
assert(is_dir($delayedDir), "❌ Delayed directory should exist");

echo "✅ Directory structure is correct\n\n";

// Test 7: Clear Operations
echo "7️⃣  Testing Clear Operations...\n";

$queue->clear();

$payload1 = JobPayload::create(new FileSystemTestJob('clear-1', 'test'));
$payload2 = JobPayload::create(new FileSystemTestJob('clear-2', 'test'));
$delayedPayload = JobPayload::create(
    new FileSystemTestJob('clear-delayed', 'test'),
    QueuePriority::normal(),
    Duration::fromSeconds(3600)
);

$queue->push($payload1);
$queue->push($payload2);
$queue->push($delayedPayload);

assert($queue->size() === 3, "❌ Should have 3 jobs before clear");

$clearedCount = $queue->clear();
assert($clearedCount === 3, "❌ Should report 3 jobs cleared");
assert($queue->size() === 0, "❌ Queue should be empty after clear");

echo "✅ Clear operations work correctly\n\n";

// Test 8: Empty Queue Handling
echo "8️⃣  Testing Empty Queue Handling...\n";

$queue->clear();
assert($queue->size() === 0, "❌ Queue should be empty");

$nullPayload = $queue->pop();
assert($nullPayload === null, "❌ Popping from empty queue should return null");

$nullPeek = $queue->peek();
assert($nullPeek === null, "❌ Peeking empty queue should return null");

echo "✅ Empty queue handling works correctly\n\n";

// Cleanup
echo "🧹 Cleaning up test files...\n";

function deleteDirectory($dir)
{
    if (! is_dir($dir)) {
        return;
    }

    $files = array_diff(scandir($dir), ['.', '..']);
    foreach ($files as $file) {
        $path = $dir . DIRECTORY_SEPARATOR . $file;
        if (is_dir($path)) {
            deleteDirectory($path);
        } else {
            unlink($path);
        }
    }
    rmdir($dir);
}

deleteDirectory($queuePath);

echo "🎉 ALL FILEQUEUE FILESYSTEM TESTS PASSED!\n";
echo "✨ FileQueue with Filesystem module integration is working correctly!\n";
