<?php

namespace App\Tests\Controller;

use App\Controller\CspReportController;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
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\Mailer\MailerInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class CspReportControllerTest extends TestCase
{
    private function setupController(CspReportController $controller): void
    {
        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $paramBag = $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class);
        $paramBag->method('get')->willReturn('admin@e-cosplay.fr');
        $paramBag->method('has')->willReturn(true);

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturn(true);
        $container->method('get')->willReturnMap([
            ['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $paramBag],
        ]);
        $controller->setContainer($container);
    }

    public function testGetReturns204(): void
    {
        $controller = new CspReportController();
        $response = $controller->get();
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportEmptyPayload(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $request = new Request([], [], [], [], [], [], '');
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(400, $response->getStatusCode());
    }

    public function testReportInvalidJson(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $request = new Request([], [], [], [], [], [], '{invalid');
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(400, $response->getStatusCode());
    }

    public function testReportIgnoredExtension(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'chrome-extension://abc123',
                'blocked-uri' => 'inline',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredMozExtension(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'moz-extension://abc',
                'blocked-uri' => '',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredLocalhost(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'http://localhost:3000/app.js',
                'blocked-uri' => 'eval',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredLocalDomain(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => '/app.js',
                'blocked-uri' => 'inline',
                'document-uri' => 'http://crm.local/dashboard',
                'violated-directive' => 'style-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredWasmEval(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'https://crm.e-cosplay.fr/app.js',
                'blocked-uri' => 'wasm-eval',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredAboutBlank(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => '',
                'blocked-uri' => 'about:blank',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'frame-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportRealViolationSendsEmail(): void
    {
        $controller = new CspReportController();
        $this->setupController($controller);
        $logger = $this->createStub(LoggerInterface::class);
        $mailer = $this->createStub(MailerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'https://evil.com/inject.js',
                'blocked-uri' => 'https://evil.com',
                'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportRealViolationEmailFailure(): void
    {
        $controller = new CspReportController();
        $this->setupController($controller);
        $logger = $this->createStub(LoggerInterface::class);

        $mailer = $this->createStub(MailerInterface::class);
        $mailer->method('send')->willThrowException(new \Exception('SMTP error'));

        $payload = json_encode([
            'csp-report' => [
                'source-file' => 'https://evil.com/inject.js',
                'blocked-uri' => 'https://evil.com',
                'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportWithoutCspReportWrapper(): void
    {
        $controller = new CspReportController();
        $this->setupController($controller);
        $logger = $this->createStub(LoggerInterface::class);
        $mailer = $this->createStub(MailerInterface::class);

        $payload = json_encode([
            'source-file' => 'https://evil.com/inject.js',
            'blocked-uri' => 'https://evil.com',
            'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
            'violated-directive' => 'script-src',
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    public function testReportIgnoredNodeModulesInline(): void
    {
        $controller = new CspReportController();
        $logger = $this->createStub(LoggerInterface::class);

        $payload = json_encode([
            'csp-report' => [
                'source-file' => '/home/user/node_modules/some-lib/index.js',
                'blocked-uri' => 'inline',
                'document-uri' => 'https://crm.e-cosplay.fr',
                'violated-directive' => 'script-src',
            ],
        ]);
        $request = new Request([], [], [], [], [], [], $payload);
        $response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
        $this->assertSame(204, $response->getStatusCode());
    }
}
