<?php

use App\Contracts\ShouldNotifyAdmin;
use App\Notifications\SystemErrorNotification;
use App\Services\AdminNotificationService;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;

beforeEach(function () {
    config([
        'libra.notifications.system_notifications_enabled' => true,
        'libra.notifications.admin_email' => 'admin@libra.ps',
        'libra.notifications.throttle_minutes' => 15,
    ]);
    Cache::flush();
});

test('critical exception triggers admin notification via service', function () {
    Notification::fake();

    $exception = new class('Critical payment error') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);

    if ($service->shouldNotifyAdmin($exception)) {
        $service->notifyError($exception);
    }

    Notification::assertSentOnDemand(
        SystemErrorNotification::class,
        function ($notification, $channels, $notifiable) {
            return $notifiable->routes['mail'] === config('libra.notifications.admin_email');
        }
    );
});

test('validation exception does not trigger admin notification', function () {
    Notification::fake();

    try {
        validator([], ['email' => 'required'])->validate();
    } catch (\Illuminate\Validation\ValidationException $e) {
        $service = app(AdminNotificationService::class);

        if ($service->shouldNotifyAdmin($e)) {
            $service->notifyError($e);
        }
    }

    Notification::assertNothingSent();
});

test('authentication exception does not trigger admin notification', function () {
    Notification::fake();

    $exception = new \Illuminate\Auth\AuthenticationException;

    $service = app(AdminNotificationService::class);

    if ($service->shouldNotifyAdmin($exception)) {
        $service->notifyError($exception);
    }

    Notification::assertNothingSent();
});

test('authorization exception does not trigger admin notification', function () {
    Notification::fake();

    $exception = new \Illuminate\Auth\Access\AuthorizationException;

    $service = app(AdminNotificationService::class);

    if ($service->shouldNotifyAdmin($exception)) {
        $service->notifyError($exception);
    }

    Notification::assertNothingSent();
});

test('model not found exception does not trigger admin notification', function () {
    Notification::fake();

    $exception = new \Illuminate\Database\Eloquent\ModelNotFoundException;

    $service = app(AdminNotificationService::class);

    if ($service->shouldNotifyAdmin($exception)) {
        $service->notifyError($exception);
    }

    Notification::assertNothingSent();
});

test('http 404 exception does not trigger admin notification', function () {
    Notification::fake();

    $exception = new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

    $service = app(AdminNotificationService::class);

    if ($service->shouldNotifyAdmin($exception)) {
        $service->notifyError($exception);
    }

    Notification::assertNothingSent();
});

test('system error notification contains required information', function () {
    $exception = new RuntimeException('Test error message');
    $notification = new SystemErrorNotification($exception);

    $mail = $notification->toMail(new AnonymousNotifiable);

    expect($mail->subject)->toContain('[URGENT]');
    expect($mail->subject)->toContain('RuntimeException');
    expect($mail->subject)->toContain('Libra Law Firm');
});

test('system error notification includes environment', function () {
    $exception = new RuntimeException('Test error');
    $notification = new SystemErrorNotification($exception);

    $mail = $notification->toMail(new AnonymousNotifiable);

    expect($mail->subject)->toContain('['.app()->environment().']');
});

test('system error notification sent immediately not queued', function () {
    Notification::fake();

    $exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);
    $service->notifyError($exception);

    Notification::assertSentOnDemand(SystemErrorNotification::class);
});

test('rate limiting prevents duplicate notifications within throttle period', function () {
    Notification::fake();

    $exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);

    $service->notifyError($exception);
    $service->notifyError($exception);
    $service->notifyError($exception);

    Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 1);
});

test('different exceptions are not rate limited together', function () {
    Notification::fake();

    $exception1 = new class('Error 1') extends Exception implements ShouldNotifyAdmin {};
    $exception2 = new class('Error 2') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);

    $service->notifyError($exception1);
    $service->notifyError($exception2);

    Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 2);
});

test('notification not sent when system notifications disabled', function () {
    Notification::fake();
    config(['libra.notifications.system_notifications_enabled' => false]);

    $exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);
    $service->notifyError($exception);

    Notification::assertNothingSent();
});

test('notification not sent when admin email not configured', function () {
    Notification::fake();
    config(['libra.notifications.admin_email' => null]);

    $exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};

    $service = app(AdminNotificationService::class);
    $service->notifyError($exception);

    Notification::assertNothingSent();
});
