<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\CalendarBundle\Tests\Controller;

use Chill\CalendarBundle\Controller\MyInvitationsController;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\Repository\InviteRepository;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Pagination\PaginatorInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Twig\Environment;

/**
 * @internal
 *
 * @coversNothing
 */
final class MyInvitationsControllerTest extends TestCase
{
    use ProphecyTrait;

    private MyInvitationsController $controller;

    protected function setUp(): void
    {
        // Create prophecies for dependencies
        $inviteRepository = $this->prophesize(InviteRepository::class);
        $paginatorFactory = $this->prophesize(PaginatorFactory::class);
        $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);

        // Create controller instance
        $this->controller = new MyInvitationsController(
            $inviteRepository->reveal(),
            $paginatorFactory->reveal(),
            $docGeneratorTemplateRepository->reveal()
        );

        // Set up necessary services for AbstractController
        $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
        $tokenStorage = $this->prophesize(TokenStorageInterface::class);
        $twig = $this->prophesize(Environment::class);

        // Use reflection to set the container
        $reflection = new \ReflectionClass($this->controller);
        $containerProperty = $reflection->getParentClass()->getProperty('container');
        $containerProperty->setAccessible(true);

        // Create a mock container
        $container = $this->prophesize(\Psr\Container\ContainerInterface::class);
        $container->has('security.authorization_checker')->willReturn(true);
        $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
        $container->has('security.token_storage')->willReturn(true);
        $container->get('security.token_storage')->willReturn($tokenStorage->reveal());
        $container->has('twig')->willReturn(true);
        $container->get('twig')->willReturn($twig->reveal());

