<?php

declare(strict_types=1);

use App\Application\LiveComponents\Dashboard\FailedJobsListComponent;
use App\Application\LiveComponents\Dashboard\FailedJobsState;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\Queue\Services\DeadLetterManager;
use App\Framework\Queue\Entities\DeadLetterJob;
use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Core\ValueObjects\Timestamp;

describe('FailedJobsListComponent', function () {
    beforeEach(function () {
        $this->deadLetterManager = Mockery::mock(DeadLetterManager::class);
        $this->componentId = ComponentId::create('failed-jobs', 'test');
        $this->initialState = FailedJobsState::empty();
    });

    afterEach(function () {
        Mockery::close();
    });

    it('polls and updates state with failed jobs', function () {
        $failedJob1 = new DeadLetterJob(
            id: JobId::generate(),
            queueName: new QueueName('default'),
            jobType: 'SendEmailJob',
            payload: ['email' => 'test@example.com'],
            exception: 'Connection timeout',
            failedAt: Timestamp::now(),
            attempts: 3
        );

        $failedJob2 = new DeadLetterJob(
            id: JobId::generate(),
            queueName: new QueueName('high-priority'),
            jobType: 'ProcessPaymentJob',
            payload: ['amount' => 100],
            exception: 'Payment gateway error',
            failedAt: Timestamp::now(),
            attempts: 5
        );

        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->with(50)
            ->andReturn([$failedJob1, $failedJob2]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->poll();

        expect($newState)->toBeInstanceOf(FailedJobsState::class);
        expect($newState->totalFailedJobs)->toBe(2);
        expect($newState->failedJobs)->toHaveCount(2);
        expect($newState->failedJobs[0]['job_type'])->toBe('SendEmailJob');
        expect($newState->failedJobs[0]['queue'])->toBe('default');
        expect($newState->failedJobs[0]['attempts'])->toBe(3);
    });

    it('handles retry job action successfully', function () {
        $jobId = JobId::generate();

        $this->deadLetterManager->shouldReceive('retryJob')
            ->once()
            ->with($jobId->toString())
            ->andReturn(true);

        // After retry, poll returns updated state
        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->with(50)
            ->andReturn([]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->retryJob($jobId->toString());

        expect($newState)->toBeInstanceOf(FailedJobsState::class);
        expect($newState->totalFailedJobs)->toBe(0); // Job was retried
    });

    it('handles retry job action failure', function () {
        $jobId = JobId::generate();

        $this->deadLetterManager->shouldReceive('retryJob')
            ->once()
            ->with($jobId->toString())
            ->andReturn(false);

        // Still poll for current state
        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->andReturn([]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->retryJob($jobId->toString());

        expect($newState)->toBeInstanceOf(FailedJobsState::class);
    });

    it('handles delete job action successfully', function () {
        $jobId = JobId::generate();

        $this->deadLetterManager->shouldReceive('deleteJob')
            ->once()
            ->with($jobId->toString())
            ->andReturn(true);

        // After delete, poll returns updated state
        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->with(50)
            ->andReturn([]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->deleteJob($jobId->toString());

        expect($newState)->toBeInstanceOf(FailedJobsState::class);
        expect($newState->totalFailedJobs)->toBe(0);
    });

    it('dispatches events on retry success', function () {
        $jobId = JobId::generate();
        $eventDispatcher = Mockery::mock(ComponentEventDispatcher::class);

        $this->deadLetterManager->shouldReceive('retryJob')
            ->once()
            ->andReturn(true);

        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->andReturn([]);

        $eventDispatcher->shouldReceive('dispatch')
            ->once()
            ->with('failed-jobs:retry-success', ['jobId' => $jobId->toString()]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $component->retryJob($jobId->toString(), $eventDispatcher);
    });

    it('has correct poll interval', function () {
        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        expect($component->getPollInterval())->toBe(10000);
    });

    it('returns correct render data', function () {
        $failedJobs = [
            [
                'id' => 'job-1',
                'queue' => 'default',
                'job_type' => 'EmailJob',
                'error' => 'Connection failed',
                'failed_at' => '2024-01-15 12:00:00',
                'attempts' => 3,
            ],
        ];

        $state = new FailedJobsState(
            totalFailedJobs: 1,
            failedJobs: $failedJobs,
            statistics: ['total_retries' => 5],
            lastUpdated: '2024-01-15 12:00:00'
        );

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $state,
            deadLetterManager: $this->deadLetterManager
        );

        $renderData = $component->getRenderData();

        expect($renderData->templatePath)->toBe('livecomponent-failed-jobs-list');
        expect($renderData->data)->toHaveKey('componentId');
        expect($renderData->data)->toHaveKey('pollInterval');
        expect($renderData->data['pollInterval'])->toBe(10000);
        expect($renderData->data['totalFailedJobs'])->toBe(1);
        expect($renderData->data['failedJobs'])->toHaveCount(1);
    });

    it('handles empty failed jobs list', function () {
        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->with(50)
            ->andReturn([]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->poll();

        expect($newState->totalFailedJobs)->toBe(0);
        expect($newState->failedJobs)->toBe([]);
    });

    it('truncates payload preview to 100 characters', function () {
        $longPayload = str_repeat('a', 200);

        $failedJob = new DeadLetterJob(
            id: JobId::generate(),
            queueName: new QueueName('default'),
            jobType: 'LongJob',
            payload: ['data' => $longPayload],
            exception: 'Error',
            failedAt: Timestamp::now(),
            attempts: 1
        );

        $this->deadLetterManager->shouldReceive('getFailedJobs')
            ->once()
            ->with(50)
            ->andReturn([$failedJob]);

        $component = new FailedJobsListComponent(
            id: $this->componentId,
            state: $this->initialState,
            deadLetterManager: $this->deadLetterManager
        );

        $newState = $component->poll();

        $payloadPreview = $newState->failedJobs[0]['payload_preview'];
        expect(strlen($payloadPreview))->toBeLessThanOrEqual(103); // 100 + '...'
    });
});
