<?php

namespace App\Tests\Controller;

use App\Entity\Billet;
use App\Entity\BilletBuyer;
use App\Entity\BilletBuyerItem;
use App\Entity\BilletOrder;
use App\Entity\Category;
use App\Entity\Payout;
use App\Entity\User;
use App\Service\AuditService;
use App\Service\BilletOrderService;
use App\Service\MailerService;
use App\Service\PayoutPdfService;
use App\Service\StripeService;
use Doctrine\ORM\EntityManagerInterface;
use Stripe\Event;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class StripeWebhookControllerTest extends WebTestCase
{
    public function testWebhookWithValidSignature(): void
    {
        $client = static::createClient();

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn(new Event());
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], [
            'HTTP_STRIPE_SIGNATURE' => 'valid',
        ], '{}');

        self::assertResponseIsSuccessful();
        self::assertSame('OK', $client->getResponse()->getContent());
    }

    public function testWebhookWithInvalidSignature(): void
    {
        $client = static::createClient();

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn(null);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], [
            'HTTP_STRIPE_SIGNATURE' => 'invalid',
        ], '{}');

        self::assertResponseStatusCodeSame(400);
    }

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

        $user = $this->createOrgaUser($em, 'acct_created');

        $event = new Event();
        $event->type = 'v2.core.account.created';

        $this->mockStripe($client, $event);

        $payload = json_encode(['related_object' => ['id' => 'acct_created']]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_created']);
        self::assertSame('started', $updated->getStripeStatus());
    }

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

        $user = $this->createOrgaUser($em, 'acct_closed');
        $user->setStripeChargesEnabled(true);
        $user->setStripePayoutsEnabled(true);
        $em->flush();

        $event = new Event();
        $event->type = 'v2.core.account.closed';

        $this->mockStripe($client, $event);

        $payload = json_encode(['related_object' => ['id' => 'acct_closed']]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_closed']);
        self::assertSame('closed', $updated->getStripeStatus());
        self::assertFalse($updated->isStripeChargesEnabled());
        self::assertFalse($updated->isStripePayoutsEnabled());
    }

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

        $this->createOrgaUser($em, 'acct_upd');

        $event = new Event();
        $event->type = 'v2.core.account.updated';

        $this->mockStripe($client, $event);

        $payload = json_encode(['related_object' => ['id' => 'acct_upd']]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_upd']);
        self::assertSame('updated', $updated->getStripeStatus());
    }

    public function testAccountStatusUnknownUser(): void
    {
        $client = static::createClient();

        $event = new Event();
        $event->type = 'v2.core.account.created';

        $this->mockStripe($client, $event);

        $payload = json_encode(['related_object' => ['id' => 'acct_nonexistent']]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();
    }

    public function testAccountStatusNoRelatedObject(): void
    {
        $client = static::createClient();

        $event = new Event();
        $event->type = 'v2.core.account.created';

        $this->mockStripe($client, $event);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();
    }

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

        $this->createOrgaUser($em, 'acct_merchant');

        $event = new Event();
        $event->type = 'v2.core.account[configuration.merchant].capability_status_updated';

        $this->mockStripe($client, $event);

        $payload = json_encode([
            'related_object' => ['id' => 'acct_merchant'],
            'changes' => ['after' => ['configuration' => [
                'merchant' => ['capabilities' => [
                    'card_payments' => ['status' => 'active'],
                ]],
            ]]],
        ]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_merchant']);
        self::assertTrue($updated->isStripeChargesEnabled());
    }

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

        $this->createOrgaUser($em, 'acct_recipient');

        $event = new Event();
        $event->type = 'v2.core.account[configuration.recipient].capability_status_updated';

        $this->mockStripe($client, $event);

        $payload = json_encode([
            'related_object' => ['id' => 'acct_recipient'],
            'changes' => ['after' => ['configuration' => [
                'recipient' => ['capabilities' => [
                    'stripe_balance' => ['payouts' => ['status' => 'active']],
                ]],
            ]]],
        ]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_recipient']);
        self::assertTrue($updated->isStripePayoutsEnabled());
    }

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

        $user = $this->createOrgaUser($em, 'acct_both');
        $user->setStripeChargesEnabled(true);
        $em->flush();

        $event = new Event();
        $event->type = 'v2.core.account[configuration.merchant].capability_status_updated';

        $this->mockStripe($client, $event);

        $payload = json_encode([
            'related_object' => ['id' => 'acct_both'],
            'changes' => ['after' => ['configuration' => [
                'merchant' => ['capabilities' => [
                    'stripe_balance' => ['payouts' => ['status' => 'active']],
                ]],
            ]]],
        ]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_both']);
        self::assertSame('active', $updated->getStripeStatus());
    }

    public function testCapabilityUnknownUser(): void
    {
        $client = static::createClient();

        $event = new Event();
        $event->type = 'v2.core.account[configuration.merchant].capability_status_updated';

        $this->mockStripe($client, $event);

        $payload = json_encode([
            'related_object' => ['id' => 'acct_ghost'],
            'changes' => ['after' => ['configuration' => []]],
        ]);
        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload);

        self::assertResponseIsSuccessful();
    }

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

        $user = $this->createOrgaUser($em, 'acct_payout');

        $event = Event::constructFrom([
            'type' => 'payout.created',
            'account' => 'acct_payout',
            'data' => ['object' => [
                'id' => 'po_test_wh_'.uniqid(),
                'status' => 'pending',
                'amount' => 5000,
                'currency' => 'eur',
                'destination' => 'ba_xxx',
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::once())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();
    }

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

        $user = $this->createOrgaUser($em, 'acct_paid');

        $payoutId = 'po_paid_'.uniqid();
        $event = Event::constructFrom([
            'type' => 'payout.paid',
            'account' => 'acct_paid',
            'data' => ['object' => [
                'id' => $payoutId,
                'status' => 'paid',
                'amount' => 10000,
                'currency' => 'eur',
                'destination' => 'ba_yyy',
                'arrival_date' => time() + 86400,
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::once())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $pdfService = $this->createMock(PayoutPdfService::class);
        $pdfService->expects(self::once())->method('generateToFile')->willReturn('/tmp/fake.pdf');
        static::getContainer()->set(PayoutPdfService::class, $pdfService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $payout = $freshEm->getRepository(Payout::class)->findOneBy(['stripePayoutId' => $payoutId]);
        self::assertNotNull($payout);
        self::assertSame('paid', $payout->getStatus());
        self::assertSame(10000, $payout->getAmount());
    }

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

        $user = $this->createOrgaUser($em, 'acct_upd_po');

        $payout = new Payout();
        $payout->setOrganizer($user);
        $payout->setStripePayoutId('po_existing_'.uniqid());
        $payout->setStatus('pending');
        $payout->setAmount(3000);
        $payout->setCurrency('eur');
        $payout->setStripeAccountId('acct_upd_po');
        $em->persist($payout);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'payout.updated',
            'account' => 'acct_upd_po',
            'data' => ['object' => [
                'id' => $payout->getStripePayoutId(),
                'status' => 'in_transit',
                'amount' => 3000,
                'currency' => 'eur',
                'destination' => 'ba_zzz',
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::once())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(Payout::class)->findOneBy(['stripePayoutId' => $payout->getStripePayoutId()]);
        self::assertSame('in_transit', $updated->getStatus());
    }

    public function testPayoutNoPayoutId(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'payout.created',
            'data' => ['object' => []],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();
    }

    public function testPayoutNoUser(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'payout.created',
            'account' => 'acct_nouser',
            'data' => ['object' => [
                'id' => 'po_nouser_'.uniqid(),
                'status' => 'pending',
                'amount' => 1000,
                'currency' => 'eur',
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');

        self::assertResponseIsSuccessful();
    }

    // === payment_intent.payment_failed ===

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

        $user = $this->createOrgaUser($em, 'acct_pf_'.uniqid());
        $order = $this->createTestOrder($em, $user);

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => [
                'metadata' => ['order_id' => (string) $order->getId()],
                'last_payment_error' => ['message' => 'Your card was declined.'],
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::once())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        $audit->expects(self::once())->method('log')->with('payment_failed', 'BilletBuyer', $order->getId(), $this->anything());
        static::getContainer()->set(AuditService::class, $audit);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId());
        self::assertSame(BilletBuyer::STATUS_CANCELLED, $updated->getStatus());
    }

    public function testPaymentFailedNoOrderId(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => ['metadata' => []]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

    public function testPaymentFailedOrderNotFound(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => [
                'metadata' => ['order_id' => '999999'],
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

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

        $user = $this->createOrgaUser($em, 'acct_pf_paid_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $order->setStatus(BilletBuyer::STATUS_PAID);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => [
                'metadata' => ['order_id' => (string) $order->getId()],
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId());
        self::assertSame(BilletBuyer::STATUS_PAID, $updated->getStatus());
    }

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

        $user = $this->createOrgaUser($em, 'acct_pf_def_'.uniqid());
        $order = $this->createTestOrder($em, $user);

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => [
                'metadata' => ['order_id' => (string) $order->getId()],
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::once())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        $audit->expects(self::once())->method('log')->with(
            'payment_failed',
            'BilletBuyer',
            $order->getId(),
            $this->callback(fn (array $d) => 'Paiement refuse' === $d['error'])
        );
        static::getContainer()->set(AuditService::class, $audit);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

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

        $user = $this->createOrgaUser($em, 'acct_pf_nomail_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $order->setEmail(null);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'payment_intent.payment_failed',
            'data' => ['object' => [
                'metadata' => ['order_id' => (string) $order->getId()],
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::never())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        static::getContainer()->set(AuditService::class, $audit);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

    // === charge.refunded ===

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

        $user = $this->createOrgaUser($em, 'acct_cr_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $order->setStatus(BilletBuyer::STATUS_PAID);
        $order->setStripeSessionId('pi_refund_test_'.uniqid());
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => [
                'payment_intent' => $order->getStripeSessionId(),
            ]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::atLeastOnce())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        $audit->expects(self::once())->method('log')->with('order_refunded_webhook', 'BilletBuyer', $order->getId(), $this->anything());
        static::getContainer()->set(AuditService::class, $audit);

        $billetOrderService = $this->createMock(BilletOrderService::class);
        $billetOrderService->expects(self::once())->method('notifyOrganizerCancelled')->with($this->anything(), 'remboursee');
        static::getContainer()->set(BilletOrderService::class, $billetOrderService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId());
        self::assertSame(BilletBuyer::STATUS_REFUNDED, $updated->getStatus());
    }

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

        $user = $this->createOrgaUser($em, 'acct_cr_tk_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $order->setStatus(BilletBuyer::STATUS_PAID);
        $piId = 'pi_ticket_refund_'.uniqid();
        $order->setStripeSessionId($piId);

        $ticket = new BilletOrder();
        $ticket->setBilletBuyer($order);
        $ticket->setBilletName('Entree');
        $ticket->setUnitPriceHT(1500);
        $ticket->setState(BilletOrder::STATE_VALID);
        $em->persist($ticket);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => ['payment_intent' => $piId]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        static::getContainer()->set(AuditService::class, $audit);

        $billetOrderService = $this->createMock(BilletOrderService::class);
        static::getContainer()->set(BilletOrderService::class, $billetOrderService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updatedTicket = $freshEm->getRepository(BilletOrder::class)->find($ticket->getId());
        self::assertSame(BilletOrder::STATE_INVALID, $updatedTicket->getState());
    }

    public function testChargeRefundedNoPaymentIntent(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => []],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

    public function testChargeRefundedOrderNotFound(): void
    {
        $client = static::createClient();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => ['payment_intent' => 'pi_nonexistent_'.uniqid()]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

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

        $user = $this->createOrgaUser($em, 'acct_cr_dup_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $piId = 'pi_already_refunded_'.uniqid();
        $order->setStatus(BilletBuyer::STATUS_REFUNDED);
        $order->setStripeSessionId($piId);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => ['payment_intent' => $piId]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();

        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId());
        self::assertSame(BilletBuyer::STATUS_REFUNDED, $updated->getStatus());
    }

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

        $user = $this->createOrgaUser($em, 'acct_cr_nomail_'.uniqid());
        $order = $this->createTestOrder($em, $user);
        $order->setStatus(BilletBuyer::STATUS_PAID);
        $piId = 'pi_nomail_'.uniqid();
        $order->setStripeSessionId($piId);
        $order->setEmail(null);
        $em->flush();

        $event = Event::constructFrom([
            'type' => 'charge.refunded',
            'data' => ['object' => ['payment_intent' => $piId]],
        ]);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects(self::never())->method('sendEmail');
        static::getContainer()->set(MailerService::class, $mailer);

        $audit = $this->createMock(AuditService::class);
        static::getContainer()->set(AuditService::class, $audit);

        $billetOrderService = $this->createMock(BilletOrderService::class);
        static::getContainer()->set(BilletOrderService::class, $billetOrderService);

        $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}');
        self::assertResponseIsSuccessful();
    }

    private function createTestOrder(EntityManagerInterface $em, User $user): BilletBuyer
    {
        $event = new \App\Entity\Event();
        $event->setAccount($user);
        $event->setTitle('WH Event '.uniqid());
        $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);
        $em->persist($category);

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

        $count = $em->getRepository(BilletBuyer::class)->count([]) + 1;
        $order = new BilletBuyer();
        $order->setEvent($event);
        $order->setFirstName('Jean');
        $order->setLastName('Dupont');
        $order->setEmail('jean-wh@test.fr');
        $order->setOrderNumber('2026-03-23-'.$count);
        $order->setTotalHT(1500);

        $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;
    }

    private function createOrgaUser(EntityManagerInterface $em, string $stripeAccountId): User
    {
        $user = new User();
        $user->setEmail('test-wh-'.uniqid().'@example.com');
        $user->setFirstName('WH');
        $user->setLastName('Test');
        $user->setPassword('$2y$13$hashed');
        $user->setRoles(['ROLE_ORGANIZER']);
        $user->setStripeAccountId($stripeAccountId);
        $em->persist($user);
        $em->flush();

        return $user;
    }

    private function mockStripe(\Symfony\Bundle\FrameworkBundle\KernelBrowser $client, Event $event): void
    {
        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('verifyWebhookSignature')->willReturn($event);
        static::getContainer()->set(StripeService::class, $stripeService);
    }
}
