<?php

namespace Tests\Unit\App\Notifications;

use App\Dtos\InformaciaDto;
use App\Models\Informacia\Informacia;
use App\Models\Pouzivatel\Pouzivatel;
use App\Models\Pracovisko\Pracovisko;
use App\Notifications\InformaciaPouzivatelNotification;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

final class InformaciaPouzivatelNotificationTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function it_sends_notification_to_user_via_database_and_mail_when_email_present(): void
    {
        Notification::fake();

        // Given a user with an email
        /** @var Pracovisko $pek */
        $pek = Pracovisko::factory()->create()->refresh();

        // Given a user with an email
        /** @var Pouzivatel $user */
        $user = Pouzivatel::factory()
            ->create([
                'pracovisko_id' => intval($pek->getKey()),
            ])
            ->refresh()
            ->load('pracovisko');

        // And an Informacia converted to DTO
        /** @var Informacia $informacia */
        $informacia = Informacia::factory()
            ->create([
                'vytvoril_id' => intval($user->getKey()),
                'odoslat_email_pouzivatelom' => true,
            ])
            ->refresh();
        $dto = InformaciaDto::from($informacia);

        // When notifying the user
        $notification = new InformaciaPouzivatelNotification($dto);
        $user->notify($notification);

        // Then the notification was sent to the user
        Notification::assertSentTo(
            $user,
            InformaciaPouzivatelNotification::class,
            function ($sentNotification, $channels) use ($user, $notification, $dto) {
                // Assert channels determined by via()
                $via = $notification->via($user);
                $this->assertContains('database', $via);
                $this->assertContains('mail', $via);

                // Assert database payload equals DTO array
                $this->assertSame($dto->toArray(), $sentNotification->toDatabase($user));

                // Assert toArray contains minimal fields
                $arrayPayload = $sentNotification->toArray($user);
                $this->assertArrayHasKey('informacia_id', $arrayPayload);
                $this->assertArrayHasKey('titulok', $arrayPayload);
                $this->assertArrayHasKey('obsah', $arrayPayload);

                return true;
            },
        );
    }

    #[Test]
    public function it_sends_notification_to_user_via_database_only_when_email_missing(): void
    {
        Notification::fake();

        // Given a user with an email
        /** @var Pracovisko $pek */
        $pek = Pracovisko::factory()->create();

        // Given a user with an email
        /** @var Pouzivatel $user */
        $user = Pouzivatel::factory()->make();
        $user->email = null;
        $user->pracovisko()->associate($pek)->save();
        $user->refresh();

        // And an Informacia converted to DTO
        /** @var Informacia $informacia */
        $informacia = Informacia::factory()
            ->make([
                'odoslat_email_pouzivatelom' => true,
            ])
            ->vytvoril()
            ->associate($user);
        $informacia->save();
        $informacia->refresh();
        $dto = InformaciaDto::from($informacia);

        // When notifying the user
        $notification = new InformaciaPouzivatelNotification($dto);
        $user->notify($notification);

        // Then the notification was sent
        Notification::assertSentTo(
            $user,
            InformaciaPouzivatelNotification::class,
            function ($sentNotification) use ($user, $notification) {
                // Channels should only include database
                $via = $notification->via($user);
                $this->assertContains('database', $via);
                $this->assertNotContains('mail', $via);

                return true;
            },
        );
    }

    protected function setUp(): void
    {
        parent::setUp();

        Event::fake();
    }
}
