<?php

declare(strict_types=1);

namespace apexl\Io\modules\jobs\Factory;

use apexl\Io\modules\jobs\Entity\QueueJob;
use apexl\Vault\Vault;
use DateTimeImmutable;
use DI\Container;

final readonly class QueueJobFactory
{
    public function __construct(private Container $container, private Vault $db) {}

    public function make(
        string $handler,
        mixed $contents,
        string|int|null $sequence = null,
        ?DateTimeImmutable $created = null,
        ?int $runAfter = null,
    ): QueueJob {
        $job = $this->container->make(QueueJob::class);

        $job->sequence = $sequence ?? $this->nextSequence($handler);
        $job->handler = $handler;
        $job->created = $created ?? new DateTimeImmutable();
        $job->contents = $contents;
        $job->run_after = $runAfter ?? 0;
        $job->store();

        return $job;
    }

    /**
     * Read-max-then-insert, deliberately unlocked. Two concurrent enqueues of
     * the same handler can and do come back with the same number, and that is
     * harmless: `sequence` orders jobs rather than identifying them, and
     * (sequence, handler) carries no unique constraint — see
     * 20260806120000_relax_job_queue_sequence_uniqueness for why it used to and
     * what that cost. `id` breaks the tie in arrival order, which is what
     * QueuedTaskFinderDb sorts on.
     */
    public function nextSequence(string $handler): int
    {
        return (int) $this->db
            ->select('job_queue')
            ->fields(['(IFNULL(MAX(sequence),0) + 1) AS sequence', 'handler'])
            ->where('handler', $handler)
            ->execute()
            ->fetch()->sequence;
    }
}