        $containerProperty->setValue($this->controller, $container->reveal());
    }

    public function testMyInvitationsReturnsCorrectAmountOfInvitations(): void
    {
        // Create test user
        $user = new User();
        $user->setUsername('testuser');

        // Create test invitations
        $invite1 = new Invite();
        $invite1->setUser($user);
        $invite1->setStatus(Invite::PENDING);

        $invite2 = new Invite();
        $invite2->setUser($user);
        $invite2->setStatus(Invite::ACCEPTED);

        $invite3 = new Invite();
        $invite3->setUser($user);
        $invite3->setStatus(Invite::DECLINED);

        $allInvitations = [$invite1, $invite2, $invite3];
        $paginatedInvitations = [$invite1, $invite2]; // First page with 2 items per page

        // Set up repository prophecies
        $inviteRepository = $this->prophesize(InviteRepository::class);
        $inviteRepository->findBy(['user' => $user])->willReturn($allInvitations);
        $inviteRepository->findBy(
            ['user' => $user],
            ['createdAt' => 'DESC'],
            2, // items per page
            0  // offset
        )->willReturn($paginatedInvitations);

        // Set up paginator prophecies
        $paginator = $this->prophesize(PaginatorInterface::class);
        $paginator->getItemsPerPage()->willReturn(2);
        $paginator->getCurrentPageFirstItemNumber()->willReturn(0);

        $paginatorFactory = $this->prophesize(PaginatorFactory::class);
        $paginatorFactory->create(3)->willReturn($paginator->reveal());

        // Set up doc generator repository
        $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
        $docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]);

        // Create controller with mocked dependencies
        $controller = new MyInvitationsController(
            $inviteRepository->reveal(),
            $paginatorFactory->reveal(),
            $docGeneratorTemplateRepository->reveal()
        );

        // Set up authorization checker to return true for ROLE_USER
        $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
        $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true);

        // Set up token storage to return user
        $token = $this->prophesize(TokenInterface::class);
        $token->getUser()->willReturn($user);
        $tokenStorage = $this->prophesize(TokenStorageInterface::class);
        $tokenStorage->getToken()->willReturn($token->reveal());

        // Set up twig to return a response
        $twig = $this->prophesize(Environment::class);
        $twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [
            'invitations' => $paginatedInvitations,
            'paginator' => $paginator->reveal(),
            'templates' => [],
        ])->willReturn('rendered content');

        // Set up container
        $container = $this->prophesize(\Psr\Container\ContainerInterface::class);
        $container->has('security.authorization_checker')->willReturn(true);
        $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
        $container->has('security.token_storage')->willReturn(true);
        $container->get('security.token_storage')->willReturn($tokenStorage->reveal());
        $container->has('twig')->willReturn(true);
        $container->get('twig')->willReturn($twig->reveal());

        // Use reflection to set the container
        $reflection = new \ReflectionClass($controller);
        $containerProperty = $reflection->getParentClass()->getProperty('container');
        $containerProperty->setAccessible(true);
        $containerProperty->setValue($controller, $container->reveal());

        // Create request
        $request = new Request();

        // Execute the action
        $response = $controller->myInvitations($request);

        // Assert that response is successful
        self::assertInstanceOf(Response::class, $response);
        self::assertSame(200, $response->getStatusCode());
        self::assertSame('rendered content', $response->getContent());
    }

    public function testMyInvitationsPageLoads(): void
    {
        // Create test user
        $user = new User();
        $user->setUsername('testuser');

        // Set up repository prophecies - no invitations
        $inviteRepository = $this->prophesize(InviteRepository::class);
        $inviteRepository->findBy(['user' => $user])->willReturn([]);
        $inviteRepository->findBy(
            ['user' => $user],
            ['createdAt' => 'DESC'],
            20, // default items per page
            0   // offset
        )->willReturn([]);

        // Set up paginator prophecies
        $paginator = $this->prophesize(PaginatorInterface::class);
        $paginator->getItemsPerPage()->willReturn(20);
        $paginator->getCurrentPageFirstItemNumber()->willReturn(0);

        $paginatorFactory = $this->prophesize(PaginatorFactory::class);
        $paginatorFactory->create(0)->willReturn($paginator->reveal());

        // Set up doc generator repository
        $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
        $docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]);

        // Create controller with mocked dependencies
        $controller = new MyInvitationsController(
            $inviteRepository->reveal(),
            $paginatorFactory->reveal(),
            $docGeneratorTemplateRepository->reveal()
        );

        // Set up authorization checker to return true for ROLE_USER
        $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
        $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true);

        // Set up token storage to return user
        $token = $this->prophesize(TokenInterface::class);
        $token->getUser()->willReturn($user);
        $tokenStorage = $this->prophesize(TokenStorageInterface::class);
        $tokenStorage->getToken()->willReturn($token->reveal());

        // Set up twig to return a response
        $twig = $this->prophesize(Environment::class);
        $twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [
            'invitations' => [],
            'paginator' => $paginator->reveal(),
            'templates' => [],
        ])->willReturn('empty page content');

        // Set up container
        $container = $this->prophesize(\Psr\Container\ContainerInterface::class);
        $container->has('security.authorization_checker')->willReturn(true);
        $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
        $container->has('security.token_storage')->willReturn(true);
        $container->get('security.token_storage')->willReturn($tokenStorage->reveal());
        $container->has('twig')->willReturn(true);
        $container->get('twig')->willReturn($twig->reveal());

        // Use reflection to set the container
        $reflection = new \ReflectionClass($controller);
        $containerProperty = $reflection->getParentClass()->getProperty('container');
        $containerProperty->setAccessible(true);
        $containerProperty->setValue($controller, $container->reveal());

        // Create request
        $request = new Request();

        // Execute the action
        $response = $controller->myInvitations($request);

        // Assert that page loads successfully
        self::assertInstanceOf(Response::class, $response);
        self::assertSame(200, $response->getStatusCode());
        self::assertSame('empty page content', $response->getContent());
    }

    public function testMyInvitationsRequiresAuthentication(): void
    {
        // Create controller with minimal dependencies
        $inviteRepository = $this->prophesize(InviteRepository::class);
        $paginatorFactory = $this->prophesize(PaginatorFactory::class);
        $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);

        $controller = new MyInvitationsController(
            $inviteRepository->reveal(),
            $paginatorFactory->reveal(),
            $docGeneratorTemplateRepository->reveal()
        );

        // Set up authorization checker to return false for ROLE_USER
        $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
        $authorizationChecker->isGranted('ROLE_USER')->willReturn(false);
        $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(false);

        // Set up container
        $container = $this->prophesize(\Psr\Container\ContainerInterface::class);
        $container->has('security.authorization_checker')->willReturn(true);
        $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());

        // Use reflection to set the container
        $reflection = new \ReflectionClass($controller);
        $containerProperty = $reflection->getParentClass()->getProperty('container');
        $containerProperty->setAccessible(true);
        $containerProperty->setValue($controller, $container->reveal());

        // Create request
        $request = new Request();

        // Expect AccessDeniedException
        $this->expectException(\Symfony\Component\Security\Core\Exception\AccessDeniedException::class);

        // Execute the action
        $controller->myInvitations($request);
    }
}
