<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\LogsController;
use App\Entity\AppLog;
use App\Entity\User;
use App\Repository\AppLogRepository;
use App\Service\AppLoggerService;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\PaginatorInterface;
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\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
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;

class LogsControllerTest extends TestCase
{
    private function createContainer(?User $user = null): 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/logs');

        $tokenStorage = $this->createStub(TokenStorageInterface::class);
        if (null !== $user) {
            $token = $this->createStub(TokenInterface::class);
            $token->method('getUser')->willReturn($user);
            $tokenStorage->method('getToken')->willReturn($token);
        }

        $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', $tokenStorage],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);

        return $container;
    }

    private function createLog(): AppLog
    {
        return new AppLog('GET', '/admin', 'app_admin', 'Consultation', 'secret', null, '127.0.0.1');
    }

    public function testIndex(): void
    {
        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('createPaginatedQueryBuilder')->willReturn($this->createStub(QueryBuilder::class));

        $pagination = new SlidingPagination([]);

        $paginator = $this->createStub(PaginatorInterface::class);
        $paginator->method('paginate')->willReturn($pagination);

        $loggerService = $this->createStub(AppLoggerService::class);

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

        $response = $controller->index(new Request(), $repo, $paginator, $loggerService);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testIndexWithLogs(): void
    {
        $log = $this->createLog();
        $ref = new \ReflectionProperty($log, 'id');
        $ref->setValue($log, 1);

        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('createPaginatedQueryBuilder')->willReturn($this->createStub(QueryBuilder::class));

        $pagination = new SlidingPagination([$log]);

        $paginator = $this->createStub(PaginatorInterface::class);
        $paginator->method('paginate')->willReturn($pagination);

        $loggerService = $this->createStub(AppLoggerService::class);
        $loggerService->method('verifyLog')->willReturn(true);

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

        $response = $controller->index(new Request(['page' => '2']), $repo, $paginator, $loggerService);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testPurge(): void
    {
        $countQuery = $this->createStub(Query::class);
        $countQuery->method('getSingleScalarResult')->willReturn(5);

        $deleteQuery = $this->createStub(Query::class);
        $deleteQuery->method('execute')->willReturn(5);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('delete')->willReturnSelf();
        $qb->method('getQuery')->willReturnOnConsecutiveCalls($countQuery, $deleteQuery);

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

        $loggerService = $this->createStub(AppLoggerService::class);

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

        $response = $controller->purge($em, $loggerService);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testPurgeWithUser(): void
    {
        $user = new User();
        $user->setEmail('admin@test.com');
        $user->setFirstName('A');
        $user->setLastName('B');
        $user->setPassword('h');

        $countQuery = $this->createStub(Query::class);
        $countQuery->method('getSingleScalarResult')->willReturn(0);

        $deleteQuery = $this->createStub(Query::class);
        $deleteQuery->method('execute')->willReturn(0);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('delete')->willReturnSelf();
        $qb->method('getQuery')->willReturnOnConsecutiveCalls($countQuery, $deleteQuery);

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

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

        $response = $controller->purge($em, $this->createStub(AppLoggerService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testDeleteNotFound(): void
    {
        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn(null);

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

        $this->expectException(NotFoundHttpException::class);
        $controller->delete(999, $repo, $this->createStub(EntityManagerInterface::class), $this->createStub(AppLoggerService::class));
    }

    public function testDeleteSuccess(): void
    {
        $log = $this->createLog();
        $ref = new \ReflectionProperty($log, 'id');
        $ref->setValue($log, 42);

        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn($log);

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

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

    public function testDeleteWithUser(): void
    {
        $user = new User();
        $user->setEmail('a@t.com');
        $user->setFirstName('A');
        $user->setLastName('B');
        $user->setPassword('h');

        $log = $this->createLog();
        $ref = new \ReflectionProperty($log, 'id');
        $ref->setValue($log, 10);

        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn($log);

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

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

    public function testPdfNotFound(): void
    {
        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn(null);

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

        $this->expectException(NotFoundHttpException::class);
        $controller->pdf(999, $repo, $this->createStub(AppLoggerService::class), $this->createStub(Environment::class), '/tmp');
    }

    public function testPdfSuccess(): void
    {
        $log = $this->createLog();
        $ref = new \ReflectionProperty($log, 'id');
        $ref->setValue($log, 5);

        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn($log);

        $loggerService = $this->createStub(AppLoggerService::class);
        $loggerService->method('verifyLog')->willReturn(true);

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

        $tmpDir = sys_get_temp_dir().'/logs_test_'.uniqid();
        mkdir($tmpDir.'/public', 0775, true);
        file_put_contents($tmpDir.'/public/logo_facture.png', 'fake');

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

        $response = $controller->pdf(5, $repo, $loggerService, $twig, $tmpDir);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));

        @unlink($tmpDir.'/public/logo_facture.png');
        @rmdir($tmpDir.'/public');
        @rmdir($tmpDir);
    }

    public function testPdfNoLogo(): void
    {
        $log = $this->createLog();
        $ref = new \ReflectionProperty($log, 'id');
        $ref->setValue($log, 6);

        $repo = $this->createStub(AppLogRepository::class);
        $repo->method('find')->willReturn($log);

        $loggerService = $this->createStub(AppLoggerService::class);
        $loggerService->method('verifyLog')->willReturn(false);

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

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

        $response = $controller->pdf(6, $repo, $loggerService, $twig, '/nonexistent');
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }
}
