<?php

namespace App\Tests\Controller;

use App\Entity\Billet;
use App\Entity\BilletBuyer;
use App\Entity\BilletBuyerItem;
use App\Entity\Category;
use App\Entity\Event;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class OrderControllerTest extends WebTestCase
{
    private function createOrga(EntityManagerInterface $em): User
    {
        $user = new User();
        $user->setEmail('orga-order-'.uniqid().'@test.fr');
        $user->setFirstName('Orga');
        $user->setLastName('Test');
        $user->setPassword('hashed');
        $user->setRoles(['ROLE_ORGANIZER']);
        $user->setIsApproved(true);
        $user->setIsVerified(true);
        $user->setCompanyName('Asso Test');
        $user->setStripeAccountId('acct_test');
        $em->persist($user);

        return $user;
    }

    private function createEventWithBillet(EntityManagerInterface $em, User $user): array
    {
        $event = new Event();
        $event->setAccount($user);
        $event->setTitle('Order Event');
        $event->setStartAt(new \DateTimeImmutable('2026-08-01 10:00'));
        $event->setEndAt(new \DateTimeImmutable('2026-08-01 18:00'));
        $event->setAddress('1 rue');
        $event->setZipcode('75001');
        $event->setCity('Paris');
        $event->setIsOnline(true);
        $em->persist($event);

        $category = new Category();
        $category->setName('Cat');
        $category->setEvent($event);
        $category->setStartAt(new \DateTimeImmutable('-1 day'));
        $category->setEndAt(new \DateTimeImmutable('+30 days'));
        $em->persist($category);

        $billet = new Billet();
        $billet->setName('Entree');
        $billet->setCategory($category);
        $billet->setPriceHT(1500);
        $billet->setQuantity(10);
        $em->persist($billet);

        $em->flush();

        return [$event, $billet];
    }

    private function createOrder(EntityManagerInterface $em, Event $event, Billet $billet, ?User $user = null): BilletBuyer
    {
        $count = $em->getRepository(BilletBuyer::class)->count([]) + 1;
        $order = new BilletBuyer();
        $order->setEvent($event);
        $order->setFirstName('Jean');
        $order->setLastName('Dupont');
        $order->setEmail('jean@test.fr');
        $order->setOrderNumber('2026-03-21-'.$count);
        $order->setTotalHT(1500);
        if ($user) {
            $order->setUser($user);
        }

        $item = new BilletBuyerItem();
        $item->setBillet($billet);
        $item->setBilletName('Entree');
        $item->setQuantity(1);
        $item->setUnitPriceHT(1500);
        $order->addItem($item);

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

        return $order;
    }

    // === CREATE ===

    public function testCreateOrderNotFound(): void
    {
        $client = static::createClient();
        $client->request('POST', '/evenement/999999/commander', [], [], ['CONTENT_TYPE' => 'application/json'], '[]');
        self::assertResponseStatusCodeSame(404);
    }

    public function testCreateOrderEmptyCart(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], '[]');
        self::assertResponseStatusCodeSame(400);

        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertArrayHasKey('redirect', $data);
    }

    public function testCreateOrderAsGuest(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $billet->getId(), 'qty' => 2],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertStringContainsString('/commande/', $data['redirect']);
        self::assertStringContainsString('/informations', $data['redirect']);
    }

    public function testCreateOrderAsLoggedUser(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $buyer = new User();
        $buyer->setEmail('buyer-'.uniqid().'@test.fr');
        $buyer->setFirstName('Buyer');
        $buyer->setLastName('Test');
        $buyer->setPassword('hashed');
        $em->persist($buyer);
        $em->flush();

        $client->loginUser($buyer);
        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $billet->getId(), 'qty' => 1],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertStringContainsString('/paiement', $data['redirect']);
    }

    public function testCreateOrderInvalidBillet(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => 999999, 'qty' => 1],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertArrayHasKey('redirect', $data);
    }

    public function testCreateOrderNotBuyableBillet(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event] = $this->createEventWithBillet($em, $user);

        $notBuyable = new Billet();
        $notBuyable->setName('Not Buyable');
        $notBuyable->setCategory($em->getRepository(Category::class)->findOneBy(['event' => $event]));
        $notBuyable->setPriceHT(500);
        $notBuyable->setNotBuyable(true);
        $em->persist($notBuyable);
        $em->flush();

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $notBuyable->getId(), 'qty' => 1],
        ]));

        self::assertResponseIsSuccessful();
    }

    public function testCreateOrderExceedsQuantity(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $billet->getId(), 'qty' => 999],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertStringContainsString('/informations', $data['redirect']);
    }

    public function testCreateOrderUnlimitedBillet(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event] = $this->createEventWithBillet($em, $user);

        $category = $em->getRepository(Category::class)->findOneBy(['event' => $event]);
        $unlimited = new Billet();
        $unlimited->setName('Illimite');
        $unlimited->setCategory($category);
        $unlimited->setPriceHT(500);
        $unlimited->setQuantity(null);
        $em->persist($unlimited);
        $em->flush();

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $unlimited->getId(), 'qty' => 50],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertStringContainsString('/informations', $data['redirect']);
    }

    // === GUEST ===

    public function testGuestPageNotFound(): void
    {
        $client = static::createClient();
        $client->request('GET', '/commande/999999/informations');
        self::assertResponseStatusCodeSame(404);
    }

    public function testGuestPageRenders(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/commande/'.$order->getId().'/informations');
        self::assertResponseIsSuccessful();
    }

    public function testGuestSubmitEmptyFields(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('POST', '/commande/'.$order->getId().'/informations', [
            'first_name' => '',
            'last_name' => '',
            'email' => '',
        ]);

        self::assertResponseRedirects('/commande/'.$order->getId().'/informations');
    }

    public function testGuestSubmitSuccess(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $order = new BilletBuyer();
        $order->setEvent($event);
        $order->setTotalHT(1500);
        $order->setOrderNumber('2026-03-21-'.random_int(100, 999));
        $item = new BilletBuyerItem();
        $item->setBillet($billet);
        $item->setBilletName('Test');
        $item->setQuantity(1);
        $item->setUnitPriceHT(1500);
        $order->addItem($item);
        $em->persist($order);
        $em->flush();

        $client->request('POST', '/commande/'.$order->getId().'/informations', [
            'first_name' => 'Jean',
            'last_name' => 'Dupont',
            'email' => 'jean@test.fr',
        ]);

        self::assertResponseRedirects('/commande/'.$order->getId().'/paiement');
    }

    public function testGuestRedirectsIfUserSet(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $buyer = new User();
        $buyer->setEmail('buyer-guest-'.uniqid().'@test.fr');
        $buyer->setFirstName('B');
        $buyer->setLastName('T');
        $buyer->setPassword('hashed');
        $em->persist($buyer);

        $order = $this->createOrder($em, $event, $billet, $buyer);

        $client->request('GET', '/commande/'.$order->getId().'/informations');
        self::assertResponseRedirects('/commande/'.$order->getId().'/paiement');
    }

    // === PAYMENT ===

    public function testPaymentNotFound(): void
    {
        $client = static::createClient();
        $client->request('GET', '/commande/999999/paiement');
        self::assertResponseStatusCodeSame(404);
    }

    public function testPaymentRedirectsIfNoName(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $order = new BilletBuyer();
        $order->setEvent($event);
        $order->setTotalHT(1500);
        $order->setOrderNumber('2026-03-21-'.random_int(100, 999));
        $em->persist($order);
        $em->flush();

        $client->request('GET', '/commande/'.$order->getId().'/paiement');
        self::assertResponseRedirects('/commande/'.$order->getId().'/informations');
    }

    public function testPaymentRendersWithStripe(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/commande/'.$order->getId().'/paiement');
        self::assertResponseIsSuccessful();
    }

    public function testPaymentNotFoundWithoutStripeAccount(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);

        $user = new User();
        $user->setEmail('no-stripe-'.uniqid().'@test.fr');
        $user->setFirstName('No');
        $user->setLastName('Stripe');
        $user->setPassword('hashed');
        $user->setRoles(['ROLE_ORGANIZER']);
        $user->setIsApproved(true);
        $user->setIsVerified(true);
        $em->persist($user);

        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/commande/'.$order->getId().'/paiement');
        self::assertResponseStatusCodeSame(404);
    }

    // === SUCCESS ===

    public function testSuccessNotFound(): void
    {
        $client = static::createClient();
        $client->request('GET', '/commande/999999/confirmation');
        self::assertResponseStatusCodeSame(404);
    }

    public function testSuccessRenders(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/commande/'.$order->getId().'/confirmation');
        self::assertResponseIsSuccessful();
    }

    public function testSuccessFailed(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/commande/'.$order->getId().'/confirmation?redirect_status=failed');
        self::assertResponseIsSuccessful();
    }

    public function testSuccessSucceeded(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class);
        $billetOrderService->expects(self::once())->method('generateOrderTickets');
        $billetOrderService->expects(self::once())->method('generateAndSendTickets');
        static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService);

        $client->request('GET', '/commande/'.$order->getId().'/confirmation?redirect_status=succeeded');
        self::assertResponseIsSuccessful();
    }

    // === PUBLIC ORDER ===

    public function testPublicOrderNotFound(): void
    {
        $client = static::createClient();
        $client->request('GET', '/ma-commande/2026-03-21-999/invalidtoken');
        self::assertResponseStatusCodeSame(404);
    }

    public function testPublicOrderRenders(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/ma-commande/'.$order->getOrderNumber().'/'.$order->getAccessToken());
        self::assertResponseIsSuccessful();
    }

    // === INVOICE ===

    public function testInvoiceNotFound(): void
    {
        $client = static::createClient();
        $client->request('GET', '/ma-commande/2026-03-21-999/invalidtoken/facture');
        self::assertResponseStatusCodeSame(404);
    }

    public function testInvoiceNotPaid(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/ma-commande/'.$order->getOrderNumber().'/'.$order->getAccessToken().'/facture');
        self::assertResponseStatusCodeSame(404);
    }

    public function testInvoiceSuccess(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);
        $order->setStatus(BilletBuyer::STATUS_PAID);
        $em->flush();

        $invoiceService = $this->createMock(\App\Service\InvoiceService::class);
        $invoiceService->expects(self::once())->method('generatePdf')->willReturn('%PDF-1.4 fake');
        static::getContainer()->set(\App\Service\InvoiceService::class, $invoiceService);

        $client->request('GET', '/ma-commande/'.$order->getOrderNumber().'/'.$order->getAccessToken().'/facture');
        self::assertResponseIsSuccessful();
        self::assertResponseHeaderSame('Content-Type', 'application/pdf');
    }

    // === DOWNLOAD TICKET ===

    public function testDownloadTicketNotFoundOrder(): void
    {
        $client = static::createClient();
        $client->request('GET', '/ma-commande/2026-03-21-999/badtoken/billet/ETICKET-AAAA-BBBB-CCCC');
        self::assertResponseStatusCodeSame(404);
    }

    public function testDownloadTicketNotFoundTicket(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('GET', '/ma-commande/'.$order->getOrderNumber().'/'.$order->getAccessToken().'/billet/ETICKET-ZZZZ-ZZZZ-ZZZZ');
        self::assertResponseStatusCodeSame(404);
    }

    public function testDownloadTicketSuccess(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $ticket = new \App\Entity\BilletOrder();
        $ticket->setBilletBuyer($order);
        $ticket->setBillet($billet);
        $ticket->setBilletName('Entree');
        $ticket->setUnitPriceHT(1500);
        $em->persist($ticket);
        $em->flush();

        $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class);
        $billetOrderService->expects(self::once())->method('generatePdf')->willReturn('%PDF-1.4 fake');
        static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService);

        $client->request('GET', '/ma-commande/'.$order->getOrderNumber().'/'.$order->getAccessToken().'/billet/'.$ticket->getReference());
        self::assertResponseIsSuccessful();
        self::assertResponseHeaderSame('Content-Type', 'application/pdf');
    }

    // === CREATE with qty <= 0 ===

    public function testCreateOrderZeroQty(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $billet->getId(), 'qty' => 0],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertArrayHasKey('redirect', $data);
    }

    public function testCreateOrderEventEnded(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);

        $event = new Event();
        $event->setAccount($user);
        $event->setTitle('Past Event');
        $event->setStartAt(new \DateTimeImmutable('-2 days'));
        $event->setEndAt(new \DateTimeImmutable('-1 day'));
        $event->setAddress('1 rue');
        $event->setZipcode('75001');
        $event->setCity('Paris');
        $event->setIsOnline(true);
        $em->persist($event);
        $em->flush();

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => 1, 'qty' => 1],
        ]));

        self::assertResponseStatusCodeSame(400);
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertStringContainsString('termine', $data['error']);
    }

    public function testCreateOrderInvalidCartStructure(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event] = $this->createEventWithBillet($em, $user);

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['invalid' => 'data'],
        ]));

        self::assertResponseStatusCodeSame(400);
    }

    public function testGuestSubmitInvalidEmail(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);
        [$event, $billet] = $this->createEventWithBillet($em, $user);
        $order = $this->createOrder($em, $event, $billet);

        $client->request('POST', '/commande/'.$order->getId().'/informations', [
            'first_name' => 'Jean',
            'last_name' => 'Dupont',
            'email' => 'not-an-email',
        ]);

        self::assertResponseRedirects('/commande/'.$order->getId().'/informations');
    }

    public function testCreateOrderStockZeroSkipsBillet(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $user = $this->createOrga($em);

        $event = new Event();
        $event->setAccount($user);
        $event->setTitle('Stock Zero Event');
        $event->setStartAt(new \DateTimeImmutable('2026-08-01 10:00'));
        $event->setEndAt(new \DateTimeImmutable('2026-08-01 18:00'));
        $event->setAddress('1 rue');
        $event->setZipcode('75001');
        $event->setCity('Paris');
        $event->setIsOnline(true);
        $em->persist($event);

        $category = new Category();
        $category->setName('Cat');
        $category->setEvent($event);
        $category->setStartAt(new \DateTimeImmutable('-1 day'));
        $category->setEndAt(new \DateTimeImmutable('+30 days'));
        $em->persist($category);

        $billet = new Billet();
        $billet->setName('Sold Out');
        $billet->setCategory($category);
        $billet->setPriceHT(1500);
        $billet->setQuantity(0);
        $em->persist($billet);
        $em->flush();

        $client->request('POST', '/evenement/'.$event->getId().'/commander', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode([
            ['billetId' => $billet->getId(), 'qty' => 1],
        ]));

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertArrayHasKey('redirect', $data);
    }
}
