<?php

namespace Opentalent\OtCore\Domain\Repository;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Opentalent\OtCore\Exception\ApiRequestException;
use Opentalent\OtCore\Service\OpentalentApiService;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
use TYPO3\CMS\Core\Core\ApplicationContext;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;

/**
 *  Base class for repositories based on the Opentalent API
 *
 */
abstract class BaseApiRepository implements LoggerAwareInterface
{
    use LoggerAwareTrait;

    const URI_TRAILING_PART = '';
    const HYDRA_TYPE = '';
    const DEFAULT_ITEMS_PER_PAGE = 8;

    protected $apiService;

    /**
     * BaseApiRepository constructor.
     */
    public function __construct($apiService = null) {
        $this->apiService = $apiService ?? GeneralUtility::makeInstance(OpentalentApiService::class);
    }

    /**
     * [FOR TESTS ONLY]
     * @param Client $client
     */
    protected function injectService(OpentalentApiService $apiService) {
        $this->apiService = $apiService;
    }

    /**
     * -- Needs to be reimplemented in subclasses --
     * Convert response's members record to an actual Domain's object
     * @param array $record
     * @return object
     */
    abstract protected function memberToObject(array $record);

    /**
     * Send a request to the API and
     * returns the first record (member)
     *
     * @param array $params
     * @param string|null $forceUri
     * @return object
     * @throws ApiRequestException
     */
    protected function getApiFirstRecord($params = [], ?string $forceUri = null)
    {
        $params['page'] = '1';
        $params['totalItems'] = '1';
        $collection = $this->getApiRecords($params, $forceUri);
        return $collection->getMembers()[0];
    }

    /**
     * Send a request to the API and
     * returns the records as an array (members)
     *
     * @param array $params
     * @param string|null $forceUri
     * @return ApiPagedCollection
     * @throws ApiRequestException
     */
    protected function getApiRecords(array $params = [], ?string $forceUri = null): ApiPagedCollection
    {
        $params = array("_format" => "json") + $params;   // _format should be the first param to match unit-tests fixtures
        if(!isset($params['itemsPerPage'])) {
            $params['itemsPerPage'] = (string)self::DEFAULT_ITEMS_PER_PAGE;
        }

        $uri = ltrim($this::URI_TRAILING_PART, '/');
        if ($forceUri !== null) {
            $uri = trim($forceUri, '/') . '/' . $uri;
        }
        $body = $this->apiService->getJsonDecoded($uri, $params);

        $page = (int)($params['page'] ?? 1);

        // build up the members
        $members = [];

        if (is_array($body['hydra:member'])) {
            foreach ($body['hydra:member'] as $record) {
                $instance = $this->memberToObject($record);
                if ($instance != null) {
                    $members[] = $instance;
                }
            }
        }

        return new ApiPagedCollection(
            (int)($body['hydra:totalItems'] ?? 0),
            (int)($body['hydra:itemsPerPage'] ?? 0),
            $page,
            $members
        );
    }
}
