<?php

namespace App\Tests\MessageHandler;

use App\Entity\AnalyticsEvent;
use App\Entity\AnalyticsUniqId;
use App\Entity\User;
use App\Message\AnalyticsMessage;
use App\MessageHandler\AnalyticsMessageHandler;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;

class AnalyticsMessageHandlerTest extends TestCase
{
    private EntityManagerInterface $em;
    private AnalyticsMessageHandler $handler;

    protected function setUp(): void
    {
        $this->em = $this->createMock(EntityManagerInterface::class);
        $this->handler = new AnalyticsMessageHandler($this->em);
    }

    public function testInvokeVisitorNotFound(): void
    {
        $repository = $this->createStub(EntityRepository::class);
        $repository->method('findOneBy')->willReturn(null);

        $this->em->method('getRepository')->willReturn($repository);
        $this->em->expects($this->never())->method('flush');

        $message = new AnalyticsMessage('uid', 'action');
        ($this->handler)($message);
    }

    public function testInvokeSetUserSuccess(): void
    {
        $visitor = new AnalyticsUniqId();
        $user = new User();

        $visitorRepo = $this->createStub(EntityRepository::class);
        $visitorRepo->method('findOneBy')->willReturn($visitor);

        $userRepo = $this->createStub(EntityRepository::class);
        $userRepo->method('find')->willReturn($user);

        $this->em->method('getRepository')->willReturnMap([
            [AnalyticsUniqId::class, $visitorRepo],
            [User::class, $userRepo],
        ]);

        $this->em->expects($this->once())->method('flush');

        $message = new AnalyticsMessage('uid', 'set_user', ['userId' => 42]);
        ($this->handler)($message);

        $this->assertSame($user, $visitor->getUser());
    }

    public function testInvokeSetUserNoUserId(): void
    {
        $visitor = new AnalyticsUniqId();
        $visitorRepo = $this->createStub(EntityRepository::class);
        $visitorRepo->method('findOneBy')->willReturn($visitor);

        $this->em->method('getRepository')->willReturn($visitorRepo);
        $this->em->expects($this->never())->method('flush');

        $message = new AnalyticsMessage('uid', 'set_user', []);
        ($this->handler)($message);
    }

    public function testInvokeSetUserNotFound(): void
    {
        $visitor = new AnalyticsUniqId();
        $visitorRepo = $this->createStub(EntityRepository::class);
        $visitorRepo->method('findOneBy')->willReturn($visitor);

        $userRepo = $this->createStub(EntityRepository::class);
        $userRepo->method('find')->willReturn(null);

        $this->em->method('getRepository')->willReturnMap([
            [AnalyticsUniqId::class, $visitorRepo],
            [User::class, $userRepo],
        ]);

        $this->em->expects($this->never())->method('flush');

        $message = new AnalyticsMessage('uid', 'set_user', ['userId' => 999]);
        ($this->handler)($message);
    }

    public function testInvokePageView(): void
    {
        $visitor = new AnalyticsUniqId();
        $visitorRepo = $this->createStub(EntityRepository::class);
        $visitorRepo->method('findOneBy')->willReturn($visitor);

        $this->em->method('getRepository')->willReturn($visitorRepo);
        
        $this->em->expects($this->once())->method('persist')->with($this->isInstanceOf(AnalyticsEvent::class));
        $this->em->expects($this->once())->method('flush');

        $payload = [
            'url' => '/test',
            'title' => 'Test Title',
            'referrer' => 'http://google.com'
        ];
        $message = new AnalyticsMessage('uid', 'page_view', $payload);
        ($this->handler)($message);
    }

    public function testInvokePageViewEmptyPayload(): void
    {
        $visitor = new AnalyticsUniqId();
        $visitorRepo = $this->createStub(EntityRepository::class);
        $visitorRepo->method('findOneBy')->willReturn($visitor);

        $this->em->method('getRepository')->willReturn($visitorRepo);
        $this->em->expects($this->once())->method('persist');
        $this->em->expects($this->once())->method('flush');

        $message = new AnalyticsMessage('uid', 'page_view');
        ($this->handler)($message);
    }
}
