<?php

namespace App\Tests\Controller;

use App\Controller\AttestationController;
use App\Entity\Attestation;
use App\Entity\Event;
use App\Entity\Payout;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

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

        $user = new User();
        $user->setEmail('test-attest-'.uniqid().'@example.com');
        $user->setFirstName('Test');
        $user->setLastName('User');
        $user->setPassword('$2y$13$hashed');
        $em->persist($user);

        $payout = new Payout();
        $payout->setOrganizer($user);
        $payout->setStripePayoutId('po_check_'.uniqid());
        $payout->setStatus('paid');
        $payout->setAmount(10000);
        $payout->setCurrency('eur');
        $em->persist($payout);
        $em->flush();

        $client->request('GET', '/attestation/check/'.$payout->getStripePayoutId());

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'Attestation authentique');
    }

    public function testCheckWithInvalidPayout(): void
    {
        $client = static::createClient();
        $client->request('GET', '/attestation/check/po_fake_invalid');

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'Attestation introuvable');
    }

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

        $user = new User();
        $user->setEmail('test-vref-'.uniqid().'@example.com');
        $user->setFirstName('Test');
        $user->setLastName('User');
        $user->setPassword('$2y$13$hashed');
        $em->persist($user);

        $event = new Event();
        $event->setAccount($user);
        $event->setTitle('Test Event');
        $event->setStartAt(new \DateTimeImmutable('+1 day'));
        $event->setEndAt(new \DateTimeImmutable('+2 days'));
        $event->setAddress('123 Rue Test');
        $event->setZipcode('75001');
        $event->setCity('Paris');
        $em->persist($event);

        $reference = 'REF-'.uniqid();
        $payload = [
            'ref' => $reference,
            'event' => 'Test Event',
            'organizer' => 'Test User',
            'generatedAt' => '01/04/2026 10:00:00',
            'totalSold' => 10,
            'billets' => [],
            'categories' => [],
        ];

        $attestation = new Attestation($reference, 'sig_'.uniqid(), $event, $user, 10, $payload);
        $em->persist($attestation);
        $em->flush();

        $client->request('GET', '/attestation/ventes/r/'.$reference);

        self::assertResponseIsSuccessful();
    }

    public function testVentesRefWithInvalidReference(): void
    {
        $client = static::createClient();
        $client->request('GET', '/attestation/ventes/r/INVALID_REF');

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'introuvable');
    }

    public function testVentesWithValidHash(): void
    {
        $client = static::createClient();
        $appSecret = static::getContainer()->getParameter('kernel.secret');

        $data = ['event' => 'Test Event', 'total' => 5000];
        $hash = AttestationController::generateHash($data, $appSecret);

        $client->request('GET', '/attestation/ventes/'.$hash);

        self::assertResponseIsSuccessful();
    }

    public function testVentesWithValidHashAndRegisteredAttestation(): void
    {
        $client = static::createClient();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $appSecret = static::getContainer()->getParameter('kernel.secret');

        $user = new User();
        $user->setEmail('test-vreg-'.uniqid().'@example.com');
        $user->setFirstName('Test');
        $user->setLastName('User');
        $user->setPassword('$2y$13$hashed');
        $em->persist($user);

        $event = new Event();
        $event->setAccount($user);
        $event->setTitle('Test Event');
        $event->setStartAt(new \DateTimeImmutable('+1 day'));
        $event->setEndAt(new \DateTimeImmutable('+2 days'));
        $event->setAddress('123 Rue Test');
        $event->setZipcode('75001');
        $event->setCity('Paris');
        $em->persist($event);

        $ref = 'REF-'.uniqid();
        $data = [
            'ref' => $ref,
            'event' => 'Registered Event',
            'organizer' => 'Test User',
            'generatedAt' => '01/04/2026 10:00:00',
            'totalSold' => 20,
            'billets' => [],
            'categories' => [],
        ];
        $json = json_encode($data, \JSON_UNESCAPED_UNICODE);
        $signatureHash = hash_hmac('sha256', $json, $appSecret);

        $attestation = new Attestation($ref, $signatureHash, $event, $user, 20, $data);
        $em->persist($attestation);
        $em->flush();

        $hash = AttestationController::generateHash($data, $appSecret);
        $client->request('GET', '/attestation/ventes/'.$hash);

        self::assertResponseIsSuccessful();
    }

    public function testVentesWithInvalidBase64(): void
    {
        $client = static::createClient();
        $client->request('GET', '/attestation/ventes/!!!invalid-base64!!!');

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'introuvable');
    }

    public function testVentesWithMissingPipeSeparator(): void
    {
        $client = static::createClient();
        $hash = rtrim(strtr(base64_encode('no-pipe-here'), '+/', '-_'), '=');

        $client->request('GET', '/attestation/ventes/'.$hash);

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'introuvable');
    }

    public function testVentesWithInvalidSignature(): void
    {
        $client = static::createClient();
        $json = json_encode(['event' => 'Test'], \JSON_UNESCAPED_UNICODE);
        $hash = rtrim(strtr(base64_encode('invalidsignature|'.$json), '+/', '-_'), '=');

        $client->request('GET', '/attestation/ventes/'.$hash);

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'introuvable');
    }

    public function testVentesWithInvalidJsonPayload(): void
    {
        $client = static::createClient();
        $appSecret = static::getContainer()->getParameter('kernel.secret');

        $invalidJson = '{invalid json}';
        $signature = hash_hmac('sha256', $invalidJson, $appSecret);
        $hash = rtrim(strtr(base64_encode($signature.'|'.$invalidJson), '+/', '-_'), '=');

        $client->request('GET', '/attestation/ventes/'.$hash);

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('body', 'introuvable');
    }

    public function testGenerateHash(): void
    {
        $data = ['key' => 'value', 'amount' => 1000];
        $secret = 'test-secret';

        $hash = AttestationController::generateHash($data, $secret);

        self::assertNotEmpty($hash);
        self::assertMatchesRegularExpression('/^[A-Za-z0-9\-_]+$/', $hash);

        $decoded = base64_decode(strtr($hash, '-_', '+/'), true);
        self::assertNotFalse($decoded);

        $parts = explode('|', $decoded, 2);
        self::assertCount(2, $parts);

        [$signature, $jsonPayload] = $parts;
        $expectedSignature = hash_hmac('sha256', $jsonPayload, $secret);
        self::assertTrue(hash_equals($expectedSignature, $signature));
        self::assertSame($data, json_decode($jsonPayload, true));
    }

    public function testGenerateHashWithUnicodeData(): void
    {
        $data = ['name' => 'Événement été', 'city' => 'Zürich'];
        $secret = 'test-secret';

        $hash = AttestationController::generateHash($data, $secret);
        $decoded = base64_decode(strtr($hash, '-_', '+/'), true);
        $parts = explode('|', $decoded, 2);

        self::assertStringContainsString('Événement été', $parts[1]);
        self::assertStringContainsString('Zürich', $parts[1]);
    }
}
