<?php

namespace Tests\Unit;

use App\Domain\Shared\Traits\ChecksTemporaryUploads;
use Tests\TestCase;

/**
 * A pending upload whose temporary file has gone.
 *
 * This is not a hypothetical: every push redeploys the container, and
 * `livewire-tmp` does not survive that. A receptionist who picks a receipt on
 * their phone and presses Save a minute later hit a 500 with a support code
 * (eab81218-…, an expense receipt, 2 September) because validation's `max:5120`
 * calls getSize() on a file that is no longer there.
 *
 * Boots the framework because the message it produces goes through __().
 */
class ChecksTemporaryUploadsTest extends TestCase
{
    private function subject(): object
    {
        return new class {
            use ChecksTemporaryUploads;

            public mixed $receipt = null;
            public array $errors = [];

            public function addError(string $key, string $message): void
            {
                $this->errors[$key] = $message;
            }

            public function check(): bool
            {
                return $this->temporaryUploadIsUsable('receipt', $this->receipt);
            }
        };
    }

    private function upload(bool $exists): object
    {
        return new class($exists) extends \Livewire\Features\SupportFileUploads\TemporaryUploadedFile {
            public function __construct(private bool $isThere)
            {
                // Deliberately not calling the parent constructor: this stands
                // in for a temporary file, and touching the disk is the very
                // thing under test.
            }

            public function exists(): bool
            {
                return $this->isThere;
            }
        };
    }

    public function test_a_file_that_is_still_there_passes_through(): void
    {
        $component = $this->subject();
        $component->receipt = $this->upload(exists: true);

        $this->assertTrue($component->check());
        $this->assertSame([], $component->errors);
    }

    public function test_a_vanished_file_is_refused_with_a_sentence_not_an_exception(): void
    {
        $component = $this->subject();
        $component->receipt = $this->upload(exists: false);

        $this->assertFalse($component->check());
        $this->assertArrayHasKey('receipt', $component->errors);
        $this->assertStringContainsString('اختر الملف مرة أخرى', $component->errors['receipt']);
        $this->assertNull($component->receipt, 'The dead handle is cleared so the next attempt starts clean.');
    }

    public function test_a_storage_failure_counts_as_gone_rather_than_blowing_up(): void
    {
        $component = $this->subject();
        $component->receipt = new class extends \Livewire\Features\SupportFileUploads\TemporaryUploadedFile {
            public function __construct() {}

            public function exists(): bool
            {
                throw new \RuntimeException('disk unreachable');
            }
        };

        $this->assertFalse($component->check());
    }

    public function test_an_array_of_uploads_is_refused_if_any_one_is_gone(): void
    {
        $component = $this->subject();
        $component->receipt = [$this->upload(exists: true), $this->upload(exists: false)];

        $this->assertFalse($component->check());
        $this->assertSame([], $component->receipt, 'An array property is cleared to an array.');
    }

    public function test_nothing_pending_is_not_a_missing_file(): void
    {
        // An empty field is the validator's business, not this guard's.
        $component = $this->subject();
        $component->receipt = null;

        $this->assertTrue($component->check());
        $this->assertSame([], $component->errors);
    }

    public function test_an_already_stored_path_is_left_alone(): void
    {
        $component = $this->subject();
        $component->receipt = 'expenses/receipts/already-saved.jpg';

        $this->assertTrue($component->check());
    }
}
