<?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\TicketBundle\Tests\Controller;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactoryInterface;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Repository\UserGroupRepositoryInterface;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\TicketBundle\Controller\TicketListApiController;
use Chill\TicketBundle\Entity\Ticket;
use Chill\TicketBundle\Repository\MotiveRepository;
use Chill\TicketBundle\Repository\TicketACLAwareRepositoryInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;

/**
 * @internal
 *
 * @covers \Chill\TicketBundle\Controller\TicketListApiController
 */
final class TicketListApiControllerByAddresseeTest extends TestCase
{
    use ProphecyTrait;

    public function testListTicketWithSingleByAddresseeFilter(): void
    {
        // Mock dependencies
        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);

        $user = new User();

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->find(1)->willReturn($user);

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $tickets = [new Ticket(), new Ticket()];
        $ticketRepository->countTickets(
            Argument::that(fn ($params) => isset($params['byAddressee']) && in_array($user, $params['byAddressee']))
        )
            ->shouldBeCalled()
            ->willReturn(2);
        $ticketRepository->findTickets(
            Argument::that(fn ($params) => isset($params['byAddressee']) && in_array($user, $params['byAddressee'])),
            0,
            10
        )->shouldBeCalled()->willReturn($tickets);

        $paginator = $this->prophesize(PaginatorInterface::class);
        $paginator->getCurrentPageFirstItemNumber()->willReturn(0);
        $paginator->getItemsPerPage()->willReturn(10);

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

        $serializer = $this->prophesize(SerializerInterface::class);
        $serializer->serialize(
            Argument::that(fn (Collection $collection) => $collection->getItems() === $tickets),
            'json',
            ['groups' => 'read:simple']
        )->willReturn('{"items":[{},{}],"pagination":{}}');

        $personRepository = $this->prophesize(PersonRepository::class);
        $motiveRepository = $this->prophesize(MotiveRepository::class);

        // Create controller
        $controller = new TicketListApiController(
            $security->reveal(),
            $ticketRepository->reveal(),
            $paginatorFactory->reveal(),
            $serializer->reveal(),
            $personRepository->reveal(),
            $motiveRepository->reveal(),
            new MockClock(),
            new ParameterBag(['chill_ticket' => ['ticket' => ['response_time_exceeded_delay' => 'PT12H']]]),
            $userRepository->reveal(),
            $this->prophesize(UserGroupRepositoryInterface::class)->reveal(),
            $this->prophesize(CenterRepositoryInterface::class)->reveal()
        );

        // Create request with byAddressee filter
        $request = new Request(
            query: ['byAddressee' => '1']
        );

        // Call controller method
        $response = $controller->listTicket($request);

