<?php

declare(strict_types=1);

namespace Plugin\tnt_stabila_shop\src\Repositories;

use JTL\DB\DbInterface;

/**
 * Persistence for the Sentry delivery spool.
 *
 * Captures are written here as fully-built Sentry envelopes by the SpoolingHttpClient, so the
 * shop request never performs the network POST. The tnt_stabila_sentry_spool cron drains the
 * table out-of-band, deleting rows that ship and counting attempts on the rest.
 */
class SentrySpoolRepository
{
    public const TABLE = 'stabila_shop_sentry_spool';

    public function __construct(private readonly DbInterface $db)
    {
    }

    /**
     * Persist one Sentry envelope for later delivery.
     */
    public function spool(string $body): void
    {
        $this->db->insertRow(
            self::TABLE,
            (object)['body' => $body, 'attempts' => 0]
        );
    }

    /**
     * Oldest-first batch of spooled envelopes still eligible for delivery.
     *
     * @return array<int, \stdClass> rows with id, body, attempts
     */
    public function fetchBatch(int $limit, int $maxAttempts): array
    {
        return $this->db->getObjects(
            'SELECT id, body, attempts FROM ' . self::TABLE
            . ' WHERE attempts < :maxAttempts ORDER BY id ASC LIMIT :lmt',
            ['maxAttempts' => $maxAttempts, 'lmt' => $limit]
        );
    }

    /**
     * Remove a delivered envelope.
     */
    public function delete(int $id): void
    {
        $this->db->delete(self::TABLE, 'id', $id);
    }

    /**
     * Record a failed delivery attempt so a persistently unreachable endpoint
     * eventually stops being retried (see cron drain).
     */
    public function markFailed(int $id): void
    {
        $this->db->executeQueryPrepared(
            'UPDATE ' . self::TABLE . ' SET attempts = attempts + 1 WHERE id = :id',
            ['id' => $id]
        );
    }

    /**
     * Drop envelopes that have exhausted their retries or aged out, so the table
     * can never grow without bound when Sentry stays unreachable.
     */
    public function purgeUndeliverable(int $maxAttempts, string $olderThan): void
    {
        $this->db->executeQueryPrepared(
            'DELETE FROM ' . self::TABLE
            . ' WHERE attempts >= :maxAttempts OR createDate < :olderThan',
            ['maxAttempts' => $maxAttempts, 'olderThan' => $olderThan]
        );
    }
}
