<?php

use App\Models\Permission;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

function makeEditorUser(): User
{
    $user = User::factory()->create();
    $perm = Permission::firstOrCreate(['name' => 'manage global settings', 'guard_name' => 'web']);
    $user->givePermissionTo($perm);

    return $user;
}

beforeEach(function () {
    Storage::fake('public');
});

// ── Access control ─────────────────────────────────────────────────────────────

test('guest cannot upload editor images', function () {
    $this->postJson('/editor/upload')->assertUnauthorized();
});

test('user without manage global settings cannot upload', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/editor/upload')
        ->assertForbidden();
});

// ── Missing file ───────────────────────────────────────────────────────────────

test('upload without file returns 400', function () {
    $user = makeEditorUser();

    $this->actingAs($user)
        ->postJson('/editor/upload')
        ->assertStatus(400)
        ->assertJsonPath('error.message', 'No file uploaded.');
});

// ── Type validation ────────────────────────────────────────────────────────────

test('upload rejects non-image file', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->create('shell.php', 100, 'application/x-php');

    $this->actingAs($user)
        ->postJson('/editor/upload', ['upload' => $file])
        ->assertStatus(422);
});

test('upload rejects svg (not in allowed mimes)', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->create('vector.svg', 50, 'image/svg+xml');

    $this->actingAs($user)
        ->postJson('/editor/upload', ['upload' => $file])
        ->assertStatus(422);
});

test('upload accepts jpeg image', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->image('photo.jpg', 800, 600);

    $this->actingAs($user)
        ->post('/editor/upload', ['upload' => $file])
        ->assertOk()
        ->assertJsonPath('uploaded', 1)
        ->assertJsonStructure(['url', 'fileName']);
});

test('upload accepts png image', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->image('icon.png', 100, 100);

    $this->actingAs($user)
        ->post('/editor/upload', ['upload' => $file])
        ->assertOk()
        ->assertJsonPath('uploaded', 1);
});

test('upload accepts webp image', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->image('modern.webp', 400, 300);

    $this->actingAs($user)
        ->post('/editor/upload', ['upload' => $file])
        ->assertOk()
        ->assertJsonPath('uploaded', 1);
});

// ── Size limit ─────────────────────────────────────────────────────────────────

test('upload rejects image exceeding 5 MB', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->create('big.jpg', 6000, 'image/jpeg'); // 6 MB

    $this->actingAs($user)
        ->postJson('/editor/upload', ['upload' => $file])
        ->assertStatus(422);
});

// ── Response structure ─────────────────────────────────────────────────────────

test('successful upload response has uploaded, fileName, url keys', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->image('test.jpg');

    $response = $this->actingAs($user)
        ->post('/editor/upload', ['upload' => $file])
        ->assertOk()
        ->json();

    expect($response)->toHaveKeys(['uploaded', 'fileName', 'url']);
    expect($response['uploaded'])->toBe(1);
    expect($response['url'])->toStartWith('/storage/');
});

// ── File is actually stored ────────────────────────────────────────────────────

test('uploaded file is persisted to the public disk under editor/', function () {
    $user = makeEditorUser();
    $file = UploadedFile::fake()->image('stored.jpg');

    $response = $this->actingAs($user)
        ->post('/editor/upload', ['upload' => $file])
        ->json();

    // Strip /storage/ prefix to get the relative path on the public disk
    $relativePath = substr((string) $response['url'], strlen('/storage/'));
    Storage::disk('public')->assertExists($relativePath);
});
