<?php

namespace classes;

use InvalidArgumentException;

class cron_task_registry
{
    private string $modules_root;

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

    public function __construct(?string $modules_root = null)
    {
        $this->modules_root = $modules_root ?? (defined('WD') ? WD . '/modules' : dirname(__DIR__) . '/modules');
    }

    /**
     * @return array<string, cron_task_definition>
     */
    public function definitions(): array
    {
        if ($this->definitions !== null) {
            return $this->definitions;
        }

        $definitions = [];
        foreach ($this->definitionFiles() as $file) {
            $module_definitions = require $file;
            if (!is_array($module_definitions)) {
                throw new InvalidArgumentException('Cron definition file must return an array: ' . $file);
            }

            foreach ($module_definitions as $definition) {
                $task = new cron_task_definition($definition);
                if (isset($definitions[$task->id])) {
                    throw new InvalidArgumentException('Duplicate cron task id: ' . $task->id);
                }
                $definitions[$task->id] = $task;
            }
        }

        uasort($definitions, static function (cron_task_definition $left, cron_task_definition $right): int {
            if ($left->priority !== $right->priority) {
                return $left->priority <=> $right->priority;
            }
            return strcmp($left->id, $right->id);
        });

        $this->definitions = $definitions;
        return $definitions;
    }

    public function get(string $id_or_legacy_name): ?cron_task_definition
    {
        $normalized = trim($id_or_legacy_name);
        if ($normalized === '') {
            return null;
        }

        $definitions = $this->definitions();
        if (isset($definitions[$normalized])) {
            return $definitions[$normalized];
        }

        foreach ($definitions as $definition) {
            if ($definition->legacy_name !== null && hash_equals($definition->legacy_name, $normalized)) {
                return $definition;
            }
        }

        return null;
    }

    /**
     * @return array<int, string>
     */
    private function definitionFiles(): array
    {
        $files = glob($this->modules_root . '/*/cron/tasks.php') ?: [];
        sort($files, SORT_STRING);
        return $files;
    }
}
