<?php

use App\Models\Declaration;
use App\Models\User;
use App\Models\Workspace;
use App\Notifications\NudgeNotification;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
use Spatie\Activitylog\Models\Activity;

function setupNudgeTest(string $senderRole = 'owner'): array
{
    $sender = User::factory()->create();
    $worker = User::factory()->create();
    $workspace = Workspace::factory()->create();

    $workspace->users()->attach($sender->id, ['role' => $senderRole, 'permissions' => []]);
    $workspace->users()->attach($worker->id, ['role' => 'worker', 'permissions' => []]);

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

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

    return [$sender, $worker, $workspace, $declaration];
}

// ── AC2, AC5, AC7: Owner can send nudge ──────────────────

test('owner can send nudge — notification dispatched, activity logged, success flash', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertRedirect();
    $response->assertSessionHas('flash', [
        'type' => 'success',
        'message' => 'Relance envoyée à '.$worker->name,
    ]);

    Notification::assertSentTo($worker, NudgeNotification::class);
});

// ── AC2, AC5: Manager can send nudge ─────────────────────

test('manager can send nudge — same behavior as owner', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('manager');

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertRedirect();
    $response->assertSessionHas('flash', [
        'type' => 'success',
        'message' => 'Relance envoyée à '.$worker->name,
    ]);

    Notification::assertSentTo($worker, NudgeNotification::class);
});

// ── AC5: Worker cannot send nudge ────────────────────────

test('worker cannot send nudge — abort 404', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    $response = $this->actingAs($worker)->post(route('declarations.nudge', $declaration));

    $response->assertNotFound();
    Notification::assertNothingSent();
});

// ── AC5: Nudge on declaration in wrong workspace ─────────

test('nudge on declaration in wrong workspace — abort 404', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    // Create a different workspace and set it as current
    $otherWorkspace = Workspace::factory()->create();
    $otherWorkspace->users()->attach($sender->id, ['role' => 'owner', 'permissions' => []]);
    session(['current_workspace_id' => $otherWorkspace->id]);

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertNotFound();
    Notification::assertNothingSent();
});

// ── Nudge on declaration with no assignee ────────────────

test('nudge on declaration with no assignee — returns error', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

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

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertRedirect();
    $response->assertSessionHas('flash', function (array $flash) {
        return $flash['type'] === 'warning';
    });

    Notification::assertNothingSent();
});

// ── AC8: Duplicate nudge within 1 hour — debounce ────────

test('duplicate nudge within 1 hour — debounce prevents, returns warning flash', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    // Create a recent nudge notification manually in the database
    $worker->notifications()->create([
        'id' => \Illuminate\Support\Str::uuid(),
        'type' => NudgeNotification::class,
        'data' => [
            'workspace_id' => $workspace->id,
            'declaration_id' => $declaration->id,
            'sender_id' => $sender->id,
        ],
        'created_at' => now()->subMinutes(30),
    ]);

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertRedirect();
    $response->assertSessionHas('flash', [
        'type' => 'warning',
        'message' => 'Relance déjà envoyée récemment',
    ]);

    Notification::assertNothingSent();
});

// ── AC8: Nudge after 1 hour — allowed ────────────────────

test('nudge after 1 hour — allowed (debounce expired)', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    // Create an old nudge notification (more than 1 hour ago)
    $worker->notifications()->create([
        'id' => \Illuminate\Support\Str::uuid(),
        'type' => NudgeNotification::class,
        'data' => [
            'workspace_id' => $workspace->id,
            'declaration_id' => $declaration->id,
            'sender_id' => $sender->id,
        ],
        'created_at' => now()->subMinutes(61),
    ]);

    $response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $response->assertRedirect();
    $response->assertSessionHas('flash', [
        'type' => 'success',
        'message' => 'Relance envoyée à '.$worker->name,
    ]);

    Notification::assertSentTo($worker, NudgeNotification::class);
});

// ── AC7: Activity log entry created ──────────────────────

test('activity log entry created with correct actor/target/action', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    $activity = Activity::query()
        ->where('subject_type', Declaration::class)
        ->where('subject_id', $declaration->id)
        ->where('causer_id', $sender->id)
        ->where('description', 'nudged')
        ->first();

    expect($activity)->not->toBeNull();
    expect($activity->causer_id)->toBe($sender->id);
    expect($activity->subject_id)->toBe($declaration->id);
});

// ── Unauthenticated user cannot nudge ────────────────────

test('unauthenticated user cannot nudge — redirected to login', function () {
    $declaration = Declaration::factory()->create();

    $response = $this->post(route('declarations.nudge', $declaration));

    $response->assertRedirect(route('login'));
});

// ── Cache cleared for assignee after nudge ───────────────

test('notification cache cleared for assignee after nudge', function () {
    Notification::fake();

    [$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');

    // Pre-populate cache
    Cache::put("user:{$worker->id}:workspace:{$workspace->id}:unread_notifications", 5);

    $this->actingAs($sender)->post(route('declarations.nudge', $declaration));

    expect(Cache::has("user:{$worker->id}:workspace:{$workspace->id}:unread_notifications"))->toBeFalse();
});
