<?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\Pagination\PaginatorFactoryInterface;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\UserGroupRepositoryInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\TicketBundle\Controller\TicketListApiController;
use Chill\TicketBundle\Entity\Motive;
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 TicketListApiControllerMotivesTest extends TestCase
{
    use ProphecyTrait;

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

        $motive1 = new Motive();
        $motive2 = new Motive();

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $tickets = [new Ticket(), new Ticket()];
        $ticketRepository->countTickets(
            Argument::that(fn ($params) => isset($params['byMotives']) && in_array($motive1, $params['byMotives']) && in_array($motive2, $params['byMotives']))
        )
            ->willReturn(2);
        $ticketRepository->findTickets(
            Argument::that(fn ($params) => isset($params['byMotives']) && in_array($motive1, $params['byMotives']) && in_array($motive2, $params['byMotives'])),
            0,
            10
        )->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);
        $motiveRepository->find(1)->willReturn($motive1);
        $motiveRepository->find(2)->willReturn($motive2);
        $userRepository = $this->prophesize(UserRepositoryInterface::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 motives filter
        $request = new Request(
            query: ['byMotives' => '1,2']
        );

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

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

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

        $motive = new Motive();

        $ticketRepository = $this->prophesize(TicketACLAwareRepositoryInterface::class);
        $tickets = [new Ticket(), new Ticket()];
        $ticketRepository->countTickets(
            Argument::that(fn ($params) => isset($params['byMotives']) && in_array($motive, $params['byMotives']))
        )
            ->willReturn(2);
        $ticketRepository->findTickets(
            Argument::that(fn ($params) => isset($params['byMotives']) && in_array($motive, $params['byMotives'])),
            0,
            10
        )->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);
        $motiveRepository->find(1)->willReturn($motive);
        $userRepository = $this->prophesize(UserRepositoryInterface::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 single motive filter
        $request = new Request(
            query: ['byMotives' => '1']
        );

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

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

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

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

        $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);
        $motiveRepository->find(999)->willReturn(null);
        $userRepository = $this->prophesize(UserRepositoryInterface::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 non-existent motive
        $request = new Request(
            query: ['byMotives' => '999']
        );

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

    public function testListTicketWithNonIntegerMotiveParameter(): void
    {
        self::expectException(BadRequestHttpException::class);
        self::expectExceptionMessage('Only numbers are allowed in by motives parameter');

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

        $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);
        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $centerRepository = $this->prophesize(CenterRepositoryInterface::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(),
            $centerRepository->reveal(),
        );

        // Create request with non-integer motive parameter
        $request = new Request(
            query: ['byMotives' => 'abc']
        );

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