<?php

declare(strict_types=1);

namespace Autogedal\FeedMonitor\Model\Adapter;

use Autogedal\FeedMonitor\Model\FeedRun;
use Autogedal\FeedMonitor\Model\Fetch\FeedFormatInterface;
use Autogedal\FeedMonitor\Model\Fetch\FeedResult;
use Autogedal\FeedMonitor\Model\JobRegistry;
use Autogedal\FeedMonitor\Model\Problem;
use Psr\Log\LoggerInterface;

/**
 * Jobs that read several feeds (autogedal_feed_oos_products): one run and one alert per sub-feed, so a broken
 * sub-feed neither stops nor hides the others.
 */
class CustomSubfeedMonitor extends AbstractJobMonitor
{
    /** Products missing from a feed above which the feed is considered broken. */
    public const DEFAULT_MAX_MISSING = 1500;

    public function begin(string $jobCode, string $feedCode): FeedRun
    {
        return $this->monitor->start($jobCode, $feedCode);
    }

    /**
     * Validates the copy of the feed saved earlier (the reference for "missing" products). An empty copy is a valid
     * reference with nothing in it; null when it is not in the expected format (the run is then ended and reported).
     */
    public function readReference(
        FeedRun $run,
        string $content,
        FeedFormatInterface $format,
        ?LoggerInterface $logger = null
    ): ?FeedResult {
        $result = $this->reader->parse($content, $format);
        if ($result->getProblem() === Problem::EMPTY) {
            return FeedResult::ok([], 0, 0);
        }
        if (!$result->isOk()) {
            $logger?->info('Saved copy of the feed not usable: ' . $result->getMessage());
            $this->monitor->fail($run, Problem::INVALID, 'Copia salvata a feed-ului nu are formatul asteptat: ' . $result->getMessage());

            return null;
        }

        return $result;
    }

    /**
     * False when far more products disappeared from the feed than the limit (the run is then ended and reported),
     * unless the next run was allowed from the CLI.
     */
    public function acceptMissingCount(FeedRun $run, int $missing, int $reference, int $limit = self::DEFAULT_MAX_MISSING): bool
    {
        if ($missing <= $limit || $this->monitor->isIncompleteAllowed($run)) {
            return true;
        }
        $this->monitor->fail($run, Problem::INCOMPLETE, sprintf(
            'Lipsesc %d produse fata de copia salvata a feed-ului (%d), peste limita de %d. Produsele nu au fost marcate epuizate.',
            $missing,
            $reference,
            $limit
        ));

        return false;
    }

    public function check(bool $outsideCron): array
    {
        $found = [];
        foreach ($this->jobs->getJobCodes(JobRegistry::SOURCE_CUSTOM) as $jobCode) {
            $maxAge = $this->getMaxAgeMinutes($jobCode);
            foreach ($maxAge === null ? [] : $this->jobs->getFeedCodes($jobCode) as $feedCode) {
                $line = $this->monitor->checkNotRunning($jobCode, $feedCode, $maxAge);
                if ($line !== null) {
                    $found[] = $line;
                }
            }
        }

        return $found;
    }
}
