<?php

declare(strict_types=1);

use App\Models\InAppNotification;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;

/**
 * Тесты API для in-app уведомлений: GET /api/notifications + PATCH /{id}/read +
 * POST /mark-all-read + DELETE /{id}. Все endpoint'ы под Sanctum SPA auth.
 *
 * RLS-проверка через защиту от кражи чужого id (where user_id) — постgres
 * superuser BYPASSRLS в тестах, но controller-level фильтр работает.
 */
uses(DatabaseTransactions::class);

beforeEach(function () {
    $this->tenant = Tenant::factory()->create();
    $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]);
    $this->actingAs($this->user);
});

function makeNotif(int $userId, int $tenantId, ?string $readAt = null, string $event = 'new_lead'): InAppNotification
{
    return InAppNotification::create([
        'tenant_id' => $tenantId,
        'user_id' => $userId,
        'event' => $event,
        'title' => 'Новый лид — Caranga',
        'body' => '79001234567',
        'deal_id' => 42,
        'payload' => ['deal_id' => 42, 'project_name' => 'Caranga'],
        'read_at' => $readAt,
    ]);
}

test('GET /api/notifications: 401 без auth', function () {
    auth()->logout();
    $this->getJson('/api/notifications')->assertStatus(401);
});

test('GET /api/notifications: пустой', function () {
    $response = $this->getJson('/api/notifications');
    $response->assertOk();
    expect($response->json('items'))->toBe([]);
    expect($response->json('unread_count'))->toBe(0);
    expect($response->json('total'))->toBe(0);
});

test('GET /api/notifications: возвращает только свои + сортировка по created_at DESC', function () {
    $other = User::factory()->create(['tenant_id' => $this->tenant->id]);
    makeNotif($this->user->id, $this->tenant->id);
    sleep(1);
    $newer = makeNotif($this->user->id, $this->tenant->id);
    makeNotif($other->id, $this->tenant->id); // чужое

    $response = $this->getJson('/api/notifications');
    $response->assertOk();
    expect($response->json('total'))->toBe(2);
    expect($response->json('items.0.id'))->toBe($newer->id);  // newer first
    expect($response->json('unread_count'))->toBe(2);
});

test('GET /api/notifications?unread_only=1: только непрочитанные', function () {
    makeNotif($this->user->id, $this->tenant->id, readAt: now()->toIso8601String());
    makeNotif($this->user->id, $this->tenant->id);

    $response = $this->getJson('/api/notifications?unread_only=1');
    expect($response->json('items'))->toHaveCount(1);
    expect($response->json('items.0.read_at'))->toBeNull();
    expect($response->json('unread_count'))->toBe(1);
    expect($response->json('total'))->toBe(2);
});

test('GET /api/notifications?limit=2: лимитирует выдачу', function () {
    for ($i = 0; $i < 5; $i++) {
        makeNotif($this->user->id, $this->tenant->id);
    }

    $response = $this->getJson('/api/notifications?limit=2');
    expect($response->json('items'))->toHaveCount(2);
    expect($response->json('total'))->toBe(5);
});

test('GET /api/notifications: 422 на limit > 100', function () {
    $this->getJson('/api/notifications?limit=101')->assertStatus(422);
});

test('GET /api/notifications: возвращает поля title/body/event/payload/deal_id', function () {
    $notif = makeNotif($this->user->id, $this->tenant->id);

    $response = $this->getJson('/api/notifications');
    $item = $response->json('items.0');
    expect($item['id'])->toBe($notif->id);
    expect($item['event'])->toBe('new_lead');
    expect($item['title'])->toBe('Новый лид — Caranga');
    expect($item['body'])->toBe('79001234567');
    expect($item['deal_id'])->toBe(42);
    expect($item['payload']['project_name'])->toBe('Caranga');
    expect($item['read_at'])->toBeNull();
});

test('PATCH /api/notifications/{id}/read: ставит read_at + idempotent', function () {
    $notif = makeNotif($this->user->id, $this->tenant->id);

    $response = $this->patchJson("/api/notifications/{$notif->id}/read");
    $response->assertOk();
    expect($response->json('read_at'))->not->toBeNull();

    $notif->refresh();
    $firstReadAt = $notif->read_at?->toIso8601String();
    expect($firstReadAt)->not->toBeNull();

    // Повторный — не меняет read_at.
    $this->patchJson("/api/notifications/{$notif->id}/read")->assertOk();
    $notif->refresh();
    expect($notif->read_at?->toIso8601String())->toBe($firstReadAt);
});

test('PATCH /api/notifications/{id}/read: 404 для чужого', function () {
    $other = User::factory()->create(['tenant_id' => $this->tenant->id]);
    $notif = makeNotif($other->id, $this->tenant->id);

    $this->patchJson("/api/notifications/{$notif->id}/read")->assertStatus(404);
});

test('PATCH /api/notifications/{id}/read: 404 на несуществующий id', function () {
    $this->patchJson('/api/notifications/999999/read')->assertStatus(404);
});

test('POST /api/notifications/mark-all-read: bulk-update + count', function () {
    makeNotif($this->user->id, $this->tenant->id);
    makeNotif($this->user->id, $this->tenant->id);
    makeNotif($this->user->id, $this->tenant->id, readAt: now()->toIso8601String()); // уже прочитано

    $response = $this->postJson('/api/notifications/mark-all-read');
    $response->assertOk();
    expect($response->json('updated'))->toBe(2);

    $unreadCount = InAppNotification::query()
        ->where('user_id', $this->user->id)
        ->whereNull('read_at')
        ->count();
    expect($unreadCount)->toBe(0);
});

test('POST /api/notifications/mark-all-read: только свои', function () {
    $other = User::factory()->create(['tenant_id' => $this->tenant->id]);
    makeNotif($other->id, $this->tenant->id); // чужое
    makeNotif($this->user->id, $this->tenant->id);

    $response = $this->postJson('/api/notifications/mark-all-read');
    expect($response->json('updated'))->toBe(1); // только своё

    $otherUnread = InAppNotification::query()
        ->where('user_id', $other->id)
        ->whereNull('read_at')
        ->count();
    expect($otherUnread)->toBe(1); // чужое осталось непрочитанным
});

test('DELETE /api/notifications/{id}: удаляет своё', function () {
    $notif = makeNotif($this->user->id, $this->tenant->id);

    $this->deleteJson("/api/notifications/{$notif->id}")->assertOk();
    expect(InAppNotification::query()->find($notif->id))->toBeNull();
});

test('DELETE /api/notifications/{id}: 404 для чужого', function () {
    $other = User::factory()->create(['tenant_id' => $this->tenant->id]);
    $notif = makeNotif($other->id, $this->tenant->id);

    $this->deleteJson("/api/notifications/{$notif->id}")->assertStatus(404);

    expect(InAppNotification::query()->find($notif->id))->not->toBeNull(); // не удалено
});
