<?php

declare(strict_types=1);

namespace KupShop\ContentBundle\EventSubscriber;

use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
use Symfony\Component\ErrorHandler\Error\OutOfMemoryError;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\Service\Attribute\Required;

class ExceptionSubscriber implements EventSubscriberInterface
{
    #[Required]
    public SentryLogger $sentryLogger;

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::EXCEPTION => [
                ['registerLog', 200],
            ],
        ];
    }

    public function registerLog(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();
        $loopLimit = 0;

        while ($exception !== null) {
            if ($exception instanceof DeadlockException) {
                $this->loggingWithProcessList($exception, '!!! DEADLOCK !!!');

                return;
            }

            if ($exception instanceof LockWaitTimeoutException) {
                $this->loggingWithProcessList($exception, '!!! LOCK TIMEOUT !!!');

                return;
            }

            // Protože hrozí, že při dalším zpracování dojde paměť exception handleru,
            // radši zkusím hned takhle co nejdřív poslat do Sentry, ať o tyhle hnusný chyby nepříjdeme.
            if ($exception instanceof OutOfMemoryError) {
                increaseMemoryLimit(100);
                \Sentry\captureException($exception);

                return;
            }

            $exception = $exception->getPrevious();
            $loopLimit++;
            if ($loopLimit >= 5) {
                break;
            }
        }
    }

    private function loggingWithProcessList(\Throwable $e, string $title): void
    {
        $this->sentryLogger->captureException(
            new \Exception($title, $e->getCode(), $e), [
                'extra' => json_encode(sqlQuery('SHOW FULL PROCESSLIST')->fetchAllAssociative()),
            ]);
    }
}
