<?php

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Notifications\DatabaseNotification;
use Illuminate\Support\Str;
use Tests\TestCase;

class HeaderNotificationControllerTest extends TestCase
{
    use RefreshDatabase;

    // -------------------------------------------------------------------------
    // GET /api/header/notifications
    // -------------------------------------------------------------------------

    public function test_unauthenticated_request_returns_redirect(): void
    {
        $response = $this->getJson('/api/header/notifications');
        $response->assertUnauthorized();
    }

    public function test_index_returns_correct_structure(): void
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)->getJson('/api/header/notifications');

        $response->assertOk()
            ->assertJsonStructure([
                'unread_count',
                'notifications',
            ]);
    }

    public function test_index_returns_only_own_notifications(): void
    {
        $user  = User::factory()->create();
        $other = User::factory()->create();

        // Create a notification for the other user
        DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $other->id,
            'data'            => ['title' => 'Other', 'message' => 'Not yours'],
        ]);

        // Create a notification for the authenticated user
        DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $user->id,
            'data'            => ['title' => 'Mine', 'message' => 'Yours'],
        ]);

        $response = $this->actingAs($user)->getJson('/api/header/notifications');

        $response->assertOk();

        $notifications = $response->json('notifications');

        $this->assertCount(1, $notifications);
        $this->assertSame('Mine', $notifications[0]['title']);
    }

    public function test_unread_count_matches_unread_notifications(): void
    {
        $user = User::factory()->create();

        // 2 unread
        foreach (range(1, 2) as $i) {
            DatabaseNotification::create([
                'id'              => Str::uuid(),
                'type'            => 'App\\Notifications\\TestNotification',
                'notifiable_type' => User::class,
                'notifiable_id'   => $user->id,
                'data'            => ['title' => "Unread {$i}", 'message' => ''],
                'read_at'         => null,
            ]);
        }

        // 1 read
        DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $user->id,
            'data'            => ['title' => 'Read', 'message' => ''],
            'read_at'         => now(),
        ]);

        $response = $this->actingAs($user)->getJson('/api/header/notifications');

        $response->assertOk();

        $data         = $response->json();
        $unreadInList = collect($data['notifications'])->where('is_read', false)->count();

        $this->assertSame(2, $data['unread_count']);
        $this->assertSame($data['unread_count'], $unreadInList);
    }

    // -------------------------------------------------------------------------
    // POST /api/header/notifications/{id}/read
    // -------------------------------------------------------------------------

    public function test_mark_read_returns_success(): void
    {
        $user = User::factory()->create();

        $notification = DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $user->id,
            'data'            => ['title' => 'Test', 'message' => ''],
            'read_at'         => null,
        ]);

        $response = $this->actingAs($user)
            ->postJson("/api/header/notifications/{$notification->id}/read");

        $response->assertOk()->assertJson(['success' => true]);

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

    public function test_mark_read_on_another_users_notification_returns_404(): void
    {
        $user  = User::factory()->create();
        $other = User::factory()->create();

        $notification = DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $other->id,
            'data'            => ['title' => 'Other', 'message' => ''],
        ]);

        $response = $this->actingAs($user)
            ->postJson("/api/header/notifications/{$notification->id}/read");

        $response->assertNotFound();
    }

    public function test_mark_read_is_idempotent(): void
    {
        $user = User::factory()->create();

        $notification = DatabaseNotification::create([
            'id'              => Str::uuid(),
            'type'            => 'App\\Notifications\\TestNotification',
            'notifiable_type' => User::class,
            'notifiable_id'   => $user->id,
            'data'            => ['title' => 'Test', 'message' => ''],
            'read_at'         => now(), // already read
        ]);

        // Call twice on an already-read notification
        $this->actingAs($user)
            ->postJson("/api/header/notifications/{$notification->id}/read")
            ->assertOk()->assertJson(['success' => true]);

        $this->actingAs($user)
            ->postJson("/api/header/notifications/{$notification->id}/read")
            ->assertOk()->assertJson(['success' => true]);

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

    // -------------------------------------------------------------------------
    // POST /api/header/notifications/read-all
    // -------------------------------------------------------------------------

    public function test_mark_all_read_clears_unread_count(): void
    {
        $user = User::factory()->create();

        foreach (range(1, 3) as $i) {
            DatabaseNotification::create([
                'id'              => Str::uuid(),
                'type'            => 'App\\Notifications\\TestNotification',
                'notifiable_type' => User::class,
                'notifiable_id'   => $user->id,
                'data'            => ['title' => "Notif {$i}", 'message' => ''],
                'read_at'         => null,
            ]);
        }

        $this->actingAs($user)
            ->postJson('/api/header/notifications/read-all')
            ->assertOk()->assertJson(['success' => true]);

        $response = $this->actingAs($user)->getJson('/api/header/notifications');
        $response->assertOk();

        $data = $response->json();
        $this->assertSame(0, $data['unread_count']);

        foreach ($data['notifications'] as $n) {
            $this->assertTrue($n['is_read']);
        }
    }
}
