<?php

use App\Enums\Day;
use App\Livewire\Studio\Master\Outlet\Edit;
use App\Livewire\Studio\Master\Outlet\Index;
use App\Models\Facility;
use App\Models\Outlet;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->user = User::factory()->create();
});

function makeLivewireFile(string $name)
{
    $file = UploadedFile::fake()->image($name, 100, 100);
    $tmpId = Str::random(32);
    $filename = $tmpId.'-'.base64_encode($file->getClientOriginalName()).'.'.$file->getClientOriginalExtension();
    $path = "livewire-tmp/{$filename}";

    Storage::disk('local')->putFileAs('livewire-tmp', $file, $filename);

    return [
        'tmpFilename' => $filename,
        'name' => $file->getClientOriginalName(),
        'extension' => $file->getClientOriginalExtension(),
        'path' => storage_path("app/private/{$path}"),
        'temporaryUrl' => url("livewire/preview-file/{$filename}"),
        'size' => $file->getSize(),
    ];
}

function mountEditComponent(User $user, Outlet $outlet)
{
    return Livewire::actingAs($user)->test(Edit::class, ['outlet' => $outlet]);
}

it('renders page successfully', function () {
    $outlet = Outlet::factory()->operational()->create();

    mountEditComponent($this->user, $outlet)
        ->assertViewIs('livewire.studio.master.outlet.form')
        ->assertViewHas('pageTitle', 'Ubah Outlet');
});

it('mounts days and sets form with outlet', function () {
    $outlet = Outlet::factory()->operational()->create();
    $component = mountEditComponent($this->user, $outlet);

    $days = Day::cases();
    expect($component->days)->toHaveCount(count($days));
    expect($component->form->outlet->id)->toBe($outlet->id);
});

it('prevents update when user is unauthorized', function () {
    $outlet = Outlet::factory()->operational()->create();
    Gate::define('update outlet', fn () => false);

    mountEditComponent($this->user, $outlet)
        ->call('save')
        ->assertForbidden();
});

it('updates outlet successfully when authorized', function () {
    $permission = Permission::create(['name' => 'update outlet']);
    $this->user->givePermissionTo($permission);

    $outletModel = Outlet::factory()->operational()->create(); // model instance
    $outletData = Outlet::factory()->operational()->raw(); // data for update
    $facilities = Facility::factory()->count(5)->raw();

    $featuredImage = makeLivewireFile('featured.jpg');
    for ($i = 1; $i <= 5; $i++) {
        $images[] = makeLivewireFile("image{$i}.jpg");
    }

    $openingHours = collect(Day::cases())
        ->mapWithKeys(fn ($day) => [
            $day->value => [
                'day' => $day->value,
                'open_time' => fake()->time('H:i'),
                'close_time' => fake()->time('H:i'),
            ],
        ])
        ->toArray();

    $data = array_merge($outletData, [
        'opening_hours' => $openingHours,
        'facilities' => collect($facilities)->pluck('name')->toArray(),
        'featured_image' => [$featuredImage],
        'images' => $images,
    ]);

    mountEditComponent($this->user, $outletModel)
        ->set('form.name', $data['name'])
        ->set('form.phone_number', $data['phone_number'])
        ->set('form.address', $data['address'])
        ->set('form.landmark', $data['landmark'])
        ->set('form.maps_url', $data['maps_url'])
        ->set('form.opened_date', $data['opened_date'])
        ->set('form.status', $data['status']->value)
        ->set('form.opening_hours', $data['opening_hours'])
        ->set('form.facilities', $data['facilities'])
        ->set('form.featured_image', $data['featured_image'])
        ->set('form.images', $data['images'])
        ->call('save')
        ->assertHasNoErrors()
        ->assertRedirect(Index::class);

    $outletModel->refresh();

    $this->assertDatabaseHas('outlets', [
        'id' => $outletModel->id,
        'name' => $data['name'],
        'phone_number' => $data['phone_number'],
    ]);

    $this->assertCount(1, $outletModel->getMedia('featured_image'));
    $this->assertCount(5, $outletModel->getMedia('images'));

    expect($outletModel->getFirstMedia('featured_image')->getPath())->toBeFile();
    foreach ($outletModel->getMedia('images') as $media) {
        expect($media->getPath())->toBeFile();
    }
});
