<?php

namespace app\service;

use support\Redis;

class AiMetadataQueue
{
    public function push(string $archiveUid): void
    {
        Redis::lPush($this->pendingKey(), $archiveUid);
    }

    public function pop(int $timeout = 5): ?string
    {
        $result = Redis::brPop([$this->pendingKey()], $timeout);
        if (!is_array($result) || count($result) < 2) {
            return null;
        }

        return (string) $result[1];
    }

    public function retryLater(string $archiveUid, string $error): void
    {
        $retryKey = $this->retryKey($archiveUid);
        $retryCount = (int) Redis::incr($retryKey);
        Redis::setEx($this->errorKey($archiveUid), 86400, $error);

        if ($retryCount > $this->maxRetries()) {
            Redis::lPush($this->failedKey(), $archiveUid);
            return;
        }

        $delay = $this->baseDelaySeconds() * (2 ** max(0, $retryCount - 1));
        Redis::zAdd($this->delayedKey(), time() + $delay, $archiveUid);
    }

    public function releaseDueDelayed(): int
    {
        $now = time();
        $items = Redis::zRangeByScore($this->delayedKey(), '-inf', (string) $now, ['limit' => [0, 100]]);
        if (!is_array($items) || $items === []) {
            return 0;
        }

        foreach ($items as $archiveUid) {
            Redis::zRem($this->delayedKey(), $archiveUid);
            Redis::lPush($this->pendingKey(), $archiveUid);
        }

        return count($items);
    }

    public function clearRetry(string $archiveUid): void
    {
        Redis::del($this->retryKey($archiveUid), $this->errorKey($archiveUid));
    }

    public function blockTimeout(): int
    {
        return (int) config('queue.ai_metadata.block_timeout', 5);
    }

    public function idleSleepSeconds(): int
    {
        return max(1, (int) config('queue.ai_metadata.idle_sleep_seconds', 1));
    }

    private function pendingKey(): string
    {
        return config('queue.ai_metadata.pending', 'proofdb:ai:metadata:pending');
    }

    private function delayedKey(): string
    {
        return config('queue.ai_metadata.delayed', 'proofdb:ai:metadata:delayed');
    }

    private function failedKey(): string
    {
        return config('queue.ai_metadata.failed', 'proofdb:ai:metadata:failed');
    }

    private function retryKey(string $archiveUid): string
    {
        return config('queue.ai_metadata.retry_prefix', 'proofdb:ai:metadata:retry:') . $archiveUid;
    }

    private function errorKey(string $archiveUid): string
    {
        return config('queue.ai_metadata.error_prefix', 'proofdb:ai:metadata:error:') . $archiveUid;
    }

    private function maxRetries(): int
    {
        return (int) config('queue.ai_metadata.max_retries', 5);
    }

    private function baseDelaySeconds(): int
    {
        return (int) config('queue.ai_metadata.base_delay_seconds', 60);
    }
}
