<?php

namespace Tests\Feature;

use App\Jobs\ConvertDocumentToMarkdown;
use App\Models\Document;
use App\Models\User;
use App\Services\DocumentConversionService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

/**
 * Swoole 队列系统兼容性测试
 * 
 * 验证现有队列任务在 Swoole 环境下的正常运行
 */
class SwooleQueueCompatibilityTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        
        // 设置测试存储磁盘
        Storage::fake('documents');
        Storage::fake('markdown');
        
        // 禁用搜索功能以避免 Meilisearch 连接问题
        config(['scout.driver' => 'null']);
    }

    /**
     * 测试文档转换队列任务可以正常分发
     * 
     * @test
     */
    public function test_document_conversion_job_can_be_dispatched()
    {
        // 创建测试用户和文档
        $user = User::factory()->create();
        $document = Document::factory()->create([
            'uploaded_by' => $user->id,
            'title' => '测试文档',
            'file_path' => 'test-document.docx',
        ]);

        // 模拟队列
        Queue::fake();

        // 分发队列任务
        ConvertDocumentToMarkdown::dispatch($document);

        // 验证任务已被分发
        Queue::assertPushed(ConvertDocumentToMarkdown::class, function ($job) use ($document) {
            // 使用反射来访问受保护的属性
            $reflection = new \ReflectionClass($job);
            $documentProperty = $reflection->getProperty('document');
            $documentProperty->setAccessible(true);
            $jobDocument = $documentProperty->getValue($job);
            return $jobDocument->id === $document->id;
        });
    }

    /**
     * 测试队列任务在 Swoole 环境下的执行
     * 
     * @test
     */
    public function test_queue_job_execution_in_swoole_environment()
    {
        // 创建测试用户和文档
        $user = User::factory()->create();
        $document = Document::factory()->create([
            'uploaded_by' => $user->id,
            'title' => '测试文档转换',
            'file_path' => 'test-conversion.docx',
        ]);

        // 创建模拟的文档文件
        Storage::disk('documents')->put($document->file_path, 'test content');

        // 模拟转换服务
        $conversionService = $this->createMock(DocumentConversionService::class);
        $conversionService->expects($this->once())
            ->method('convertToMarkdown')
            ->with($document)
            ->willReturn([
                'markdown' => '# 测试文档\n\n这是测试内容',
                'tempDir' => '/tmp/test',
            ]);

        $conversionService->expects($this->once())
            ->method('saveMarkdownToFile')
            ->willReturn('markdown/test-document.md');

        $conversionService->expects($this->once())
            ->method('updateDocumentMarkdown');

        $this->app->instance(DocumentConversionService::class, $conversionService);

        // 执行队列任务
        $job = new ConvertDocumentToMarkdown($document);
        $job->handle($conversionService);

        // 验证任务执行成功（没有抛出异常）
        $this->assertTrue(true);
    }

    /**
     * 测试队列任务失败处理
     * 
     * @test
     */
    public function test_queue_job_failure_handling()
    {
        // 创建测试用户和文档
        $user = User::factory()->create();
        $document = Document::factory()->create([
            'uploaded_by' => $user->id,
            'title' => '失败测试文档',
            'file_path' => 'fail-test.docx',
        ]);

        // 模拟转换服务抛出异常
        $conversionService = $this->createMock(DocumentConversionService::class);
        $conversionService->expects($this->once())
            ->method('convertToMarkdown')
            ->willThrowException(new \Exception('转换失败'));

        $this->app->instance(DocumentConversionService::class, $conversionService);

        // 执行队列任务并期望异常
        $job = new ConvertDocumentToMarkdown($document);
        
        $this->expectException(\Exception::class);
        $this->expectExceptionMessage('转换失败');
        
        $job->handle($conversionService);
    }

    /**
     * 测试队列任务可以包装底层 Throwable
     *
     * @test
     */
    public function test_queue_job_wraps_throwables_from_conversion_service()
    {
        $user = User::factory()->create();
        $document = Document::factory()->create([
            'uploaded_by' => $user->id,
            'title' => 'Throwable 测试文档',
            'file_path' => 'throwable-test.docx',
        ]);

        $conversionService = $this->createMock(DocumentConversionService::class);
        $conversionService->expects($this->once())
            ->method('convertToMarkdown')
            ->willThrowException(new \Error('底层转换错误'));

        $this->app->instance(DocumentConversionService::class, $conversionService);

        $job = new ConvertDocumentToMarkdown($document);

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage('底层转换错误');

        $job->handle($conversionService);
    }

    /**
     * 测试队列任务的重试机制
     * 
     * @test
     */
    public function test_queue_job_retry_mechanism()
    {
        // 创建测试文档
        $user = User::factory()->create();
        $document = Document::factory()->create([
            'uploaded_by' => $user->id,
            'title' => '重试测试文档',
        ]);

        // 创建队列任务实例
        $job = new ConvertDocumentToMarkdown($document);

        // 验证重试配置
        $this->assertEquals(config('documents.conversion.retry_times', 3), $job->tries);
        $this->assertEquals(config('documents.conversion.timeout', 300), $job->timeout);
        $this->assertEquals(config('documents.conversion.retry_delay', 60), $job->backoff);
    }

    /**
     * 测试队列连接配置
     * 
     * @test
     */
    public function test_queue_connection_configuration()
    {
        // 验证队列连接配置
        $defaultConnection = config('queue.default');
        $this->assertNotEmpty($defaultConnection);

        // 验证数据库队列连接配置
        $databaseConfig = config('queue.connections.database');
        $this->assertIsArray($databaseConfig);
        $this->assertEquals('database', $databaseConfig['driver']);
        $this->assertEquals('jobs', $databaseConfig['table']);
    }

    /**
     * 测试队列在 Swoole 环境下的内存管理
     * 
     * @test
     */
    public function test_queue_memory_management_in_swoole()
    {
        // 获取初始内存使用量
        $initialMemory = memory_get_usage();

        // 创建多个队列任务
        $user = User::factory()->create();
        $documents = Document::factory()->count(5)->create(['uploaded_by' => $user->id]);

        Queue::fake();

        // 分发多个任务
        foreach ($documents as $document) {
            ConvertDocumentToMarkdown::dispatch($document);
        }

        // 验证任务都被分发
        Queue::assertPushed(ConvertDocumentToMarkdown::class, 5);

        // 检查内存使用是否在合理范围内
        $currentMemory = memory_get_usage();
        $memoryIncrease = $currentMemory - $initialMemory;
        
        // 内存增长应该在合理范围内（小于 10MB）
        $this->assertLessThan(10 * 1024 * 1024, $memoryIncrease, '队列任务内存使用过多');
    }

    /**
     * 测试队列任务的序列化和反序列化
     * 
     * @test
     */
    public function test_queue_job_serialization()
    {
        // 创建测试文档
        $user = User::factory()->create();
        $document = Document::factory()->create(['uploaded_by' => $user->id]);

        // 创建队列任务
        $job = new ConvertDocumentToMarkdown($document);

        // 序列化任务
        $serialized = serialize($job);
        $this->assertIsString($serialized);

        // 反序列化任务
        $unserialized = unserialize($serialized);
        $this->assertInstanceOf(ConvertDocumentToMarkdown::class, $unserialized);
        
        // 使用反射来访问受保护的属性
        $reflection = new \ReflectionClass($unserialized);
        $documentProperty = $reflection->getProperty('document');
        $documentProperty->setAccessible(true);
        $jobDocument = $documentProperty->getValue($unserialized);
        $this->assertEquals($document->id, $jobDocument->id);
    }
}