        // Assert response
        $this->assertInstanceOf(JsonResponse::class, $response);
        $this->assertEquals('{"items":[{},{}],"pagination":{}}', $response->getContent());
    }

    public function testListTicketWithMultipleByAddresseeFilter(): void
    {
        // Mock dependencies
        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);

        $user1 = new User();
        $user2 = new User();

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->find(1)->willReturn($user1);
        $userRepository->find(2)->willReturn($user2);

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $tickets = [new Ticket(), new Ticket()];
        $ticketRepository->countTickets(
            Argument::that(fn ($params) => isset($params['byAddressee'])
                && in_array($user1, $params['byAddressee'])
                && in_array($user2, $params['byAddressee']))
        )
            ->shouldBeCalled()
            ->willReturn(2);
        $ticketRepository->findTickets(
            Argument::that(fn ($params) => isset($params['byAddressee'])
                && in_array($user1, $params['byAddressee'])
                && in_array($user2, $params['byAddressee'])),
            0,
            10
        )->shouldBeCalled()->willReturn($tickets);

        $paginator = $this->prophesize(PaginatorInterface::class);
        $paginator->getCurrentPageFirstItemNumber()->willReturn(0);
        $paginator->getItemsPerPage()->willReturn(10);

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

        $serializer = $this->prophesize(SerializerInterface::class);
        $serializer->serialize(
            Argument::that(fn (Collection $collection) => $collection->getItems() === $tickets),
            'json',
            ['groups' => 'read:simple']
        )->willReturn('{"items":[{},{}],"pagination":{}}');

        $personRepository = $this->prophesize(PersonRepository::class);
        $motiveRepository = $this->prophesize(MotiveRepository::class);

        // Create controller
        $controller = new TicketListApiController(
            $security->reveal(),
            $ticketRepository->reveal(),
            $paginatorFactory->reveal(),
            $serializer->reveal(),
            $personRepository->reveal(),
            $motiveRepository->reveal(),
            new MockClock(),
            new ParameterBag(['chill_ticket' => ['ticket' => ['response_time_exceeded_delay' => 'PT12H']]]),
            $userRepository->reveal(),
            $this->prophesize(UserGroupRepositoryInterface::class)->reveal(),
            $this->prophesize(CenterRepositoryInterface::class)->reveal()
        );

        // Create request with multiple byAddressee filter
        $request = new Request(
            query: ['byAddressee' => '1,2']
        );

        // Call controller method
        $response = $controller->listTicket($request);

        // Assert response
        $this->assertInstanceOf(JsonResponse::class, $response);
        $this->assertEquals('{"items":[{},{}],"pagination":{}}', $response->getContent());
    }

    public function testListTicketWithByAddresseeFilterUserNotFound(): void
    {
        self::expectException(BadRequestHttpException::class);
        self::expectExceptionMessage('User not found');

        // Mock dependencies
        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);

        $user1 = new User();

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->find(1)->willReturn($user1);
        $userRepository->find(2)->willReturn(null); // User not found

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $paginatorFactory = $this->prophesize(PaginatorFactoryInterface::class);
        $serializer = $this->prophesize(SerializerInterface::class);
        $personRepository = $this->prophesize(PersonRepository::class);
        $motiveRepository = $this->prophesize(MotiveRepository::class);

        // Create controller
        $controller = new TicketListApiController(
            $security->reveal(),
            $ticketRepository->reveal(),
            $paginatorFactory->reveal(),
            $serializer->reveal(),
            $personRepository->reveal(),
            $motiveRepository->reveal(),
            new MockClock(),
            new ParameterBag(['chill_ticket' => ['ticket' => ['response_time_exceeded_delay' => 'PT12H']]]),
            $userRepository->reveal(),
            $this->prophesize(UserGroupRepositoryInterface::class)->reveal(),
            $this->prophesize(CenterRepositoryInterface::class)->reveal()
        );

        // Create request with byAddressee filter with non-existent user
        $request = new Request(
            query: ['byAddressee' => '1,2']
        );

        // Call controller method - should throw exception
        $controller->listTicket($request);
    }

    public function testListTicketWithByAddresseeAndByAddresseeToMeFilters(): void
    {
        // Mock dependencies
        $currentUser = new User();
        $user1 = new User();

        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);
        $security->getUser()->willReturn($currentUser);

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->find(1)->willReturn($user1);

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $tickets = [new Ticket(), new Ticket()];
        $ticketRepository->countTickets(
            Argument::that(fn ($params) => isset($params['byAddressee'])
                && in_array($currentUser, $params['byAddressee'])
                && in_array($user1, $params['byAddressee']))
        )
            ->shouldBeCalled()
            ->willReturn(2);
        $ticketRepository->findTickets(
            Argument::that(fn ($params) => isset($params['byAddressee'])
                && in_array($currentUser, $params['byAddressee'])
                && in_array($user1, $params['byAddressee'])),
            0,
            10
        )->shouldBeCalled()->willReturn($tickets);

        $paginator = $this->prophesize(PaginatorInterface::class);
        $paginator->getCurrentPageFirstItemNumber()->willReturn(0);
        $paginator->getItemsPerPage()->willReturn(10);

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

        $serializer = $this->prophesize(SerializerInterface::class);
        $serializer->serialize(
            Argument::that(fn (Collection $collection) => $collection->getItems() === $tickets),
            'json',
            ['groups' => 'read:simple']
        )->willReturn('{"items":[{},{}],"pagination":{}}');

        $personRepository = $this->prophesize(PersonRepository::class);
        $motiveRepository = $this->prophesize(MotiveRepository::class);

        // Create controller
        $controller = new TicketListApiController(
            $security->reveal(),
            $ticketRepository->reveal(),
            $paginatorFactory->reveal(),
            $serializer->reveal(),
            $personRepository->reveal(),
            $motiveRepository->reveal(),
            new MockClock(),
            new ParameterBag(['chill_ticket' => ['ticket' => ['response_time_exceeded_delay' => 'PT12H']]]),
            $userRepository->reveal(),
            $this->prophesize(UserGroupRepositoryInterface::class)->reveal(),
            $this->prophesize(CenterRepositoryInterface::class)->reveal()
        );

        // Create request with both byAddressee and byAddresseeToMe filters
        $request = new Request(
            query: [
                'byAddressee' => '1',
                'byAddresseeToMe' => '',
            ]
        );

        // Call controller method
        $response = $controller->listTicket($request);

        // Assert response
        $this->assertInstanceOf(JsonResponse::class, $response);
        $this->assertEquals('{"items":[{},{}],"pagination":{}}', $response->getContent());
    }
}
