<?php

declare(strict_types=1);

namespace NoahVet\Reef\Jane\Runtime\Client;

use Http\Message\MultipartStream\MultipartStreamBuilder;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\SerializerInterface;

abstract class BaseEndpoint implements Endpoint
{
    protected $queryParameters = [];

    protected $headerParameters = [];

    protected $body;

    abstract public function getMethod(): string;

    abstract public function getBody(SerializerInterface $serializer, $streamFactory = null): array;

    abstract public function getUri(): string;

    abstract public function getAuthenticationScopes(): array;

    public function getQueryString(): string
    {
        $optionsResolved = $this->getQueryOptionsResolver()->resolve($this->queryParameters);
        $optionsResolved = \array_map(fn ($value) => null !== $value ? $value : '', $optionsResolved);
        $optionsResolvedCorrected = [];
        \array_walk($optionsResolved, function ($value, string $key) use (&$optionsResolvedCorrected): void {
            if (\str_ends_with($key, '[]')) {
                $key = \substr($key, 0, -2);
            }
            $optionsResolvedCorrected[$key] = $value;
        });

        return \http_build_query($optionsResolvedCorrected, '', '&', \PHP_QUERY_RFC3986);
    }

    public function getHeaders(array $baseHeaders = []): array
    {
        return \array_merge($this->getExtraHeaders(), $baseHeaders, $this->getHeadersOptionsResolver()->resolve($this->headerParameters));
    }

    abstract protected function transformResponseBody(ResponseInterface $response, SerializerInterface $serializer, ?string $contentType = null);

    protected function getExtraHeaders(): array
    {
        return [];
    }

    protected function getQueryOptionsResolver(): OptionsResolver
    {
        return new OptionsResolver();
    }

    protected function getHeadersOptionsResolver(): OptionsResolver
    {
        return new OptionsResolver();
    }

    // ----------------------------------------------------------------------------------------------------
    // Used for OpenApi2 compatibility
    protected function getFormBody(): array
    {
        return [['Content-Type' => ['application/x-www-form-urlencoded']], \http_build_query($this->getFormOptionsResolver()->resolve($this->formParameters))];
    }

    protected function getMultipartBody($streamFactory = null): array
    {
        $bodyBuilder = new MultipartStreamBuilder($streamFactory);
        $formParameters = $this->getFormOptionsResolver()->resolve($this->formParameters);
        foreach ($formParameters as $key => $value) {
            $bodyBuilder->addResource($key, $value);
        }

        return [['Content-Type' => ['multipart/form-data; boundary="'.($bodyBuilder->getBoundary().'"')]], $bodyBuilder->build()];
    }

    protected function getFormOptionsResolver(): OptionsResolver
    {
        return new OptionsResolver();
    }

    protected function getSerializedBody(SerializerInterface $serializer): array
    {
        return [['Content-Type' => ['application/json']], $serializer->serialize($this->body, 'json')];
    }
}
