<?php

namespace App\Controller;

use App\Entity\Billet;
use App\Entity\BilletBuyer;
use App\Entity\BilletOrder;
use App\Entity\Category;
use App\Entity\Event;
use App\Entity\OrganizerInvitation;
use App\Entity\User;
use App\Service\EventIndexService;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;

class HomeController extends AbstractController
{
    private const BREADCRUMB_HOME = ['name' => 'Accueil', 'url' => '/'];
    private const BREADCRUMB_ORGANIZERS = ['name' => 'Organisateurs', 'url' => '/organisateurs'];

    #[Route('/', name: 'app_home')]
    public function index(EntityManagerInterface $em): Response
    {
        $allUsers = $em->getRepository(User::class)->findAll();
        $organizers = \count(array_filter($allUsers, fn (User $u) => \in_array('ROLE_ORGANIZER', $u->getRoles(), true) && $u->isApproved()));
        $eventsCount = \count($em->getRepository(Event::class)->findBy(['isOnline' => true, 'isSecret' => false]));
        $ticketsSold = $em->getRepository(BilletOrder::class)->count([]);
        $paidOrders = $em->getRepository(BilletBuyer::class)->findBy(['status' => BilletBuyer::STATUS_PAID]);

        $totalHT = 0;
        foreach ($paidOrders as $order) {
            $totalHT += $order->getTotalHT();
        }

        return $this->render('home/index.html.twig', [
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
            ],
            'stats' => [
                'events' => $eventsCount,
                'organizers' => $organizers,
                'tickets' => $ticketsSold,
                'totalHT' => $totalHT / 100,
                'orders' => \count($paidOrders),
            ],
        ]);
    }

    #[Route('/evenements', name: 'app_events')]
    public function events(Request $request, PaginatorInterface $paginator, EventIndexService $eventIndex, EntityManagerInterface $em): Response
    {
        $searchQuery = $request->query->getString('q', '');
        $city = $request->query->getString('city', '');
        $date = $request->query->getString('date', '');
        $hasFilters = '' !== $city || '' !== $date;

        if ($hasFilters) {
            $qb = $em->createQueryBuilder()
                ->select('e')
                ->from(Event::class, 'e')
                ->where('e.isOnline = true')
                ->andWhere('e.isSecret = false')
                ->orderBy('e.startAt', 'ASC');

            if ('' !== $searchQuery) {
                $searchResults = $eventIndex->searchEvents('event_global', $searchQuery, ['isOnline' => true, 'isSecret' => false]);
                $ids = array_map(fn ($e) => $e->getId(), $searchResults);
                if ($ids) {
                    $qb->andWhere('e.id IN (:ids)')->setParameter('ids', $ids);
                } else {
                    $qb->andWhere('1 = 0');
                }
            }

            if ('' !== $city) {
                $qb->andWhere('LOWER(e.city) LIKE LOWER(:city)')->setParameter('city', '%'.$city.'%');
            }

            if ('' !== $date) {
                $dateObj = new \DateTimeImmutable($date);
                $qb->andWhere('e.startAt >= :dateStart')->setParameter('dateStart', $dateObj->setTime(0, 0));
                $qb->andWhere('e.startAt <= :dateEnd')->setParameter('dateEnd', $dateObj->setTime(23, 59, 59));
            }

            $eventsQuery = $qb->getQuery()->getResult();
        } else {
            $eventsQuery = $eventIndex->searchEvents('event_global', $searchQuery, ['isOnline' => true, 'isSecret' => false]);
        }

        $events = $paginator->paginate($eventsQuery, $request->query->getInt('page', 1), 12);

        return $this->render('home/events.html.twig', [
            'events' => $events,
            'searchQuery' => $searchQuery,
            'city' => $city,
            'date' => $date,
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
                ['name' => 'Evenements', 'url' => '/evenements'],
            ],
        ]);
    }

    #[Route('/organisateurs', name: 'app_organizers')]
    public function organizers(EntityManagerInterface $em): Response
    {
        $allUsers = $em->getRepository(User::class)->findAll();
        $organizers = array_filter($allUsers, fn (User $u) => \in_array('ROLE_ORGANIZER', $u->getRoles(), true) && $u->isApproved());

        return $this->render('home/organizers.html.twig', [
            'organizers' => $organizers,
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
                self::BREADCRUMB_ORGANIZERS,
            ],
        ]);
    }

    #[Route('/organisateur/{id}-{slug}', name: 'app_organizer_detail', requirements: ['id' => '\d+', 'slug' => '[a-z0-9-]+'])]
    public function organizerDetail(int $id, string $slug, EntityManagerInterface $em): Response
    {
        $organizer = $em->getRepository(User::class)->find($id);

        if (!$organizer || !\in_array('ROLE_ORGANIZER', $organizer->getRoles(), true) || !$organizer->isApproved()) {
            throw $this->createNotFoundException('Organisateur introuvable.');
        }

        if ($slug !== $organizer->getSlug()) {
            return $this->redirectToRoute('app_organizer_detail', [
                'id' => $organizer->getId(),
                'slug' => $organizer->getSlug(),
            ], 301);
        }

        $events = $em->getRepository(Event::class)->findBy(
            ['account' => $organizer, 'isOnline' => true, 'isSecret' => false],
            ['startAt' => 'ASC'],
        );

        return $this->render('home/organizer_detail.html.twig', [
            'organizer' => $organizer,
            'events' => $events,
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
                self::BREADCRUMB_ORGANIZERS,
                ['name' => $organizer->getCompanyName() ?? $organizer->getFirstName().' '.$organizer->getLastName(), 'url' => '/organisateur/'.$organizer->getId().'-'.$organizer->getSlug()],
            ],
        ]);
    }

    #[Route('/evenement/{orgaSlug}/{id}-{eventSlug}', name: 'app_event_detail', requirements: ['id' => '\d+', 'orgaSlug' => '[a-z0-9-]+', 'eventSlug' => '[a-z0-9-]+'])]
    public function eventDetail(int $id, string $orgaSlug, string $eventSlug, EntityManagerInterface $em): Response
    {
        $event = $em->getRepository(Event::class)->find($id);

        if (!$event || !$event->isOnline()) {
            throw $this->createNotFoundException('Evenement introuvable.');
        }

        $organizer = $event->getAccount();
        if ($orgaSlug !== $organizer->getSlug()) {
            return $this->redirectToRoute('app_event_detail', [
                'orgaSlug' => $organizer->getSlug(),
                'id' => $event->getId(),
                'eventSlug' => $event->getSlug(),
            ], 301);
        }

        if ($eventSlug !== $event->getSlug()) {
            return $this->redirectToRoute('app_event_detail', [
                'orgaSlug' => $organizer->getSlug(),
                'id' => $event->getId(),
                'eventSlug' => $event->getSlug(),
            ], 301);
        }

        $categories = $em->getRepository(Category::class)->findBy(
            ['event' => $event, 'isHidden' => false],
            ['position' => 'ASC'],
        );

        $activeCategories = array_filter($categories, fn (Category $c) => $c->isActive());
        $allBillets = $activeCategories ? $em->getRepository(Billet::class)->findBy(
            ['category' => $activeCategories, 'notBuyable' => false],
            ['position' => 'ASC'],
        ) : [];

        $billets = [];
        foreach ($allBillets as $billet) {
            $billets[$billet->getCategory()->getId()][] = $billet;
        }

        return $this->render('home/event_detail.html.twig', [
            'event' => $event,
            'organizer' => $organizer,
            'categories' => $categories,
            'billets' => $billets,
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
                self::BREADCRUMB_ORGANIZERS,
                ['name' => $organizer->getCompanyName() ?? $organizer->getFirstName().' '.$organizer->getLastName(), 'url' => '/organisateur/'.$organizer->getId().'-'.$organizer->getSlug()],
                ['name' => $event->getTitle(), 'url' => '/evenement/'.$organizer->getSlug().'/'.$event->getId().'-'.$event->getSlug()],
            ],
        ]);
    }

    #[Route('/evenement/{id}/contact', name: 'app_event_contact', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function eventContact(int $id, Request $request, EntityManagerInterface $em, MailerService $mailerService): Response
    {
        $event = $em->getRepository(Event::class)->find($id);

        if (!$event || !$event->isOnline()) {
            throw $this->createNotFoundException('Evenement introuvable.');
        }

        $organizer = $event->getAccount();
        $name = trim($request->request->getString('name'));
        $firstname = trim($request->request->getString('firstname'));
        $email = trim($request->request->getString('email'));
        $message = trim($request->request->getString('message'));

        if ('' === $name || '' === $email || '' === $message) {
            $this->addFlash('error', 'Veuillez remplir tous les champs.');

            return $this->redirectToRoute('app_event_detail', [
                'orgaSlug' => $organizer->getSlug(),
                'id' => $event->getId(),
                'eventSlug' => $event->getSlug(),
            ]);
        }

        $html = $this->renderView('email/event_contact.html.twig', [
            'event' => $event,
            'senderName' => $firstname.' '.$name,
            'senderEmail' => $email,
            'senderMessage' => $message,
        ]);

        $mailerService->sendEmail(
            to: $organizer->getEmail(),
            subject: sprintf('Message pour votre evenement "%s" - E-Ticket', $event->getTitle()),
            content: $html,
            replyTo: $email,
            withUnsubscribe: false,
        );

        $this->addFlash('success', 'Votre message a ete envoye a l\'organisateur.');

        return $this->redirectToRoute('app_event_detail', [
            'orgaSlug' => $organizer->getSlug(),
            'id' => $event->getId(),
            'eventSlug' => $event->getSlug(),
        ]);
    }

    #[Route('/tarifs', name: 'app_tarifs')]
    public function tarifs(): Response
    {
        return $this->render('home/tarifs.html.twig', [
            'breadcrumbs' => [
                self::BREADCRUMB_HOME,
                ['name' => 'Tarifs', 'url' => '/tarifs'],
            ],
        ]);
    }

    #[Route('/evenement/{id}/stock', name: 'app_event_stock', requirements: ['id' => '\d+'], methods: ['GET'])]
    public function eventStock(int $id, EntityManagerInterface $em): Response
    {
        $event = $em->getRepository(Event::class)->find($id);
        if (!$event || !$event->isOnline()) {
            return $this->json([]);
        }

        $billets = $em->getRepository(Billet::class)
            ->createQueryBuilder('b')
            ->join('b.category', 'c')
            ->where('c.event = :event')
            ->setParameter('event', $event)
            ->getQuery()
            ->getResult();

        $stock = [];
        foreach ($billets as $billet) {
            $stock[$billet->getId()] = $billet->getQuantity();
        }

        return $this->json($stock);
    }

    #[Route('/offline', name: 'app_offline_page')]
    public function offline(): Response
    {
        return $this->render('home/offline.html.twig');
    }

    #[Route('/invitation/{token}', name: 'app_invitation_view', methods: ['GET'])]
    public function viewInvitation(string $token, EntityManagerInterface $em): Response
    {
        $invitation = $em->getRepository(OrganizerInvitation::class)->findOneBy(['token' => $token]);
        if (!$invitation) {
            throw $this->createNotFoundException();
        }

        if ($invitation->isExpired()) {
            return $this->render('home/invitation_landing.html.twig', [
                'invitation' => $invitation,
                'expired' => true,
            ]);
        }

        if (OrganizerInvitation::STATUS_SENT === $invitation->getStatus()) {
            $invitation->setStatus(OrganizerInvitation::STATUS_OPENED);
            $em->flush();
        }

        return $this->render('home/invitation_landing.html.twig', [
            'invitation' => $invitation,
            'expired' => false,
        ]);
    }

    #[Route('/invitation/{token}/{action}', name: 'app_invitation_respond', requirements: ['action' => 'accept|refuse'], methods: ['POST'])]
    public function respondInvitation(string $token, string $action, EntityManagerInterface $em, MailerService $mailerService): Response
    {
        $invitation = $em->getRepository(OrganizerInvitation::class)->findOneBy(['token' => $token]);
        if (!$invitation || !\in_array($invitation->getStatus(), [OrganizerInvitation::STATUS_SENT, OrganizerInvitation::STATUS_OPENED], true) || $invitation->isExpired()) {
            throw $this->createNotFoundException();
        }

        if ('accept' === $action) {
            $invitation->setStatus(OrganizerInvitation::STATUS_ACCEPTED);
        } else {
            $invitation->setStatus(OrganizerInvitation::STATUS_REFUSED);
        }

        $invitation->setRespondedAt(new \DateTimeImmutable());
        $em->flush();

        if ('accept' === $action) {
            $registerUrl = $this->generateUrl('app_invitation_register', [
                'token' => $invitation->getToken(),
            ], \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL);

            $html = $this->renderView('email/organizer_accepted.html.twig', [
                'invitation' => $invitation,
                'registerUrl' => $registerUrl,
            ]);

            $mailerService->sendEmail(
                $invitation->getEmail(),
                'Bienvenue sur E-Ticket - Finalisez votre inscription',
                $html,
            );
        }

        $statusLabel = 'accept' === $action ? 'acceptee' : 'refusee';
        $mailerService->sendEmail(
            $this->getParameter('admin_email'),
            'Invitation '.$statusLabel.' - '.$invitation->getCompanyName(),
            '<p>L\'invitation pour <strong>'.$invitation->getCompanyName().'</strong> ('.$invitation->getEmail().') a ete <strong>'.$statusLabel.'</strong> le '.$invitation->getRespondedAt()->format('d/m/Y H:i').'.</p>',
            null,
            null,
            false,
        );

        return $this->render('home/invitation_response.html.twig', [
            'invitation' => $invitation,
            'accepted' => 'accept' === $action,
        ]);
    }

    #[Route('/invitation/{token}/inscription', name: 'app_invitation_register', methods: ['GET', 'POST'])]
    public function invitationRegister(string $token, Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasher, Security $security): Response
    {
        $invitation = $em->getRepository(OrganizerInvitation::class)->findOneBy(['token' => $token]);
        if (!$invitation || OrganizerInvitation::STATUS_ACCEPTED !== $invitation->getStatus()) {
            throw $this->createNotFoundException();
        }

        $existing = $em->getRepository(User::class)->findOneBy(['email' => $invitation->getEmail()]);
        if ($existing) {
            $this->addFlash('error', 'Un compte existe deja avec l\'email '.$invitation->getEmail().'. Connectez-vous directement.');

            return $this->redirectToRoute('app_login');
        }

        if ($request->isMethod('POST')) {
            return $this->handleInvitationRegistration($invitation, $request, $em, $passwordHasher, $security);
        }

        return $this->render('home/invitation_register.html.twig', [
            'invitation' => $invitation,
        ]);
    }

    private function handleInvitationRegistration(OrganizerInvitation $invitation, Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasher, Security $security): Response
    {
        $firstName = trim($request->request->getString('first_name'));
        $lastName = trim($request->request->getString('last_name'));
        $password = $request->request->getString('password');

        if ('' === $firstName || '' === $lastName || '' === $password) {
            $this->addFlash('error', 'Tous les champs obligatoires doivent etre remplis.');

            return $this->redirectToRoute('app_invitation_register', ['token' => $invitation->getToken()]);
        }

        $user = new User();
        $user->setEmail($invitation->getEmail());
        $user->setFirstName($firstName);
        $user->setLastName($lastName);
        $user->setPassword($passwordHasher->hashPassword($user, $password));
        $user->setRoles(['ROLE_ORGANIZER']);
        $user->setCompanyName($invitation->getCompanyName());
        $user->setOffer($invitation->getOffer());
        $user->setCommissionRate($invitation->getCommissionRate());
        $user->setIsApproved(true);
        $user->setIsVerified(true);
        $billingAmount = $invitation->getBillingAmount() ?? 1000;
        $user->setBillingAmount($billingAmount);
        $user->setIsBilling(0 !== $billingAmount);
        $user->setBillingState('good');
        $user->setSiret(trim($request->request->getString('siret')) ?: null);
        $user->setAddress(trim($request->request->getString('address')) ?: null);
        $user->setPostalCode(trim($request->request->getString('postal_code')) ?: null);
        $user->setCity(trim($request->request->getString('city')) ?: null);
        $user->setPhone(trim($request->request->getString('phone')) ?: null);

        $em->persist($user);
        $em->flush();

        $security->login($user, 'form_login');

        $this->addFlash('success', 'Bienvenue ! Votre compte organisateur est actif.');

        return $this->redirectToRoute('app_account');
    }
}
