<?php

namespace App\Tests\Controller;

use App\Controller\AnalyticsController;
use App\Controller\CspReportController;
use App\Controller\EmailTrackingController;
use App\Controller\ExternalRedirectController;
use App\Controller\SonarBadgeController;
use App\Entity\EmailTracking;
use App\Repository\EmailTrackingRepository;
use App\Service\AnalyticsCryptoService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface as HttpClientResponseInterface;
use Twig\Environment;

class SmallControllersTest extends TestCase
{
    private string $tempDir;

    protected function setUp(): void
    {
        $this->tempDir = sys_get_temp_dir() . '/small_ctrl_test_' . uniqid();
        mkdir($this->tempDir);
        mkdir($this->tempDir . '/public');
        touch($this->tempDir . '/public/logo_facture.png');
    }

    protected function tearDown(): void
    {
        $this->removeDir($this->tempDir);
    }

    private function removeDir(string $dir): void
    {
        if (!is_dir($dir)) return;
        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            is_dir($path) ? $this->removeDir($path) : unlink($path);
        }
        rmdir($dir);
    }

    private function createContainer(array $services = []): ContainerInterface
    {
        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturnCallback(fn($id) => isset($services[$id]) || $id === 'parameter_bag' || $id === 'twig' || $id === 'router');
        $container->method('get')->willReturnCallback(fn($id) => $services[$id] ?? null);
        return $container;
    }

    // --- SonarBadgeController ---

    public function testSonarBadgeSuccess(): void
    {
        $client = $this->createMock(HttpClientInterface::class);
        $resp = $this->createStub(HttpClientResponseInterface::class);
        $resp->method('getContent')->willReturn('<svg>badge</svg>');
        $client->expects($this->once())->method('request')->willReturn($resp);

        $controller = new SonarBadgeController();
        $response = $controller->badge('coverage', $client, 'http://sonar', 'project', 'token');

        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('<svg>badge</svg>', $response->getContent());
    }

    // --- CspReportController ---

    public function testCspReportInvalidJson(): void
    {
        $request = new Request([], [], [], [], [], [], 'invalid');
        $mailer = $this->createStub(MailerInterface::class);
        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->once())->method('error');

        $controller = new CspReportController();
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(400, $response->getStatusCode());
    }

    public function testCspReportIgnoredViolation(): void
    {
        $payload = json_encode(['csp-report' => ['source-file' => 'chrome-extension://abc']]);
        $request = new Request([], [], [], [], [], [], $payload);
        $mailer = $this->createStub(MailerInterface::class);
        $logger = $this->createStub(LoggerInterface::class);

        $controller = new CspReportController();
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(204, $response->getStatusCode());
        $this->assertSame('Report ignored', $response->getContent());
    }

    public function testCspReportSuccess(): void
    {
        $payload = json_encode(['csp-report' => [
            'source-file' => 'http://evil.com/script.js',
            'document-uri' => 'http://site.fr',
            'violated-directive' => 'script-src',
            'blocked-uri' => 'http://evil.com'
        ]]);
        $request = new Request([], [], [], [], [], [], $payload);
        $mailer = $this->createMock(MailerInterface::class);
        $mailer->expects($this->once())->method('send');
        $logger = $this->createStub(LoggerInterface::class);

        $bag = $this->createStub(ParameterBagInterface::class);
        $bag->method('get')->willReturn('admin@test.com');
        
        $container = $this->createContainer(['parameter_bag' => $bag]);

        $controller = new CspReportController();
        $controller->setContainer($container);
        $response = $controller->report($request, $mailer, $logger);
        $this->assertSame(204, $response->getStatusCode());
    }

    // --- EmailTrackingController ---

    public function testEmailViewSuccess(): void
    {
        $repo = $this->createStub(EmailTrackingRepository::class);
        $tracking = new EmailTracking('id', 'r', 's', '<html><body></body></html>', [['name' => 'att.pdf', 'path' => '/tmp/att.pdf']]);
        $repo->method('findOneBy')->willReturn($tracking);

        $router = $this->createStub(\Symfony\Component\Routing\RouterInterface::class);
        $router->method('generate')->willReturn('/download');
        
        $container = $this->createContainer(['router' => $router]);
        $controller = new EmailTrackingController();
        $controller->setContainer($container);

        $response = $controller->view('id', $repo);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('/download', $response->getContent());
    }

    public function testEmailAttachmentSuccess(): void
    {
        $filePath = $this->tempDir . '/test.pdf';
        file_put_contents($filePath, 'PDF CONTENT');

        $repo = $this->createStub(EmailTrackingRepository::class);
        $tracking = new EmailTracking('id', 'r', 's', null, [['name' => 'test.pdf', 'path' => $filePath]]);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $response = $controller->attachment('id', 0, $repo);
        $this->assertSame(200, $response->getStatusCode());
    }

    // --- AnalyticsController ---

    public function testAnalyticsTrackInvalidToken(): void
    {
        $crypto = $this->createStub(AnalyticsCryptoService::class);
        $em = $this->createStub(EntityManagerInterface::class);
        $bus = $this->createStub(MessageBusInterface::class);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => 'data']));

        $controller = new AnalyticsController();
        $response = $controller->track('invalid', 'secret', $request, $crypto, $em, $bus);
        $this->assertSame(404, $response->getStatusCode());
    }

    public function testAnalyticsTrackInvalidHash(): void
    {
        $secret = 'secret';
        $token = substr(hash('sha256', $secret.'_endpoint'), 0, 8);
        
        $crypto = $this->createStub(AnalyticsCryptoService::class);
        $crypto->method('decrypt')->willReturn(['uid' => 'uid-123', 'h' => 'bad-hash']);
        $crypto->method('verifyVisitorHash')->willReturn(false);

        $em = $this->createStub(EntityManagerInterface::class);
        $bus = $this->createStub(MessageBusInterface::class);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => 'data']));

        $controller = new AnalyticsController();
        $response = $controller->track($token, $secret, $request, $crypto, $em, $bus);

        $this->assertSame(403, $response->getStatusCode());
    }

    public function testAnalyticsTrackPageView(): void
    {
        $secret = 'secret';
        $token = substr(hash('sha256', $secret.'_endpoint'), 0, 8);
        
        $crypto = $this->createStub(AnalyticsCryptoService::class);
        $crypto->method('decrypt')->willReturn(['uid' => 'uid-123', 'h' => 'hash', 'u' => '/home']);
        $crypto->method('verifyVisitorHash')->willReturn(true);

        $em = $this->createStub(EntityManagerInterface::class);
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects($this->once())->method('dispatch')->willReturn(new Envelope(new \stdClass()));
        
        $request = new Request([], [], [], [], [], [], json_encode(['d' => 'data']));

        $controller = new AnalyticsController();
        $response = $controller->track($token, $secret, $request, $crypto, $em, $bus);

        $this->assertSame(204, $response->getStatusCode());
    }
}
