<?php

namespace App\Tests\Controller;

use App\Controller\AnalyticsController;
use App\Service\AnalyticsCryptoService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;

class AnalyticsControllerTest extends TestCase
{
    private AnalyticsCryptoService $crypto;
    private string $analyticsSecret = 'test-secret-analytics';
    private string $validToken;

    protected function setUp(): void
    {
        $this->crypto = new AnalyticsCryptoService($this->analyticsSecret);
        $this->validToken = substr(hash('sha256', $this->analyticsSecret.'_endpoint'), 0, 8);
    }

    private function createBus(): MessageBusInterface
    {
        $bus = $this->createStub(MessageBusInterface::class);
        $bus->method('dispatch')->willReturnCallback(fn($msg) => new Envelope($msg));
        return $bus;
    }

    public function testTrackInvalidToken(): void
    {
        $controller = new AnalyticsController();
        $request = new Request([], [], [], [], [], [], '{}');

        $response = $controller->track(
            'badtoken',
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackEmptyPayload(): void
    {
        $controller = new AnalyticsController();
        $request = new Request([], [], [], [], [], [], '{}');

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackInvalidEncryptedData(): void
    {
        $controller = new AnalyticsController();
        $request = new Request([], [], [], [], [], [], json_encode(['d' => 'invalid-base64-data']));

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackNewVisitorCreation(): void
    {
        $controller = new AnalyticsController();

        $encrypted = $this->crypto->encrypt(['sw' => 1920, 'sh' => 1080, 'l' => 'fr']);
        $request = new Request([], [], [], [], [], ['HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'], json_encode(['d' => $encrypted]));

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

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $em,
            $this->createBus(),
        );

        $this->assertSame(200, $response->getStatusCode());
        $body = json_decode($response->getContent(), true);
        $this->assertArrayHasKey('d', $body);

        $decrypted = $this->crypto->decrypt($body['d']);
        $this->assertNotNull($decrypted);
        $this->assertArrayHasKey('uid', $decrypted);
        $this->assertArrayHasKey('h', $decrypted);
    }

    public function testTrackPageViewWithValidHash(): void
    {
        $controller = new AnalyticsController();
        $uid = 'test-uid-1234';
        $hash = $this->crypto->generateVisitorHash($uid);

        $encrypted = $this->crypto->encrypt([
            'uid' => $uid,
            'h' => $hash,
            'u' => '/test-page',
            't' => 'Test Page',
            'r' => 'https://google.com',
        ]);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => $encrypted]));

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackSetUserWithValidHash(): void
    {
        $controller = new AnalyticsController();
        $uid = 'test-uid-5678';
        $hash = $this->crypto->generateVisitorHash($uid);

        $encrypted = $this->crypto->encrypt([
            'uid' => $uid,
            'h' => $hash,
            'setUser' => 42,
        ]);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => $encrypted]));

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackWithInvalidHash(): void
    {
        $controller = new AnalyticsController();

        $encrypted = $this->crypto->encrypt([
            'uid' => 'test-uid',
            'h' => 'wrong-hash',
        ]);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => $encrypted]));

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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

    public function testTrackWithMissingHash(): void
    {
        $controller = new AnalyticsController();

        $encrypted = $this->crypto->encrypt([
            'uid' => 'test-uid',
        ]);
        $request = new Request([], [], [], [], [], [], json_encode(['d' => $encrypted]));

        $response = $controller->track(
            $this->validToken,
            $this->analyticsSecret,
            $request,
            $this->crypto,
            $this->createStub(EntityManagerInterface::class),
            $this->createBus(),
        );

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