<?php

declare(strict_types=1);

namespace Autogedal\FeedMonitor\Model;

/**
 * Monitored jobs:
 * - declared in di.xml by the modules that own them (source "custom"):
 *     job_code => [label, max_age_minutes, log_file, feeds => [feed_code => label]]
 * - read at runtime by the providers (e.g. Firebear import jobs, source "firebear").
 * max_age_minutes: default interval after which a job (or each of its sub-feeds) that has not started is reported
 * as not running; it can be changed from the admin (Config::getMaxAgeMinutes()). Null: no regular schedule, not
 * checked.
 */
class JobRegistry
{
    public const SOURCE_CUSTOM = 'custom';

    /** @var array<string, array<string, mixed>>|null */
    private ?array $all = null;

    /**
     * @param array<string, array<string, mixed>> $jobs
     * @param JobProviderInterface[] $providers
     */
    public function __construct(
        private readonly array $jobs = [],
        private readonly array $providers = []
    ) {
    }

    /**
     * @return string[]
     */
    public function getJobCodes(?string $source = null): array
    {
        $codes = [];
        foreach ($this->all() as $code => $job) {
            if ($source === null || ($job['source'] ?? self::SOURCE_CUSTOM) === $source) {
                $codes[] = (string)$code;
            }
        }

        return $codes;
    }

    public function has(string $jobCode): bool
    {
        return isset($this->all()[$jobCode]);
    }

    public function getLabel(string $jobCode, string $feedCode = ''): string
    {
        $job = $this->all()[$jobCode] ?? [];
        $label = (string)($job['label'] ?? $jobCode);
        if ($feedCode !== '') {
            $label .= ' / ' . (string)($job['feeds'][$feedCode] ?? $feedCode);
        }

        return $label;
    }

    /**
     * @return string[] sub-feed codes, empty when the job is monitored as a whole
     */
    public function getFeedCodes(string $jobCode): array
    {
        return array_keys((array)($this->all()[$jobCode]['feeds'] ?? []));
    }

    /**
     * Keys checked for "not running": the job, or each of its sub-feeds.
     *
     * @return string[]
     */
    public function getMonitorKeys(): array
    {
        $keys = [];
        foreach ($this->getJobCodes() as $jobCode) {
            $feeds = $this->getFeedCodes($jobCode);
            if (!$feeds) {
                $keys[] = $jobCode;
            }
            foreach ($feeds as $feedCode) {
                $keys[] = MonitorKey::build($jobCode, $feedCode);
            }
        }

        return $keys;
    }

    /**
     * @return int|null null when the job has no regular schedule
     */
    public function getDefaultMaxAgeMinutes(string $jobCode): ?int
    {
        $job = $this->all()[$jobCode] ?? [];
        if (array_key_exists('max_age_minutes', $job) && $job['max_age_minutes'] === null) {
            return null;
        }

        return max(1, (int)($job['max_age_minutes'] ?? 1560));
    }

    public function getLogFile(string $jobCode): string
    {
        return (string)($this->all()[$jobCode]['log_file'] ?? '');
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function all(): array
    {
        if ($this->all === null) {
            $this->all = $this->jobs;
            foreach ($this->providers as $provider) {
                $this->all += $provider->getJobs();
            }
        }

        return $this->all;
    }
}
