<?php

namespace NoahVet\Reef\Iterator;

/**
 * @template TValue of object
 *
 * @implements ReefApiIteratorInterface<TValue, array<string, mixed>>
 */
class ApiClientChunkedReefIteratorDecorator implements ReefApiIteratorInterface
{
    /**
     * @var array<string, mixed>
     */
    protected array $params;

    protected int $chunk = 0;

    protected int $lastChunk;

    /**
     * @param ReefApiIteratorInterface<TValue, array<string, mixed>> $iterator
     */
    public function __construct(
        protected readonly ReefApiIteratorInterface $iterator,
        protected string $chunkParamName,
        protected int $chunkSize = 50,
    ) {
        $this->params = $iterator->getParams();
        $this->lastChunk = 0;
    }

    #[\Override]
    public function current(): mixed
    {
        return $this->iterator->current();
    }

    #[\Override]
    public function next(): void
    {
        $this->iterator->next();

        if (!$this->iterator->valid()) {
            ++$this->chunk;

            if ($this->chunk < $this->lastChunk) {
                $this->iterator->setParams($this->getChunkedParams());
            }
        }
    }

    #[\Override]
    public function key(): mixed
    {
        return $this->chunk * $this->chunkSize + $this->iterator->key();
    }

    #[\Override]
    public function valid(): bool
    {
        return $this->chunk < $this->lastChunk && $this->iterator->valid();
    }

    #[\Override]
    public function rewind(): void
    {
        $this->chunk = 0;
        $this->lastChunk = $this->countChunks();
        $this->iterator->setParams($this->getChunkedParams());
    }

    #[\Override]
    public function getParams(): array
    {
        return $this->params;
    }

    #[\Override]
    public function setParams(array $params): void
    {
        $this->params = $params;
        $this->rewind();
    }

    public function countChunks(): int
    {
        if (!isset($this->params[$this->chunkParamName]) || !\is_array($this->params[$this->chunkParamName])) {
            return 1;
        }

        return (int) \ceil(\count($this->params[$this->chunkParamName]) / $this->chunkSize);
    }

    /**
     * @return array<string, mixed>
     */
    public function getChunkedParams(): array
    {
        if (!isset($this->params[$this->chunkParamName]) || !\is_array($this->params[$this->chunkParamName])) {
            return $this->params;
        }

        $ret = $this->params;
        $ret[$this->chunkParamName] = \array_slice($ret[$this->chunkParamName], $this->chunk * $this->chunkSize, $this->chunkSize);

        return $ret;
    }
}
