<?php

namespace App\Tests\functional;

use App\Entity\Capsule;
use App\Entity\User;
use App\Repository\CapsuleRepository;
use App\Repository\UserRepository;
use Doctrine\Persistence\ObjectManager;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class CapsuleEditorControllerTest extends WebTestCase
{
    private KernelBrowser $client;
    private ObjectManager $object_manager;
    private User $user_author;
    private User $editor_non_author;
    private UserRepository $user_repository; /** @phpstan-ignore-line */
    private CapsuleRepository $capsule_repository; /** @phpstan-ignore-line */
    private Capsule $capsule;

    protected function setUp(): void
    {
        self::ensureKernelShutdown();

        $this->client = static::createClient();

        $this->object_manager = $this->client->getContainer()
            ->get('doctrine')
            ->getManager();

        $this->user_repository = $this->object_manager->getRepository(User::class);
        $this->capsule_repository = $this->object_manager->getRepository(Capsule::class);

        $this->setUsers();
        $this->setCapsule();
    }

    protected function tearDown(): void
    {
        parent::tearDown();
        self::ensureKernelShutdown();
    }

    public function testAuthorShouldBeAbleToDeleteACapsule(): void
    {
        $this->client->loginUser($this->user_author);
        $this->client->request('GET', '/en/capsule/delete/' . $this->capsule->getId());

        $this->assertResponseIsSuccessful();
    }

    public function testEditorNonAuthorShouldNotBeAbleToDeleteACapsule(): void
    {
        $this->client->loginUser($this->editor_non_author);
        $this->client->request('GET', '/en/capsule/delete/' . $this->capsule->getId());

        $this->assertResponseRedirects('/en/my_capsules', 302);
    }

    public function testAuthorShouldBeAbleToAddANewEditorForACapsule(): void
    {
        $uri = '/en/capsule/' . $this->capsule->getId() . '/editors';
        $this->client->loginUser($this->user_author);
        $crawler = $this->client->request('GET', $uri);

        $this->assertResponseIsSuccessful();

        $this->client->enableProfiler();
        $submit_button = $crawler->selectButton('Validate');
        $form = $submit_button->form();
        $form['capsule_editors_form[email]'] = $this->editor_non_author->getEmail();
        $this->client->submit($form);

        $this->assertResponseRedirects($uri, 302);
        $this->client->followRedirect();
        $this->assertResponseIsSuccessful($uri);

        $capsule_refreshed = $this->capsule_repository->findOneBy(['id' => $this->capsule->getId()]);
        if (! $capsule_refreshed instanceof Capsule) {
            throw new \Exception("Capsule does not exist.");
        }

        $editor = $this->user_repository->findOneBy(['id' => $this->editor_non_author->getId()]);
        $this->assertSame($editor, $capsule_refreshed->getEditors()->last());
    }

    public function testEditorShouldBeAbleToAccessTheCapsuleEditorsPage(): void
    {
        $this->capsule->addEditor($this->editor_non_author);
        $uri = '/en/capsule/' . $this->capsule->getId() . '/editors';
        $this->client->loginUser($this->editor_non_author);

        $this->client->request('GET', $uri);

        $this->assertResponseIsSuccessful();
    }

    public function testNonRegisteredUserAddedAsEditorShouldReceiveAnEmail(): void
    {
        $uri = '/en/capsule/' . $this->capsule->getId() . '/editors';
        $this->client->loginUser($this->user_author);
        $crawler = $this->client->request('GET', $uri);

        $this->assertResponseIsSuccessful();

        $this->client->enableProfiler();
        $submit_button = $crawler->selectButton('Validate');
        $non_registered_user_email = "non_registered_user@email.fr";
        $form = $submit_button->form();
        $form['capsule_editors_form[email]'] = $non_registered_user_email;
        $this->client->submit($form);

        $this->assertResponseRedirects($uri, 302);

        $this->assertEmailCount(1);
        $emailMessage = $this->getMailerMessage(0);

        if (null === $emailMessage) {
            throw new \Exception("Email message could not be found");
        }

        $this->assertEmailAddressContains(
            $emailMessage,
            'To',
            $non_registered_user_email
        );

        $this->client->followRedirect();
        $this->assertResponseIsSuccessful($uri);
    }

    public function testRegisteredUserShouldReceiveAnEmailWithCapsuleEditionLink(): void
    {
        $uri = '/en/capsule/' . $this->capsule->getId() . '/editors';
        $this->client->loginUser($this->user_author);
        $crawler = $this->client->request('GET', $uri);

        $this->assertResponseIsSuccessful();

        $this->client->enableProfiler();
        $submit_button = $crawler->selectButton('Validate');
        $form = $submit_button->form();
        $form['capsule_editors_form[email]'] = $this->editor_non_author->getEmail();
        $this->client->submit($form);

        $this->assertResponseRedirects($uri, 302);

        $this->assertEmailCount(1);
        $emailMessage = $this->getMailerMessage(0);

        if (null === $emailMessage) {
            throw new \Exception("Email message could not be found");
        }

        $this->assertEmailAddressContains(
            $emailMessage,
            'To',
            $this->editor_non_author->getEmail()
        );

        $this->client->followRedirect();
        $this->assertResponseIsSuccessful($uri);
    }

    public function testAlreadyAddedEditorShouldNotReceiveAnEmail(): void
    {
        $this->capsule->addEditor($this->editor_non_author);
        $this->object_manager->persist($this->capsule);
        $this->object_manager->flush();

        $this->assertContains($this->editor_non_author, $this->capsule->getEditors()->toArray());
        $uri = '/en/capsule/' . $this->capsule->getId() . '/editors';
        $this->client->loginUser($this->user_author);
        $crawler = $this->client->request('GET', $uri);

        $this->assertResponseIsSuccessful();

        $this->client->enableProfiler();
        $submit_button = $crawler->selectButton('Validate');
        $form = $submit_button->form();
        $form['capsule_editors_form[email]'] = $this->editor_non_author->getEmail();
        $this->client->submit($form);

        $this->assertEmailCount(1);
        $this->assertResponseRedirects($uri, 302);

        $this->client->followRedirect();
        $this->assertResponseIsSuccessful($uri);
    }

    private function setUsers(): void
    {
        $verified_user_1 = $this->user_repository
            ->findOneBy(['email' => 'defaultUser@localhost.com']);

        if (! $verified_user_1 instanceof User) {
            throw new \Exception("User does not exist.");
        }

        $this->user_author = $verified_user_1;

        $verified_user_2 = $this->user_repository
            ->findOneBy(['email' => 'defaultUser2@localhost.com']);

        if (! $verified_user_2 instanceof User) {
            throw new \Exception("User does not exist.");
        }

        $this->editor_non_author = $verified_user_2;
    }

    private function setCapsule(): void
    {
        $capsule = $this->capsule_repository->findOneBy(['name' => 'Pomme']);

        if (! $capsule instanceof Capsule) {
            throw new \Exception("Capsule does not exist.");
        }

        $this->capsule = $capsule;
    }
}