<?php

use App\Enums\DeclarationStatus;
use App\Mail\DeclarationFileRequestMail;
use App\Mail\NudgeNotificationMail;
use App\Models\Declaration;
use App\Models\User;
use App\Models\Workspace;
use App\Notifications\DocumentUploadedNotification;
use App\Notifications\NudgeNotification;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;

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

    $declaration = Declaration::factory()->forWorkspace($workspace)->create([
        'status' => DeclarationStatus::EnCours,
        'assigned_to' => $user->id,
    ]);

    session(['current_workspace_id' => $workspace->id]);

    return [$user, $workspace, $declaration];
}

// ── AC3: Status change to en_attente_client triggers client email ──

test('status change to en_attente_client queues client email', function () {
    Mail::fake();

    [$user, $workspace, $declaration] = setupEmailNotificationTest();

    $declaration->update(['status' => DeclarationStatus::EnAttenteClient]);

    Mail::assertQueued(DeclarationFileRequestMail::class, function ($mail) use ($declaration) {
        return $mail->declaration->id === $declaration->id;
    });
});

test('status change to en_attente_client creates declaration invitation', function () {
    Mail::fake();

    [$user, $workspace, $declaration] = setupEmailNotificationTest();

    $declaration->update(['status' => DeclarationStatus::EnAttenteClient]);

    expect($declaration->invitations()->count())->toBe(1);
    expect($declaration->invitations()->first()->email)->toBe($declaration->client->contact_email);
});

test('status change to en_attente_client skips email when client has no email', function () {
    Mail::fake();

    [$user, $workspace, $declaration] = setupEmailNotificationTest();

    $declaration->client->update(['contact_email' => null]);

    $declaration->update(['status' => DeclarationStatus::EnAttenteClient]);

    Mail::assertNothingQueued();
});

// ── AC4: Status change to ferme invalidates dashboard cache ──

test('status change to ferme invalidates dashboard cache', function () {
    Mail::fake();

    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();
    $workspace->users()->attach($user->id, ['role' => 'owner', 'permissions' => []]);

    $declaration = Declaration::factory()->forWorkspace($workspace)->create([
        'status' => DeclarationStatus::Termine,
        'assigned_to' => $user->id,
    ]);

    $cacheKey = "dashboard:{$workspace->id}:{$user->id}";
    Cache::put($cacheKey, 'cached-data', 300);

    expect(Cache::has($cacheKey))->toBeTrue();

    $declaration->update(['status' => DeclarationStatus::Ferme]);

    expect(Cache::has($cacheKey))->toBeFalse();
});

// ── AC6: Status change to en_cours does NOT trigger any email ──

test('status change to en_cours does not trigger any email', function () {
    Mail::fake();

    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();
    $workspace->users()->attach($user->id, ['role' => 'owner', 'permissions' => []]);

    $declaration = Declaration::factory()->forWorkspace($workspace)->create([
        'status' => DeclarationStatus::Created,
        'assigned_to' => $user->id,
    ]);

    $declaration->update(['status' => DeclarationStatus::EnCours]);

    Mail::assertNothingQueued();
});

// ── AC2: DocumentUploadedNotification has no mail channel ──

test('document uploaded notification has no mail channel', function () {
    $declaration = Declaration::factory()->create();
    $notification = new DocumentUploadedNotification($declaration, 1);

    $channels = $notification->via(new stdClass);

    expect($channels)->toBe(['database']);
    expect($channels)->not->toContain('mail');
});

// ── AC1: Nudge email contains required fields ──

test('nudge email contains required fields', function () {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create(['name' => 'Cabinet Test']);
    $workspace->users()->attach($user->id, ['role' => 'owner', 'permissions' => []]);

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

    $mail = new NudgeNotificationMail($declaration, $user);

    $rendered = $mail->render();

    expect($rendered)->toContain($user->name);
    expect($rendered)->toContain('Voir la déclaration');
    expect($rendered)->toContain('Cabinet Test');
});

// ── AC1: Nudge notification sends via database and mail channels ──

test('nudge notification sends via database and mail channels', function () {
    $declaration = Declaration::factory()->create();
    $sender = User::factory()->create();
    $notification = new NudgeNotification($declaration, $sender);

    $channels = $notification->via(new stdClass);

    expect($channels)->toBe(['database', 'mail']);
});

// ── AC6: Workspace scoping on notification dispatches ──

test('status change email is scoped to the correct workspace client', function () {
    Mail::fake();

    [$user, $workspace, $declaration] = setupEmailNotificationTest();

    $otherWorkspace = Workspace::factory()->create();
    $otherDeclaration = Declaration::factory()->forWorkspace($otherWorkspace)->create([
        'status' => DeclarationStatus::EnCours,
    ]);

    // Only our declaration should trigger email
    $declaration->update(['status' => DeclarationStatus::EnAttenteClient]);
    $otherDeclaration->update(['status' => DeclarationStatus::EnAttenteClient]);

    Mail::assertQueued(DeclarationFileRequestMail::class, 2);

    // Each email goes to the correct client
    Mail::assertQueued(DeclarationFileRequestMail::class, function ($mail) use ($declaration) {
        return $mail->declaration->id === $declaration->id;
    });
});

// ── AC5: Failed email retries — verify $tries and $backoff properties ──

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

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

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

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