<?php
namespace WapplerSystems\Meilisearch\System\Meilisearch;

/***************************************************************
 *  Copyright notice
 *
 *  (c) 2009-2015 Ingo Renner <ingo@typo3.org>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

use MeiliSearch\Client;
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
use WapplerSystems\Meilisearch\System\Logging\MeilisearchLogManager;
use WapplerSystems\Meilisearch\System\Meilisearch\Parser\SchemaParser;
use WapplerSystems\Meilisearch\System\Meilisearch\Parser\StopWordParser;
use WapplerSystems\Meilisearch\System\Meilisearch\Parser\SynonymParser;
use WapplerSystems\Meilisearch\System\Meilisearch\Service\MeilisearchAdminService;
use WapplerSystems\Meilisearch\System\Meilisearch\Service\MeilisearchReadService;
use WapplerSystems\Meilisearch\System\Meilisearch\Service\MeilisearchWriteService;
use WapplerSystems\Meilisearch\Util;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
 * Meilisearch Service Access
 *
 * @author Ingo Renner <ingo@typo3.org>
 */
class MeilisearchConnection
{
    /**
     * @var MeilisearchAdminService
     */
    protected $adminService;

    /**
     * @var MeilisearchReadService
     */
    protected $readService;

    /**
     * @var MeilisearchWriteService
     */
    protected $writeService;

    /**
     * @var TypoScriptConfiguration
     */
    protected $configuration;

    /**
     * @var SynonymParser
     */
    protected $synonymParser = null;

    /**
     * @var StopWordParser
     */
    protected $stopWordParser = null;

    /**
     * @var SchemaParser
     */
    protected $schemaParser = null;

    /**
     * @var Client[]
     */
    protected $nodes = [];

    /**
     * @var MeilisearchLogManager
     */
    protected $logger = null;

    /**
     * @var ClientInterface[]
     */
    protected $clients = [];

    /**
     * @var ClientInterface
     */
    protected $psr7Client;

    /**
     * @var RequestFactoryInterface
     */
    protected $requestFactory;

    /**
     * @var StreamFactoryInterface
     */
    protected $streamFactory;

    /**
     * @var EventDispatcherInterface
     */
    protected $eventDispatcher;

    /**
     * Constructor
     *
     * @param Client $readNode
     * @param Client $writeNode
     * @param ?TypoScriptConfiguration $configuration
     * @param ?SynonymParser $synonymParser
     * @param ?StopWordParser $stopWordParser
     * @param ?SchemaParser $schemaParser
     * @param ?MeilisearchLogManager $logManager
     * @param ?ClientInterface $psr7Client
     * @param ?RequestFactoryInterface $requestFactory
     * @param ?StreamFactoryInterface $streamFactory
     * @param ?EventDispatcherInterface $eventDispatcher
     *
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function __construct(
        Client $readNode,
        Client $writeNode,
        TypoScriptConfiguration $configuration = null,
        SynonymParser $synonymParser = null,
        StopWordParser $stopWordParser = null,
        SchemaParser $schemaParser = null,
        MeilisearchLogManager $logManager = null,
        ClientInterface $psr7Client = null,
        EventDispatcherInterface $eventDispatcher = null
    ) {
        $this->nodes['read'] = $readNode;
        $this->nodes['write'] = $writeNode;
        $this->nodes['admin'] = $writeNode;
        $this->configuration = $configuration ?? Util::getMeilisearchConfiguration();
        $this->synonymParser = $synonymParser;
        $this->stopWordParser = $stopWordParser;
        $this->schemaParser = $schemaParser;
        $this->logger = $logManager;
        $this->psr7Client = $psr7Client ?? GeneralUtility::getContainer()->get(ClientInterface::class);
        $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
    }

    /**
     * @param string $key
     * @return Client
     */
    public function getNode(string $key): Client
    {
        return $this->nodes[$key];
    }

    /**
     * @return MeilisearchAdminService
     */
    public function getAdminService(): MeilisearchAdminService
    {
        if ($this->adminService === null) {
            $this->adminService = $this->buildAdminService();
        }

        return $this->adminService;
    }

    /**
     * @return MeilisearchAdminService
     * @noinspection PhpIncompatibleReturnTypeInspection
     */
    protected function buildAdminService(): MeilisearchAdminService
    {
        $endpointKey = 'admin';
        $client = $this->getClient($endpointKey);
        return GeneralUtility::makeInstance(MeilisearchAdminService::class, $client, $this->configuration, $this->logger, $this->synonymParser, $this->stopWordParser, $this->schemaParser);
    }

    /**
     * @return MeilisearchReadService
     */
    public function getReadService(): MeilisearchReadService
    {
        if ($this->readService === null) {
            $this->readService = $this->buildReadService();
        }

        return $this->readService;
    }

    /**
     * @return MeilisearchReadService
     * @noinspection PhpIncompatibleReturnTypeInspection
     */
    protected function buildReadService(): MeilisearchReadService
    {
        $endpointKey = 'read';
        $client = $this->getClient($endpointKey);
        return GeneralUtility::makeInstance(MeilisearchReadService::class, $client);
    }

    /**
     * @return MeilisearchWriteService
     */
    public function getWriteService(): MeilisearchWriteService
    {
        if ($this->writeService === null) {
            $this->writeService = $this->buildWriteService();
        }

        return $this->writeService;
    }

    /**
     * @return MeilisearchWriteService
     * @noinspection PhpIncompatibleReturnTypeInspection
     */
    protected function buildWriteService(): MeilisearchWriteService
    {
        $endpointKey = 'write';
        $client = $this->getClient($endpointKey);
        return GeneralUtility::makeInstance(MeilisearchWriteService::class, $client);
    }


    /**
     * @param Client $client
     * @param ?string $endpointKey
     */
    public function setClient(Client $client, ?string $endpointKey = 'read')
    {
        $this->clients[$endpointKey] = $client;
    }
}
