<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Controller;

use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Search\ParsingException;
use Chill\MainBundle\Search\SearchApi;
use Chill\MainBundle\Search\SearchApiNoQueryException;
use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Search\SearchProvider;
use Chill\MainBundle\Search\UnknowSearchDomainException;
use Chill\MainBundle\Search\UnknowSearchNameException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * Class SearchController.
 */
class SearchController extends AbstractController
{
    public function __construct(protected SearchProvider $searchProvider, protected TranslatorInterface $translator, protected PaginatorFactory $paginatorFactory, protected SearchApi $searchApi) {}

    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/search/advanced/{name}', name: 'chill_main_advanced_search')]
    public function advancedSearchAction(mixed $name, Request $request)
    {
        try {
            /** @var Chill\MainBundle\Search\SearchProvider $variable */
            $searchProvider = $this->searchProvider;
            /** @var Chill\MainBundle\Search\HasAdvancedSearchFormInterface $variable */
            $search = $this->searchProvider
                ->getHasAdvancedFormByName($name);
        } catch (UnknowSearchNameException) {
            throw $this->createNotFoundException('no advanced search for '."{$name}");
        }

        if ($request->query->has('q')) {
            $data = $search->convertTermsToFormData($searchProvider->parse(
                $request->query->get('q')
            ));
        }

        $form = $this->createAdvancedSearchForm($name, $data ?? []);

        if ($request->isMethod(Request::METHOD_POST)) {
            $form->handleRequest($request);

            if ($form->isValid()) {
                $pattern = $this->searchProvider
                    ->getHasAdvancedFormByName($name)
                    ->convertFormDataToQuery($form->getData());

                return $this->redirectToRoute('chill_main_search', [
                    'q' => $pattern, 'name' => $name,
                ]);
            }
        }

        return $this->render(
            '@ChillMain/Search/advanced_search.html.twig',
            [
                'form' => $form->createView(),
                'name' => $name,
                'title' => $search->getAdvancedSearchTitle(),
            ]
        );
    }

    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/search/advanced', name: 'chill_main_advanced_search_list')]
    public function advancedSearchListAction(Request $request)
    {
        /** @var Chill\MainBundle\Search\SearchProvider $variable */
        $searchProvider = $this->searchProvider;
        $advancedSearchProviders = $searchProvider
            ->getHasAdvancedFormSearchServices();

        if (1 === \count($advancedSearchProviders)) {
            return $this->redirectToRoute('chill_main_advanced_search', [
                'name' => array_key_first($advancedSearchProviders),
            ]);
        }

        return $this->render('@ChillMain/Search/choose_list.html.twig');
    }

    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/search.{_format}', name: 'chill_main_search', requirements: ['_format' => 'html|json', '_locale' => '[a-z]{1,3}'], defaults: ['_format' => 'html'])]
    public function searchAction(Request $request, mixed $_format)
    {
        $pattern = trim((string) $request->query->get('q', ''));

        if ('' === $pattern) {
            switch ($_format) {
                case 'html':
                    return $this->render(
                        '@ChillMain/Search/error.html.twig',
                        [
                            'message' => $this->translator->trans('Your search is empty. '
                                  .'Please provide search terms.'),
                            'pattern' => $pattern,
                        ]
                    );

                case 'json':
                    return new JsonResponse([
                        'results' => [],
                        'pagination' => ['more' => false],
                    ]);
            }
        }

        $name = $request->query->get('name', null);

        try {
            if (null === $name) {
                if ('json' === $_format) {
                    return new JsonResponse('Currently, we still do not aggregate results '
                        .'from different providers', JsonResponse::HTTP_BAD_REQUEST);
                }

                // no specific search selected. Rendering result in "preview" mode
                $results = $this->searchProvider
                    ->getSearchResults(
                        $pattern,
                        0,
                        5,
                        [SearchInterface::SEARCH_PREVIEW_OPTION => true]
                    );
            } else {
                // we want search on a specific search provider. Display full results.
                $results = [$this->searchProvider
                    ->getResultByName(
                        $pattern,
                        $name,
                        $this->paginatorFactory->getCurrentPageFirstItemNumber(),
                        $this->paginatorFactory->getCurrentItemsPerPage(),
                        [
                            SearchInterface::SEARCH_PREVIEW_OPTION => false,
                            SearchInterface::REQUEST_QUERY_PARAMETERS => $request
                                ->get(SearchInterface::REQUEST_QUERY_KEY_ADD_PARAMETERS, []),
                        ],
                        $_format
                    ), ];

                if ('json' === $_format) {
                    return new JsonResponse(\reset($results));
                }
            }
        } catch (UnknowSearchDomainException $ex) {
            return $this->render(
                '@ChillMain/Search/error.html.twig',
                [
                    'message' => $this->translator->trans('The domain %domain% '
                          .'is unknow. Please check your search.', ['%domain%' => $ex->getDomain()]),
                    'pattern' => $pattern,
                ]
            );
        } catch (UnknowSearchNameException $ex) {
            throw $this->createNotFoundException('The name '.$ex->getName().' is not found');
        } catch (ParsingException $ex) {
            return $this->render(
                '@ChillMain/Search/error.html.twig',
                [
                    'message' => $this->translator->trans('Invalid terms').
                    ': '.$this->translator->trans($ex->getMessage()),
                    'pattern' => $pattern,
                ]
            );
        }

        return $this->render(
            '@ChillMain/Search/list.html.twig',
            ['results' => $results, 'pattern' => $pattern]
        );
    }

    #[\Symfony\Component\Routing\Annotation\Route(path: '/api/1.0/search.{_format}', name: 'chill_main_search_global', requirements: ['_format' => 'json'], defaults: ['_format' => 'json'])]
    public function searchApi(Request $request, mixed $_format): JsonResponse
    {
        // TODO this is an incomplete implementation
        $query = $request->query->get('q', '');
        $types = $request->query->all('type');

        if (0 === \count($types)) {
            throw new BadRequestHttpException('The request must contains at  one type');
        }

        try {
            $collection = $this->searchApi->getResults($query, $types, []);
        } catch (SearchApiNoQueryException $e) {
            throw new BadRequestHttpException($e->getMessage(), $e);
        }

        return $this->json($collection, \Symfony\Component\HttpFoundation\Response::HTTP_OK, [], ['groups' => ['read']]);
    }

    protected function createAdvancedSearchForm($name, array $data = [])
    {
        $builder = $this
            ->get('form.factory')
            ->createNamedBuilder(
                '',
                FormType::class,
                $data,
                ['method' => Request::METHOD_POST]
            );

        $this->searchProvider
            ->getHasAdvancedFormByName($name)
            ->buildForm($builder);

        $builder->add('submit', SubmitType::class, [
            'label' => 'Search',
        ]);

        return $builder->getForm();
    }
}
