<?php

use App\Enums\Day;
use App\Livewire\Studio\Master\Outlet\Create;
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 mountCreateComponent(User $user)
{
    return Livewire::actingAs($user)->test(Create::class);
}

it('renders page successfully', function () {
    mountCreateComponent($this->user)
        ->assertViewIs('livewire.studio.master.outlet.form')
        ->assertViewHas('pageTitle', 'Tambah Outlet');
});

it('mounts days and form opening_hours correctly', function () {
    $component = mountCreateComponent($this->user);
    $days = Day::cases();

    expect($component->days)->toHaveCount(count($days));

    foreach ($days as $day) {
        expect(array_key_exists($day->value, $component->form->opening_hours))->toBeTrue();
        expect($component->form->opening_hours[$day->value])->toBe(['open_time' => null, 'close_time' => null]);
    }
});

it('prevents create when user is unauthorized', function () {
    Gate::define('create outlet', fn () => false);

    mountCreateComponent($this->user)
        ->call('save')
        ->assertForbidden();
});

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

    $outlet = Outlet::factory()->operational()->raw();
    $facilities = Facility::factory()->count(5)->raw();

    $featuredImage = makeLivewireFile('featured.jpg');
    $images = [];

    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($outlet, [
        'opening_hours' => $openingHours,
        'facilities' => collect($facilities)->pluck('name')->toArray(),
        'featured_image' => [$featuredImage],
        'images' => $images,
    ]);

    mountCreateComponent($this->user)
        ->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);

    $outlet = Outlet::first();

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

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

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