<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\AdminBundle\Controller;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Host;
use Ferienpass\CoreBundle\Entity\Offer;
use Ferienpass\CoreBundle\Entity\Participant;
use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/search')]
class AdminSearchController extends AbstractController
{
    #[Route('', name: 'admin_search', methods: ['GET'])]
    public function search(Request $request, EntityManagerInterface $entityManager): Response
    {
        $query = (string) $request->query->get('q', '');
        $page = (int) $request->query->get('page', 1);
        $limit = 10;
        $offset = ($page - 1) * $limit;

        $mode = null;
        if (str_starts_with($query, '@')) {
            $mode = 'user';
            $query = mb_substr($query, 1);
        } elseif (str_starts_with($query, '#')) {
            $mode = 'participant';
            $query = mb_substr($query, 1);
        } elseif (str_starts_with($query, '>')) {
            $mode = 'offer';
            $query = mb_substr($query, 1);
        } elseif (str_starts_with($query, '€')) {
            $mode = 'payment';
            $query = mb_substr($query, 1);
        }

        if (mb_strlen($query) < 2) {
            return $this->render('@FerienpassAdmin/search/results.html.twig', [
                'results' => [],
            ]);
        }

        $results = [];
        if (null === $mode || 'offer' === $mode) {
            $results['offers'] = $this->searchOffers($query, $entityManager, $limit, $offset);
            $results['editions'] = $this->searchEditions($query, $entityManager, $limit, $offset);
        }
        if ($this->isGranted('ROLE_PARTICIPANTS_ADMIN')) {
            if (null === $mode || 'participant' === $mode) {
                $results['participants'] = $this->searchParticipants($query, $entityManager, $limit, $offset);
            }
        }

        if ($this->isGranted('ROLE_ADMIN')) {
            if (null === $mode || 'user' === $mode) {
                $results['users'] = $this->searchUsers($query, $entityManager, $limit, $offset);
            }
        }

        if ($this->isGranted('ROLE_PARTICIPANTS_ADMIN')) {
            if (null === $mode || 'payment' === $mode) {
                $results['payments'] = $this->searchPayments($query, $entityManager, $limit, $offset);
            }
        }

        if ($this->isGranted('ROLE_ADMIN')) {
            if (null === $mode || 'host' === $mode) {
                $results['hosts'] = $this->searchHosts($query, $entityManager, $limit, $offset);
            }
        }

        if ($page > 1 && empty(array_filter($results))) {
            return new Response('', Response::HTTP_NO_CONTENT);
        }

        return $this->render('@FerienpassAdmin/search/results.html.twig', [
            'results' => $results,
            'page' => $page,
        ]);
    }

    private function searchOffers(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        $qb = $entityManager->getRepository(Offer::class)->createQueryBuilder('o');
        $qb->where('o.name LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit);

        if (!$this->isGranted('ROLE_ADMIN')) {
            $qb->innerJoin('o.hosts', 'h')
                ->innerJoin('h.memberAssociations', 'ma')
                ->andWhere('ma.user = :user')
                ->setParameter('user', $this->getUser());
        }

        return $qb->getQuery()->getResult();
    }

    private function searchEditions(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        $editions = $entityManager->getRepository(Edition::class)->createQueryBuilder('e')
            ->where('e.name LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();

        if ($this->isGranted('ROLE_ADMIN')) {
            return $editions;
        }

        return array_filter($editions, fn (Edition $edition) => $this->isGranted('offer.create', $edition));
    }

    private function searchParticipants(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        return $entityManager->getRepository(Participant::class)->createQueryBuilder('p')
            ->where('p.firstname LIKE :query OR p.lastname LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    private function searchUsers(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        return $entityManager->getRepository(User::class)->createQueryBuilder('u')
            ->where('u.firstname LIKE :query OR u.lastname LIKE :query OR u.email LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    private function searchHosts(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        $qb = $entityManager->getRepository(Host::class)->createQueryBuilder('h');
        $qb->where('h.name LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit);

        if (!$this->isGranted('ROLE_ADMIN')) {
            $qb->innerJoin('h.memberAssociations', 'ma')
                ->andWhere('ma.user = :user')
                ->setParameter('user', $this->getUser());
        }

        return $qb->getQuery()->getResult();
    }

    private function searchPayments(string $query, EntityManagerInterface $entityManager, int $limit = 5, int $offset = 0): array
    {
        return $entityManager->getRepository(Payment::class)->createQueryBuilder('p')
            ->leftJoin('p.items', 'pi')
            ->leftJoin('pi.attendance', 'a')
            ->leftJoin('a.participant', 'part')
            ->where('p.receiptNumber LIKE :query')
            ->orWhere('p.billingAddress LIKE :query')
            ->orWhere('part.firstname LIKE :query')
            ->orWhere('part.lastname LIKE :query')
            ->setParameter('query', '%'.$query.'%')
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->groupBy('p.id')
            ->getQuery()
            ->getResult();
    }
}
