<?php

namespace App\Tests\Controller;

use App\Entity\OrganizerInvitation;
use App\Entity\User;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class InvitationFlowTest extends WebTestCase
{
    private function createInvitation(EntityManagerInterface $em, string $status = OrganizerInvitation::STATUS_SENT, ?string $email = null): OrganizerInvitation
    {
        $invitation = new OrganizerInvitation();
        $invitation->setCompanyName('Asso Test '.uniqid());
        $invitation->setFirstName('Jean');
        $invitation->setLastName('Dupont');
        $invitation->setEmail($email ?? 'invite-'.uniqid().'@example.com');
        $invitation->setOffer('basic');
        $invitation->setCommissionRate(2.5);
        $invitation->setStatus($status);

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

        return $invitation;
    }

    // --- viewInvitation ---

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

        $invitation = $this->createInvitation($em);

        $client->request('GET', '/invitation/'.$invitation->getToken());
        self::assertResponseIsSuccessful();
    }

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

        $invitation = $this->createInvitation($em);
        self::assertSame(OrganizerInvitation::STATUS_SENT, $invitation->getStatus());

        $client->request('GET', '/invitation/'.$invitation->getToken());

        $em->refresh($invitation);
        self::assertSame(OrganizerInvitation::STATUS_OPENED, $invitation->getStatus());
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_OPENED);

        $client->request('GET', '/invitation/'.$invitation->getToken());
        self::assertResponseIsSuccessful();

        $em->refresh($invitation);
        self::assertSame(OrganizerInvitation::STATUS_OPENED, $invitation->getStatus());
    }

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

        $invitation = $this->createInvitation($em);
        $ref = new \ReflectionProperty($invitation, 'createdAt');
        $ref->setValue($invitation, new \DateTimeImmutable('-10 days'));
        $em->flush();

        $client->request('GET', '/invitation/'.$invitation->getToken());
        self::assertResponseIsSuccessful();
        self::assertStringContainsString('expiree', $client->getResponse()->getContent());
    }

    public function testViewInvitationInvalidTokenReturns404(): void
    {
        $client = static::createClient();
        $client->request('GET', '/invitation/invalid-token-that-does-not-exist');

        self::assertResponseStatusCodeSame(404);
    }

    // --- respondInvitation ---

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

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

        $invitation = $this->createInvitation($em);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/accept');
        self::assertResponseIsSuccessful();

        $em->refresh($invitation);
        self::assertSame(OrganizerInvitation::STATUS_ACCEPTED, $invitation->getStatus());
        self::assertNotNull($invitation->getRespondedAt());
    }

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

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

        $invitation = $this->createInvitation($em);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/refuse');
        self::assertResponseIsSuccessful();

        $em->refresh($invitation);
        self::assertSame(OrganizerInvitation::STATUS_REFUSED, $invitation->getStatus());
        self::assertNotNull($invitation->getRespondedAt());
    }

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

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_OPENED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/accept');
        self::assertResponseIsSuccessful();

        $em->refresh($invitation);
        self::assertSame(OrganizerInvitation::STATUS_ACCEPTED, $invitation->getStatus());
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/accept');
        self::assertResponseStatusCodeSame(404);
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_REFUSED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/refuse');
        self::assertResponseStatusCodeSame(404);
    }

    public function testRespondInvalidTokenReturns404(): void
    {
        $client = static::createClient();
        $client->request('POST', '/invitation/invalid-token/accept');

        self::assertResponseStatusCodeSame(404);
    }

    // --- invitationRegister (GET) ---

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('GET', '/invitation/'.$invitation->getToken().'/inscription');
        self::assertResponseIsSuccessful();
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_SENT);

        $client->request('GET', '/invitation/'.$invitation->getToken().'/inscription');
        self::assertResponseStatusCodeSame(404);
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_REFUSED);

        $client->request('GET', '/invitation/'.$invitation->getToken().'/inscription');
        self::assertResponseStatusCodeSame(404);
    }

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

        $email = 'existing-'.uniqid().'@example.com';

        $existingUser = new User();
        $existingUser->setEmail($email);
        $existingUser->setFirstName('Existing');
        $existingUser->setLastName('User');
        $existingUser->setPassword('hashed');
        $em->persist($existingUser);
        $em->flush();

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED, $email);

        $client->request('GET', '/invitation/'.$invitation->getToken().'/inscription');
        self::assertResponseRedirects('/connexion');
    }

    // --- invitationRegister (POST) ---

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => 'Pierre',
            'last_name' => 'Martin',
            'password' => 'securepassword123',
            'siret' => '12345678901234',
            'address' => '10 rue de la Paix',
            'postal_code' => '75002',
            'city' => 'Paris',
            'phone' => '0612345678',
        ]);

        self::assertResponseRedirects('/mon-compte');

        $user = $em->getRepository(User::class)->findOneBy(['email' => $invitation->getEmail()]);
        self::assertNotNull($user);
        self::assertSame('Pierre', $user->getFirstName());
        self::assertSame('Martin', $user->getLastName());
        self::assertSame($invitation->getCompanyName(), $user->getCompanyName());
        self::assertSame('basic', $user->getOffer());
        self::assertSame(2.5, $user->getCommissionRate());
        self::assertTrue($user->isApproved());
        self::assertTrue($user->isVerified());
        self::assertContains('ROLE_ORGANIZER', $user->getRoles());
        self::assertSame('12345678901234', $user->getSiret());
        self::assertSame('Paris', $user->getCity());
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => 'Auto',
            'last_name' => 'Login',
            'password' => 'securepassword123',
        ]);

        self::assertResponseRedirects('/mon-compte');

        $client->followRedirect();
        self::assertResponseIsSuccessful();
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => '',
            'last_name' => '',
            'password' => '',
        ]);

        self::assertResponseRedirects('/invitation/'.$invitation->getToken().'/inscription');
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => 'Test',
            'last_name' => 'User',
            'password' => '',
        ]);

        self::assertResponseRedirects('/invitation/'.$invitation->getToken().'/inscription');

        $user = $em->getRepository(User::class)->findOneBy(['email' => $invitation->getEmail()]);
        self::assertNull($user);
    }

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

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => 'Minimal',
            'last_name' => 'User',
            'password' => 'securepassword123',
            'siret' => '',
            'address' => '',
            'postal_code' => '',
            'city' => '',
            'phone' => '',
        ]);

        self::assertResponseRedirects('/mon-compte');

        $user = $em->getRepository(User::class)->findOneBy(['email' => $invitation->getEmail()]);
        self::assertNotNull($user);
        self::assertNull($user->getSiret());
        self::assertNull($user->getCity());
        self::assertNull($user->getPhone());
    }

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

        $email = 'dup-'.uniqid().'@example.com';

        $existingUser = new User();
        $existingUser->setEmail($email);
        $existingUser->setFirstName('Existing');
        $existingUser->setLastName('User');
        $existingUser->setPassword('hashed');
        $em->persist($existingUser);
        $em->flush();

        $invitation = $this->createInvitation($em, OrganizerInvitation::STATUS_ACCEPTED, $email);

        $client->request('POST', '/invitation/'.$invitation->getToken().'/inscription', [
            'first_name' => 'Pierre',
            'last_name' => 'Martin',
            'password' => 'securepassword123',
        ]);

        self::assertResponseRedirects('/connexion');
    }

    // --- Full flow ---

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

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

        $invitation = $this->createInvitation($em);
        $token = $invitation->getToken();
        $email = $invitation->getEmail();
        $companyName = $invitation->getCompanyName();

        // Step 1: View invitation (status: sent → opened)
        $client->request('GET', '/invitation/'.$token);
        self::assertResponseIsSuccessful();

        // Step 2: Accept invitation (status: opened → accepted)
        $client->request('POST', '/invitation/'.$token.'/accept');
        self::assertResponseIsSuccessful();

        // Step 3: View registration form
        $client->request('GET', '/invitation/'.$token.'/inscription');
        self::assertResponseIsSuccessful();

        // Step 4: Submit registration
        $client->request('POST', '/invitation/'.$token.'/inscription', [
            'first_name' => 'Jean',
            'last_name' => 'Dupont',
            'password' => 'motdepasse123',
        ]);
        self::assertResponseRedirects('/mon-compte');

        // Verify user was created correctly
        $freshEm = static::getContainer()->get(EntityManagerInterface::class);
        $user = $freshEm->getRepository(User::class)->findOneBy(['email' => $email]);
        self::assertNotNull($user);
        self::assertTrue($user->isApproved());
        self::assertTrue($user->isVerified());
        self::assertContains('ROLE_ORGANIZER', $user->getRoles());
        self::assertSame($companyName, $user->getCompanyName());
    }
}
