<?php declare(strict_types=1);

namespace Newland\NeosCommon\Controller;

/*
 * This file is part of the "neos-common" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Headers;
use Neos\Flow\Http\Request;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Controller\ActionController;
use Newland\NeosCommon\LinkHandler\LinkHandlerFactory;
use Newland\NeosCommon\LinkHandler\Domain\Model\Record;
use Newland\NeosCommon\Routing\FrontendNodeRoutePartHandler;
use function Safe\json_encode;

/**
 * Rudimentary REST service for nodes
 *
 * @Flow\Scope("singleton")
 */
class RecordController extends ActionController
{
    /**
     * @Flow\Inject()
     * @var LinkHandlerFactory
     */
    protected $linkHandlerFactory;

    /**
     * @var FrontendNodeRoutePartHandler
     * @Flow\Inject
     */
    protected $frontendNodeRoutePartHandler;

    public function callActionMethod()
    {
        try {
            parent::callActionMethod();
        } catch (\Exception $e) {
            $this->response->setContent(
                (string) json_encode(
                    [
                        'error' => get_class($e),
                        'message' => $e->getMessage(),
                        'trace' => $e->getTrace(),
                    ]
                )
            );
            $this->response->setHeader('Content-Type', 'application/json');
            $this->response->setStatus(500);
        }
    }


    /**
     * Shows a list of nodes
     *
     * @param string $searchTerm An optional search term used for filtering the list of nodes
     * @param array $recordIdentifiers An optional list of node identifiers
     * @param array $recordTypes
     * @param string|null $language
     * @return string
     */
    public function indexAction(
        $searchTerm = '',
        array $recordIdentifiers = [],
        array $recordTypes = [],
        string $language = null
    ): string {
        if ($recordIdentifiers === []) {
            $records = $this->search($searchTerm, $recordTypes ?: $this->linkHandlerFactory->allTypes(), $language);
        } else {
            $records = $this->findByIdentifiers($recordIdentifiers);
        }

        $formatted = [];
        foreach ($records as $type => $typeRecords) {
            foreach ($typeRecords as $record) {
                $formatted[] = [
                    'dataType' => 'Newland.NeosCommon:Linkhandler',
                    'loaderUri' => 'record://' . $type . ':' . $record->getId(),
                    'label' => $record->getTitle(),
                    'identifier' => $record->getId(),
                ];
            }
        }

        /** @var Headers $headers */
        $headers = $this->response->getHeaders();
        $headers->set('Content-Type', 'application/json');

        return json_encode($formatted);
    }


    /**
     * @param string $searchTerm
     * @param array $recordTypes
     * @param string|null $language
     * @return Record[][]
     */
    private function search(string $searchTerm, array $recordTypes, string $language = null): array
    {
        $request = $this->getControllerContext()->getRequest();
        if ($request instanceof ActionRequest === false) {
            return [];
        }

        /** @var Request $httpRequest */
        $httpRequest = $request->getHttpRequest();
        $requestUriHost = $httpRequest->getServerParams()['HTTP_HOST'] ?? null;
        $site = $this->frontendNodeRoutePartHandler->getSiteByHostName($requestUriHost);

        $results = [];
        foreach ($recordTypes as $type) {
            $results[$type] = $this->linkHandlerFactory
                ->get($type, $language, $site)
                ->findBySearchTerm($searchTerm);
        }

        return $results;
    }

    /**
     * @param array $recordIdentifiers
     * @return Record[][]
     */
    private function findByIdentifiers(array $recordIdentifiers): array
    {
        $identifiersByType = [];
        foreach ($recordIdentifiers as $identifier) {
            if (preg_match('/([a-zA-Z0-9\-]+):([a-z0-9\-]+)/', $identifier, $matches)) {
                [ , $recordType, $identifier ] = $matches;
                $identifiersByType[$recordType] = $identifiersByType[$recordType] ?? [];
                $identifiersByType[$recordType][] = $identifier;
            }
        }

        $results = [];
        foreach ($identifiersByType as $type => $identifiers) {
            $results[$type] = [];
            $handler = $this->linkHandlerFactory->get((string) $type);

            foreach ($identifiers as $identifier) {
                $result = $handler->findByIdentifier($identifier);
                if ($result) {
                    $results[$type][] = $result;
                }
            }
        }

        return $results;
    }
}
