<?php

declare(strict_types=1);

namespace NoahVet\Reef\Iterator;

use NoahVet\Reef\Factory\Jane\EndpointFactoryInterface;
use NoahVet\Reef\Jane\Normalizer\JaneObjectNormalizer;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Serializer\Encoder\JsonDecode;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;

/**
 * @template TClient of object
 * @template TValue of object
 * @template TParams of array<string, mixed>
 * @template TEndpoint of object
 *
 * @extends AbstractSimpleReefApiIterator<TValue, TParams>
 */
class ApiClientSimpleReefIterator extends AbstractSimpleReefApiIterator
{
    private readonly Serializer $serializer;

    /**
     * @param class-string<TEndpoint> $endpointClass
     */
    public function __construct(
        private readonly object $client,
        private readonly string $endpointClass,
        private readonly EndpointFactoryInterface $endpointFactory,
        array $params,
        int $querySize = 50,
        NormalizerInterface $responseNormalizer = new JaneObjectNormalizer(),
    ) {
        parent::__construct($params, $querySize);

        $this->serializer = new Serializer(
            [
                new ArrayDenormalizer(),
                $responseNormalizer,
            ],
            [
                new JsonEncoder(
                    new JsonEncode(),
                    new JsonDecode(['json_decode_associative' => true],
                    ),
                ),
            ]);
    }

    public function apiCall(array $params): ResponseInterface
    {
        $endpoint = $this->endpointFactory->create($this->endpointClass, $params);

        \assert(\method_exists($this->client, 'executeRawEndpoint'));

        return $this->client->executeRawEndpoint($endpoint);
    }

    public function parseResponse(ResponseInterface $response): array
    {
        $endpoint = $this->endpointFactory->create($this->endpointClass, $this->params);

        \assert(\method_exists($endpoint, 'parseResponse'));

        return $endpoint->parseResponse($response, $this->serializer);
    }

    /**
     * @param TParams $params
     *
     * @return TEndpoint
     */
    protected function instanciateEndpoint(array $params): object
    {
        return new $this->endpointClass($params);
    }
}
