<?php

declare(strict_types=1);

namespace Plugin\tnt_stabila_shop\src\CronJobs;

use DateTime;
use JTL\Backend\NotificationEntry;
use JTL\Cache\JTLCacheInterface;
use JTL\Cron\Job;
use JTL\Cron\JobHydrator;
use JTL\Cron\JobInterface;
use JTL\Cron\QueueEntry;
use JTL\DB\DbInterface;
use JTL\Shop;
use Plugin\tnt_stabila_shop\src\Helper\PerformanceBooster;
use Plugin\tnt_stabila_shop\src\Helper\SalesAnomalyDetector;
use Plugin\tnt_stabila_shop\src\Helper\ShopRuntime;
use Plugin\tnt_stabila_shop\src\Models\Notifications;
use Plugin\tnt_stabila_shop\src\Repositories\DatabaseRepository;
use Plugin\tnt_stabila_shop\src\Repositories\ReportRepository;
use Plugin\tnt_stabila_shop\src\Services\ReportService;
use Psr\Log\LoggerInterface;

/**
 * Class CronJob
 * @package Plugin\tnt_stabila_shop
 */
class CronJob extends Job
{
    public const CRON_JOB_ID       = 'tnt_stabila_cron';
    protected const CRON_JOB_NAME  = 'Stabila Cron';
    protected const CRON_FREQUENCY = 12;
    private DatabaseRepository $database;

    public function __construct(
        DbInterface $db,
        LoggerInterface $logger,
        JobHydrator $hydrator,
        JTLCacheInterface $cache,
    ) {
        parent::__construct($db, $logger, $hydrator, $cache);
        $this->database = new DatabaseRepository($this->db);
    }

    private function tidyUpPerformanceTable(): bool
    {
        try {
            $timezone = ShopRuntime::timezone();
        } catch (\DateException) {
            $timezone = null;
        }

        return $this->database->tidyUpPerformanceTable(
            (new DateTime('now', $timezone))->modify('-1 year')
        );
    }

    private function tidyUpEventsTable(): bool
    {
        return $this->database->tidyUpEventsTable();
    }

    private function optimizeCache(): bool
    {
        $performanceBooster = PerformanceBooster::getSQLiteCache();
        if ($performanceBooster === null) {
            return false;
        }
        if ($performanceBooster === false) {
            return true;
        }
        $performanceBooster->exec('VACUUM');

        return true;
    }

    private function tidyUpReports(): bool
    {
        $reportService = new ReportService(
            new ReportRepository(Shop::Container()->getDB())
        );
        $reportService->removeOldPluginReports();

        return true;
    }

    /**
     * Flag an anomalous drop in recent order volume as an admin notification.
     *
     * Compares the last 30 days of orders against the average of prior full
     * months. Gated behind the salesAnomalyThreshold setting (minimum % drop);
     * a threshold of 0/unset keeps the historic behaviour of never notifying.
     */
    private function checkSalesAnomaly(): void
    {
        $threshold = $this->database->getSetting('salesAnomalyThreshold');
        $threshold = \is_int($threshold) ? $threshold : null;
        if ($threshold === null || $threshold <= SalesAnomalyDetector::DISABLED) {
            return;
        }
        $monthly = $this->database->getMonthlyOrderTotals();
        // Drop the current (partial) month so it does not skew the baseline downwards.
        $baseline = SalesAnomalyDetector::baselineAverage(\array_slice($monthly, 0, -1));
        $recent   = (float)$this->database->getTotalOrdersXLastDays(30);
        if (!SalesAnomalyDetector::isAnomalous($recent, $baseline, $threshold)) {
            return;
        }
        $description = \sprintf(
            \__('Orders in the last 30 days (%d) are %s%% below the usual monthly average of %s.'),
            (int)$recent,
            \number_format(SalesAnomalyDetector::dropPercent($recent, $baseline), 1, ',', '.'),
            \number_format($baseline, 1, ',', '.')
        );
        $this->database->saveNotification(
            type: NotificationEntry::TYPE_WARNING,
            title: \__('Sales anomaly detected by Stabila plugin'),
            description: $description,
            hash: Notifications::generateHash(NotificationEntry::TYPE_WARNING, $description)
        );
    }

    /**
     * @inheritdoc
     */
    public function start(QueueEntry $queueEntry): JobInterface
    {
        parent::start($queueEntry);
        $this->logger->debug('Stabila cron job started');

        if ($this->tidyUpPerformanceTable()) {
            $this->logger->debug('Performance-Table could not be cleaned during stabila cron job');
        }
        if ($this->tidyUpEventsTable()) {
            $this->logger->debug('Events-Table could not be cleaned during stabila cron job');
        }
        if ($this->tidyUpReports()) {
            $this->logger->debug('Reports could not be removed during stabila cron job');
        }
        if ($this->optimizeCache()) {
            $this->logger->debug('SQLite cache could not be optimized during stabila cron job');
        }
        $this->checkSalesAnomaly();

        $this->logger->debug('Stabila cron job finished.');
        $this->setFinished(true);

        return $this;
    }

    /**
     * @return object{name: string, jobType: string, frequency: int, startDate: string, startTime: string}
     */
    public static function createCron(): object
    {
        return (object)[
            'name'      => self::CRON_JOB_NAME,
            'jobType'   => self::CRON_JOB_ID,
            'frequency' => self::CRON_FREQUENCY,
            'startDate' => 'NOW()',
            'startTime' => '00:00:00',
        ];
    }
}
