<?php

namespace App\Tests\Controller;

use App\Controller\EmailTrackingController;
use App\Entity\EmailTracking;
use App\Repository\EmailTrackingRepository;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
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\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class EmailTrackingControllerTest extends TestCase
{
    private string $projectDir;

    protected function setUp(): void
    {
        $this->projectDir = sys_get_temp_dir().'/email_tracking_test_'.uniqid();
        mkdir($this->projectDir.'/public', 0775, true);
        file_put_contents($this->projectDir.'/public/logo.jpg', 'fake-jpg');
    }

    protected function tearDown(): void
    {
        @unlink($this->projectDir.'/public/logo.jpg');
        @rmdir($this->projectDir.'/public');
        @rmdir($this->projectDir);
    }

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

        $router = $this->createStub(RouterInterface::class);
        $router->method('generate')->willReturn('/email/msg123/attachment/0');

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturn(true);
        $container->method('get')->willReturnMap([
            ['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)],
        ]);
        $controller->setContainer($container);
    }

    public function testTrackWithExistingTracking(): void
    {
        $tracking = new EmailTracking('msg-123', 'test@test.com', 'Subject');

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $em = $this->createStub(EntityManagerInterface::class);

        $controller = new EmailTrackingController();
        $response = $controller->track('msg-123', $repo, $em, $this->projectDir);

        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('opened', $tracking->getState());
    }

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

        $em = $this->createStub(EntityManagerInterface::class);

        $controller = new EmailTrackingController();
        $response = $controller->track('msg-unknown', $repo, $em, $this->projectDir);

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

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

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $this->expectException(NotFoundHttpException::class);
        $controller->view('msg-unknown', $repo);
    }

    public function testViewNoHtmlBody(): void
    {
        $tracking = new EmailTracking('msg-123', 'test@test.com', 'Subject');

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $this->expectException(NotFoundHttpException::class);
        $controller->view('msg-123', $repo);
    }

    public function testViewWithHtmlBody(): void
    {
        $tracking = new EmailTracking('msg-123', 'test@test.com', 'Subject', '<html><body>Hello</body></html>');

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $response = $controller->view('msg-123', $repo);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('Hello', $response->getContent());
    }

    public function testViewWithAttachments(): void
    {
        $tmpFile = tempnam(sys_get_temp_dir(), 'att_');
        file_put_contents($tmpFile, 'test-content');

        $tracking = new EmailTracking(
            'msg-456',
            'test@test.com',
            'Subject',
            '<html><body>With attachment</body></html>',
            [['path' => $tmpFile, 'name' => 'document.pdf']],
        );

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $response = $controller->view('msg-456', $repo);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('Pieces jointes', $response->getContent());
        $this->assertStringContainsString('document.pdf', $response->getContent());

        @unlink($tmpFile);
    }

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

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $this->expectException(NotFoundHttpException::class);
        $controller->attachment('msg-unknown', 0, $repo);
    }

    public function testAttachmentIndexNotFound(): void
    {
        $tracking = new EmailTracking('msg-123', 'test@test.com', 'Subject', '<html></html>');

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $this->expectException(NotFoundHttpException::class);
        $controller->attachment('msg-123', 0, $repo);
    }

    public function testAttachmentFileNotExists(): void
    {
        $tracking = new EmailTracking(
            'msg-789',
            'test@test.com',
            'Subject',
            '<html></html>',
            [['path' => '/nonexistent/file.pdf', 'name' => 'file.pdf']],
        );

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $this->expectException(NotFoundHttpException::class);
        $controller->attachment('msg-789', 0, $repo);
    }

    public function testAttachmentSuccess(): void
    {
        $tmpFile = tempnam(sys_get_temp_dir(), 'att_dl_');
        file_put_contents($tmpFile, 'pdf-content');

        $tracking = new EmailTracking(
            'msg-dl',
            'test@test.com',
            'Subject',
            '<html></html>',
            [['path' => $tmpFile, 'name' => 'rapport.pdf']],
        );

        $repo = $this->createStub(EmailTrackingRepository::class);
        $repo->method('findOneBy')->willReturn($tracking);

        $controller = new EmailTrackingController();
        $this->setupController($controller);

        $response = $controller->attachment('msg-dl', 0, $repo);
        $this->assertSame(200, $response->getStatusCode());

        @unlink($tmpFile);
    }
}
