<?php

declare(strict_types=1);

namespace Plugin\tnt_stabila_shop\src\Services;

use DateTime;
use JTL\Backend\Notification;
use JTL\Backend\NotificationEntry;
use JTL\Cache\JTLCacheInterface;
use JTL\Mail\Mail\Mail;
use JTL\Router\Route;
use JTL\Shop;
use Plugin\tnt_stabila_shop\src\DomainObjects\StabilaEventDTO;
use Plugin\tnt_stabila_shop\src\Helper\Formatter;
use Plugin\tnt_stabila_shop\src\Helper\ShopRuntime;
use Plugin\tnt_stabila_shop\src\Helper\MathHelper;
use Plugin\tnt_stabila_shop\src\Models\Notifications;
use Plugin\tnt_stabila_shop\src\Repositories\DatabaseRepository;
use Plugin\tnt_stabila_shop\src\Repositories\SessionRepository;
use Plugin\tnt_stabila_shop\src\UserInterface\Tables\ErrorTable\ErrorTableData;
use Plugin\tnt_stabila_shop\src\UserInterface\Tables\ErrorTable\ErrorTablePagination;

class ErrorService
{
    use Formatter;

    public function __construct(
        protected DatabaseRepository $database,
        protected SessionRepository $session,
        protected JTLCacheInterface|null $cache = null
    ) {
        $this->cache = $cache ?? Shop::Container()->getCache();
    }

    /**
     * @return object{lastErrors: int, weeklyErrors: list<object{returnValue: int}>, changeType: string, changeAmount: string}
     */
    public function getErrorsLastWeek(): object
    {
        $errorsLastXDays = $this->database->getErrorsLastXDays(7);
        // Limited by the last 2 years
        $weeklyErrorTotals = $this->database->getWeeklyErrorTotals();
        $performance       = MathHelper::calcPerformance($errorsLastXDays, $weeklyErrorTotals);
        $changeType        = '+';
        if ($errorsLastXDays > 0 && $errorsLastXDays < $performance->average) {
            $changeType = '-';
        }

        return (object)[
            'lastErrors'   => $errorsLastXDays,
            'weeklyErrors' => $weeklyErrorTotals,
            'changeType'   => $changeType,
            'changeAmount' => self::numberFormat($performance->performance, 2, '%'),
        ];
    }

    public function getErrorTable(int $page, string $search = ''): ErrorTableData|null
    {
        $pagination = new ErrorTablePagination($this->database, $page);
        $data       = $pagination->getData($search);
        if ($data === []) {
            return null;
        }
        $firstRow = $data[0];
        $header   = \array_keys(\get_object_vars($firstRow));

        return new ErrorTableData($header, \array_values($data), 'error.tpl', $pagination);
    }

    /**
     * @param int $errorID
     * @return object{id: int, exceptionType: string, sdkVersion: string, filePath: string, functionName: string,
     *     template: string, shopVersion: string, severity: object{badgeType: string, value: string},
     *     message: string, varDump: string, hash: string, createDate: DateTime, reportLink: string,
     *     reportMaxDate: string, totalExecTime: float, shopLanguage: string, shopCurrency: string,
     *     cartCount: int, cartItems: string, deliveryCountry: string, invoiceCountry: string,
     *     taxRates: string[], userBrowser: string, userID: int}|null
     */
    public function getErrorDetails(int $errorID): object|null
    {
        return $this->database->getErrorDetails($errorID);
    }

    public function notifyAdminAboutError(StabilaEventDTO $event, bool $sendMail = false): void
    {
        $notificationType = match ((string)$event->severity) {
            'info'           => NotificationEntry::TYPE_INFO,
            'warning'        => NotificationEntry::TYPE_WARNING,
            'error', 'fatal' => NotificationEntry::TYPE_DANGER,
            default          => NotificationEntry::TYPE_NONE,
        };
        if ($sendMail) {
            $mailer = Shop::Container()->getMailer();
            $mail   = new Mail();
            $mail->setToMail($mail->getFromMail());
            $mail->setToName($mail->getFromName());
            $mail->setSubject(\__('Error reported by Stabila plugin') . ShopRuntime::shopUrl());
            $mail->setBodyHTML('<p>' . $event->exceptions[0]->getValue() . '</p>');
            $mail->setBodyText($event->exceptions[0]->getValue());
            $mailer->send($mail);
        }
        // Add notification
        $this->database->saveNotification(
            type: $notificationType,
            title: \__('Error reported by Stabila plugin'),
            description: $event->exceptions[0]->getValue(),
            hash: Notifications::generateHash($notificationType, $event->exceptions[0]->getValue())
        );
    }
}
