<?php

namespace App\Services;

use App\Contracts\ShouldNotifyAdmin;
use App\Notifications\SystemErrorNotification;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
use Throwable;

class AdminNotificationService
{
    public function notifyError(Throwable $exception): void
    {
        if (! config('libra.notifications.system_notifications_enabled')) {
            return;
        }

        $adminEmail = config('libra.notifications.admin_email');

        if (empty($adminEmail)) {
            return;
        }

        $errorKey = $this->getErrorKey($exception);
        $throttleMinutes = config('libra.notifications.throttle_minutes', 15);

        if (Cache::has($errorKey)) {
            return;
        }

        Cache::put($errorKey, true, now()->addMinutes($throttleMinutes));

        Notification::route('mail', $adminEmail)
            ->notifyNow(new SystemErrorNotification($exception));
    }

    protected function getErrorKey(Throwable $exception): string
    {
        return 'admin_notified:'.md5(get_class($exception).$exception->getMessage());
    }

    public function shouldNotifyAdmin(Throwable $exception): bool
    {
        if ($exception instanceof ShouldNotifyAdmin) {
            return true;
        }

        $excludedTypes = [
            \Illuminate\Validation\ValidationException::class,
            \Illuminate\Auth\AuthenticationException::class,
            \Illuminate\Auth\Access\AuthorizationException::class,
            \Illuminate\Database\Eloquent\ModelNotFoundException::class,
            \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
            \Illuminate\Session\TokenMismatchException::class,
        ];

        foreach ($excludedTypes as $type) {
            if ($exception instanceof $type) {
                return false;
            }
        }

        if ($exception instanceof \Illuminate\Database\QueryException) {
            return $this->isCriticalDatabaseError($exception);
        }

        if ($exception instanceof \Symfony\Component\Mailer\Exception\TransportExceptionInterface) {
            return true;
        }

        return false;
    }

    protected function isCriticalDatabaseError(\Illuminate\Database\QueryException $e): bool
    {
        $criticalCodes = ['2002', '1045', '1049'];

        return in_array($e->getCode(), $criticalCodes);
    }
}
