<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\OrderNumberController;
use App\Entity\OrderNumber;
use App\Repository\OrderNumberRepository;
use App\Service\OrderNumberService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Twig\Environment;

class OrderNumberControllerTest extends TestCase
{
    private function createContainer(): ContainerInterface
    {
        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $twig = $this->createStub(Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $router = $this->createStub(RouterInterface::class);
        $router->method('generate')->willReturn('/admin/numerotation');

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturn(true);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $router],
            ['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);

        return $container;
    }

    private function createRepoWithQueryBuilder(): OrderNumberRepository
    {
        $query = $this->createStub(Query::class);
        $query->method('getResult')->willReturn([]);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('orderBy')->willReturnSelf();
        $qb->method('setMaxResults')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);

        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('createQueryBuilder')->willReturn($qb);

        return $repo;
    }

    public function testIndex(): void
    {
        $orderService = $this->createStub(OrderNumberService::class);
        $orderService->method('preview')->willReturn('04/2026-00001');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($orderService, $this->createRepoWithQueryBuilder());
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testUpdateInvalidFormat(): void
    {
        $request = new Request([], ['next_number' => 'invalid']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $this->createStub(OrderNumberRepository::class), $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateEmptyNumber(): void
    {
        $request = new Request([], ['next_number' => '']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $this->createStub(OrderNumberRepository::class), $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateNumberAlreadyExists(): void
    {
        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('findOneBy')->willReturn(new OrderNumber('04/2026-00042'));

        $request = new Request([], ['next_number' => '04/2026-00042']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $repo, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateNumberTooLow(): void
    {
        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('findOneBy')->willReturn(null);

        $request = new Request([], ['next_number' => '04/2026-00000']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $repo, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateSuccess(): void
    {
        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('findOneBy')->willReturn(null);

        $request = new Request([], ['next_number' => '04/2026-00042']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $repo, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateSuccessFirstNumber(): void
    {
        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('findOneBy')->willReturn(null);

        $request = new Request([], ['next_number' => '04/2026-00001']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $repo, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdatePreviousAlreadyExists(): void
    {
        $callCount = 0;
        $repo = $this->createStub(OrderNumberRepository::class);
        $repo->method('findOneBy')->willReturnCallback(function (array $criteria) use (&$callCount) {
            ++$callCount;
            if (1 === $callCount) {
                return null; // newNumber not found
            }

            return new OrderNumber('04/2026-00041'); // previousNumOrder exists
        });

        $request = new Request([], ['next_number' => '04/2026-00042']);
        $request->setMethod('POST');

        $controller = new OrderNumberController();
        $controller->setContainer($this->createContainer());

        $response = $controller->update($request, $repo, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }
}
