<?php

namespace Opentalent\OtCore\Tests\Unit\Domain\Repository;

use GuzzleHttp\Client;
use Opentalent\OtCore\Domain\Repository\ApiPagedCollection;
use Opentalent\OtCore\Domain\Repository\BaseApiRepository;
use Opentalent\OtCore\Exception\ApiRequestException;

/**
 * Proxy to reveal the private methods of the abstract class BaseApiRepository
 * @package Opentalent\OtCore\Tests\Unit\Domain\Repository
 */
class ConcreteBaseApiRepository extends BaseApiRepository {
    protected function memberToObject(array $member) { return $member; }
    public function injectClient($client) { parent::injectClient($client); }
    public function getResponse($uri, $params = []): \Psr\Http\Message\ResponseInterface { return parent::getResponse($uri, $params); }
    public function getBody($uri, $params = []): string { return parent::getBody($uri, $params); }
    public function getJsonDecoded($uri, $params = []): array { return parent::getJsonDecoded($uri, $params); }
    public function getApiFirstRecord($params = [], $forceUri = null) { return parent::getApiFirstRecord($params, $forceUri); }
    public function getApiRecords($params = [], $forceUri = null): ApiPagedCollection { return parent::getApiRecords($params, $forceUri); }
}

class BaseApiRepositoryTest extends AbstractApiRepositoryTestCase
{
    const TESTED_CLASS = 'Opentalent\OtCore\Tests\Unit\Domain\Repository\ConcreteBaseApiRepository';

    /**
     * get should build a valid url, send a query and
     * return a Guzzle response object
     *
     * @test
     */
    public function get() {

        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = ['itemsPerPage' => 10, 'foo' => 1];

        // uri as it is supposed to be processed by the repo
        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";

        $this->injectClientFor($processed_uri);
        $actual = $this->repository->getResponse($base_uri, $params);

        $this->assertEquals(200, $actual->getStatusCode());
    }

    /**
     * get should build a valid url, send a query and
     * return a Guzzle response object
     *
     * @test
     */
    public function getWithNoParams() {

        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = [];

        // uri as it is supposed to be processed by the repo
        $processed_uri = $base_uri . "?_format=json&itemsPerPage=8";

        $this->injectClientFor($processed_uri);
        $actual = $this->repository->getResponse($base_uri, $params);

        $this->assertEquals(200, $actual->getStatusCode());
    }

    /**
     * get should build a valid url, send a query and
     * return a Guzzle response object
     *
     * @test
     */
    public function getInvalidUri()
    {
        $base_uri = "a very bad uri";
        $params = [];
        $processed_uri = $base_uri . "?_format=json&itemsPerPage=8";

        $client = $this->prophesize(Client::class);
        $client->request(BaseApiRepository::HTTP_METHOD, $processed_uri)
            ->shouldBeCalled()
            ->willThrow(new \GuzzleHttp\Exception\TransferException('error'));
        $this->inject($this->repository, "client", $client->reveal());

        try {
            $this->repository->getResponse($base_uri, $params);
            throw new \AssertionError("An ApiRequestException should have been thrown");
        } catch (ApiRequestException $e) {
            $this->assertEquals('error', $e->getMessage());
        }
    }

    /**
     * getBody should return the response body as a string
     *
     * @test
     */
    public function getBody() {

        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = ['itemsPerPage' => 10, 'foo' => 1];

        // uri as it is supposed to be processed by the repo
        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";

        $this->injectClientFor($processed_uri);
        $actual = $this->repository->getBody($base_uri, $params);

        $this->assertEquals('{"@context": "/api/contexts/PortailOrganization"}', $actual);
    }

    /**
     * getBody should return the response body as an array
     *
     * @test
     */
    public function getJsonDecoded() {

        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = ['itemsPerPage' => 10, 'foo' => 1];

        // uri as it is supposed to be processed by the repo
        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";

        $this->injectClientFor($processed_uri);
        $actual = $this->repository->getJsonDecoded($base_uri, $params);

        $this->assertEquals(["@context" => "/api/contexts/PortailOrganization"], $actual);
    }

    /**
     * getApiFirstRecord should return the first member of the api response
     * this member has been processed by the memberToObject method, which does nothing here
     *
     * @test
     */
    public function getApiFirstRecord() {
        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = ['filter[where][id]' => 1];
        $processed_uri = $base_uri . "?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8";
        $this->injectClientFor($processed_uri);

        $actual = $this->repository->getApiFirstRecord($params, $base_uri);

        $this->assertEquals('PortailOrganization', $actual['@type']);
    }

    /**
     * getApiFirstRecord should return the members of the api response
     * these members has been processed by the memberToObject method, which does nothing here
     *
     * @test
     */
    public function getApiRecords() {
        $base_uri = "https://api.opentalent.fr/api/public/organizations";
        $params = ['filter[where][id]' => 1];
        $processed_uri = $base_uri . "?_format=json&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8";
        $this->injectClientFor($processed_uri);

        $actual = $this->repository->getApiRecords($params, $base_uri);

        $this->assertEquals('PortailOrganization', $actual->getMembers()[0]['@type']);
    }
}
