<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Service\Workflow;

use Chill\MainBundle\Cron\CronJobInterface;
use Chill\MainBundle\Entity\CronJobExecution;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Messenger\MessageBusInterface;

class CancelStaleWorkflowCronJob implements CronJobInterface
{
    public const KEY = 'remove-stale-workflow';

    public const KEEP_INTERVAL = 'P90D';

    private const LAST_CANCELED_WORKFLOW = 'last-canceled-workflow-id';

    public function __construct(
        private readonly EntityWorkflowRepository $workflowRepository,
        private readonly ClockInterface $clock,
        private readonly MessageBusInterface $messageBus,
        private readonly LoggerInterface $logger,
        private readonly EntityManagerInterface $entityManager,
    ) {}

    public function canRun(?CronJobExecution $cronJobExecution): bool
    {
        if (null === $cronJobExecution) {
            return true;
        }

        return $this->clock->now() >= $cronJobExecution->getLastEnd()->add(new \DateInterval('P1D'));
    }

    public function getKey(): string
    {
        return self::KEY;
    }

    public function run(array $lastExecutionData): ?array
    {
        $this->logger->info('Cronjob started: Canceling stale workflows.');

        $olderThanDate = $this->clock->now()->sub(new \DateInterval(self::KEEP_INTERVAL));
        $staleEntityWorkflows = $this->workflowRepository->findWorkflowsWithoutFinalStepAndOlderThan($olderThanDate);
        $lastCanceled = $lastExecutionData[self::LAST_CANCELED_WORKFLOW] ?? 0;
        $processedCount = 0;

        foreach ($staleEntityWorkflows as $staleEntityWorkflow) {
            try {
                $this->messageBus->dispatch(new CancelStaleWorkflowMessage($staleEntityWorkflow->getId()));
                $lastCanceled = max($staleEntityWorkflow->getId(), $lastCanceled);
                ++$processedCount;
            } catch (\Exception $e) {
                $this->logger->error('Failed to dispatch CancelStaleWorkflow', ['exception' => $e, 'entityWorkflowId' => $staleEntityWorkflow->getId()]);
                continue;
            }

            if (0 === $processedCount % 10) {
                $this->entityManager->clear();
            }
        }

        $this->logger->info("Cronjob completed: {$processedCount} workflows processed.");

        return [self::LAST_CANCELED_WORKFLOW => $lastCanceled];
    }
}
