<?php

namespace Tests\Feature;

use App\Enums\NotificationSeverityEnum;
use App\Enums\NotificationTypeEnum;
use App\Models\Feed;
use App\Models\Notification;
use App\Services\Notification\NotificationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class NotificationTest extends TestCase
{
    use RefreshDatabase;

    private NotificationService $service;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = new NotificationService;
    }

    public function test_send_creates_notification_with_all_fields(): void
    {
        $notification = $this->service->send(
            NotificationTypeEnum::FEED_STALE,
            NotificationSeverityEnum::WARNING,
            'Feed is stale',
            'Feed "VRT" has not returned new articles in 24 hours.',
            data: ['hours_stale' => 24],
        );

        $this->assertDatabaseHas('notifications', [
            'type' => 'feed_stale',
            'severity' => 'warning',
            'title' => 'Feed is stale',
        ]);
        $this->assertEquals(['hours_stale' => 24], $notification->data);
        $this->assertNull($notification->read_at);
    }

    public function test_info_convenience_method(): void
    {
        $notification = $this->service->info('Test title', 'Test message');

        $this->assertEquals(NotificationTypeEnum::GENERAL, $notification->type);
        $this->assertEquals(NotificationSeverityEnum::INFO, $notification->severity);
    }

    public function test_warning_convenience_method(): void
    {
        $notification = $this->service->warning('Warning title', 'Warning message');

        $this->assertEquals(NotificationSeverityEnum::WARNING, $notification->severity);
    }

    public function test_error_convenience_method(): void
    {
        $notification = $this->service->error('Error title', 'Error message');

        $this->assertEquals(NotificationSeverityEnum::ERROR, $notification->severity);
    }

    public function test_mark_as_read(): void
    {
        $notification = Notification::factory()->unread()->create();

        $this->assertFalse($notification->isRead());

        $notification->markAsRead();

        $this->assertTrue($notification->fresh()->isRead());
        $this->assertNotNull($notification->fresh()->read_at);
    }

    public function test_mark_all_as_read(): void
    {
        Notification::factory()->count(3)->unread()->create();
        Notification::factory()->count(2)->read()->create();

        $this->assertEquals(3, Notification::unread()->count());

        Notification::markAllAsRead();

        $this->assertEquals(0, Notification::unread()->count());
    }

    public function test_unread_scope(): void
    {
        Notification::factory()->count(2)->unread()->create();
        Notification::factory()->count(3)->read()->create();

        $this->assertEquals(2, Notification::unread()->count());
    }

    public function test_recent_scope_limits_to_50(): void
    {
        Notification::factory()->count(60)->create();

        $this->assertCount(50, Notification::recent()->get());
    }

    public function test_enums_cast_correctly(): void
    {
        $notification = Notification::factory()->create([
            'type' => NotificationTypeEnum::PUBLISH_FAILED,
            'severity' => NotificationSeverityEnum::ERROR,
        ]);

        $fresh = Notification::find($notification->id);
        $this->assertInstanceOf(NotificationTypeEnum::class, $fresh->type);
        $this->assertInstanceOf(NotificationSeverityEnum::class, $fresh->severity);
        $this->assertEquals(NotificationTypeEnum::PUBLISH_FAILED, $fresh->type);
        $this->assertEquals(NotificationSeverityEnum::ERROR, $fresh->severity);
    }

    public function test_notifiable_morph_to_relationship(): void
    {
        $feed = Feed::factory()->create();

        $notification = $this->service->send(
            NotificationTypeEnum::FEED_STALE,
            NotificationSeverityEnum::WARNING,
            'Feed stale',
            'No new articles',
            notifiable: $feed,
        );

        $fresh = Notification::find($notification->id);
        $this->assertInstanceOf(Feed::class, $fresh->notifiable);
        $this->assertEquals($feed->id, $fresh->notifiable->id);
    }

    public function test_notification_without_notifiable(): void
    {
        $notification = $this->service->info('General notice', 'Something happened');

        $this->assertNull($notification->notifiable_type);
        $this->assertNull($notification->notifiable_id);
        $this->assertNull($notification->notifiable);
    }

    public function test_notification_type_enum_labels(): void
    {
        $this->assertEquals('General', NotificationTypeEnum::GENERAL->label());
        $this->assertEquals('Feed Stale', NotificationTypeEnum::FEED_STALE->label());
        $this->assertEquals('Feed Empty', NotificationTypeEnum::FEED_EMPTY->label());
        $this->assertEquals('Publish Failed', NotificationTypeEnum::PUBLISH_FAILED->label());
        $this->assertEquals('Credential Expired', NotificationTypeEnum::CREDENTIAL_EXPIRED->label());
    }

    public function test_data_stores_null_when_empty(): void
    {
        $notification = $this->service->info('Title', 'Message');

        $this->assertNull($notification->data);
    }

    public function test_data_stores_array_when_provided(): void
    {
        $notification = $this->service->info('Title', 'Message', data: ['key' => 'value']);

        $this->assertEquals(['key' => 'value'], $notification->data);
    }
}
