<?php

use App\Enums\NotificationType;
use App\Enums\WorkspaceUserRole;
use App\Mail\NudgeNotificationMail;
use App\Models\Declaration;
use App\Models\User;
use App\Models\Workspace;
use App\Notifications\DeclarationOverdueNotification;
use App\Notifications\DocumentUploadedNotification;
use App\Notifications\NudgeNotification;
use Illuminate\Support\Facades\Notification;
use Inertia\Testing\AssertableInertia as Assert;

function setupNotificationUser(string $role = 'owner'): array
{
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();
    $workspace->users()->attach($user->id, [
        'role' => $role,
        'permissions' => [],
    ]);
    session(['current_workspace_id' => $workspace->id]);

    return [$user, $workspace];
}

// ── NotificationType Enum ──────────────────────────────────

test('notification type enum has all expected values', function () {
    expect(NotificationType::getValues())->toContain('nudge')
        ->toContain('declaration_overdue')
        ->toContain('document_uploaded')
        ->toContain('bulk_notification')
        ->toContain('status_changed');

    expect(NotificationType::getValues())->toHaveCount(5);
});

// ── NudgeNotification ──────────────────────────────────────

test('nudge notification sends via database and mail channels', function () {
    Notification::fake();

    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();
    $recipient = User::factory()->create();
    $workspace->users()->attach($recipient->id, ['role' => 'worker', 'permissions' => []]);

    $recipient->notify(new NudgeNotification($declaration, $user));

    Notification::assertSentTo($recipient, NudgeNotification::class, function ($notification, $channels) {
        return in_array('database', $channels) && in_array('mail', $channels);
    });
});

test('nudge notification payload includes workspace_id and notification_type', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $notification = new NudgeNotification($declaration, $user);
    $payload = $notification->toDatabase($user);

    expect($payload)->toHaveKey('workspace_id', $workspace->id)
        ->toHaveKey('declaration_id', $declaration->id)
        ->toHaveKey('sender_id', $user->id)
        ->toHaveKey('notification_type', NotificationType::Nudge);
});

test('nudge notification toMail returns NudgeNotificationMail', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $notification = new NudgeNotification($declaration, $user);
    $mailable = $notification->toMail($user);

    expect($mailable)->toBeInstanceOf(NudgeNotificationMail::class);
});

// ── DocumentUploadedNotification ────────────────────────────

test('document uploaded notification sends via database channel only', function () {
    Notification::fake();

    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id));

    Notification::assertSentTo($user, DocumentUploadedNotification::class, function ($notification, $channels) {
        return $channels === ['database'];
    });
});

test('document uploaded notification payload includes workspace_id and notification_type', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $notification = new DocumentUploadedNotification($declaration, $declaration->client_id);
    $payload = $notification->toDatabase($user);

    expect($payload)->toHaveKey('workspace_id', $workspace->id)
        ->toHaveKey('declaration_id', $declaration->id)
        ->toHaveKey('client_id', $declaration->client_id)
        ->toHaveKey('notification_type', NotificationType::DocumentUploaded);
});

// ── DeclarationOverdueNotification ──────────────────────────

test('declaration overdue notification sends via database and mail channels', function () {
    Notification::fake();

    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $user->notify(new DeclarationOverdueNotification($declaration));

    Notification::assertSentTo($user, DeclarationOverdueNotification::class, function ($notification, $channels) {
        return in_array('database', $channels) && in_array('mail', $channels);
    });
});

test('declaration overdue notification payload includes workspace_id and notification_type', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $notification = new DeclarationOverdueNotification($declaration);
    $payload = $notification->toDatabase($user);

    expect($payload)->toHaveKey('workspace_id', $workspace->id)
        ->toHaveKey('declaration_id', $declaration->id)
        ->toHaveKey('notification_type', NotificationType::DeclarationOverdue);
});

test('declaration overdue notification has retry configuration', function () {
    $declaration = Declaration::factory()->make();
    $notification = new DeclarationOverdueNotification($declaration);

    expect($notification->tries)->toBe(3);
});

test('nudge notification has retry configuration', function () {
    $declaration = Declaration::factory()->make();
    $sender = User::factory()->make();
    $notification = new NudgeNotification($declaration, $sender);

    expect($notification->tries)->toBe(3);
});

// ── NotificationController ──────────────────────────────────

test('notification controller index returns paginated workspace-scoped notifications', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    // Create a notification in the database for this user
    $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id));

    $response = $this->actingAs($user)->get(route('notifications.index'));

    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->component('notifications/Index')
        ->has('notifications')
        ->has('markAllReadUrl')
    );
});

test('notification controller markAsRead marks notification as read', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id));
    $notification = $user->notifications()->first();

    expect($notification->read_at)->toBeNull();

    $response = $this->actingAs($user)->post(route('notifications.read', $notification->id));

    $response->assertRedirect();
    expect($notification->fresh()->read_at)->not->toBeNull();
});

test('notification controller markAllAsRead marks all as read', function () {
    [$user, $workspace] = setupNotificationUser();
    $declaration = Declaration::factory()->forWorkspace($workspace)->create();

    $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id));
    $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id));

    expect($user->unreadNotifications)->toHaveCount(2);

    $response = $this->actingAs($user)->post(route('notifications.readAll'));

    $response->assertRedirect();
    $user->refresh();
    expect($user->unreadNotifications)->toHaveCount(0);
});

test('unauthenticated users cannot access notification routes', function () {
    $this->get(route('notifications.index'))->assertRedirect(route('login'));
    $this->post(route('notifications.readAll'))->assertRedirect(route('login'));
});
