<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Domain\Model\Session;

use App\Administration\Domain\Model\RefreshToken\TokenFamilyId;
use App\Administration\Domain\Model\Session\DeviceInfo;
use App\Administration\Domain\Model\Session\Location;
use App\Administration\Domain\Model\Session\Session;
use App\Administration\Domain\Model\User\UserId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SessionTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';

    #[Test]
    public function createGeneratesSessionWithCorrectData(): void
    {
        $familyId = TokenFamilyId::generate();
        $userId = UserId::generate();
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $deviceInfo = DeviceInfo::fromUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0');
        $location = Location::fromIp('192.168.1.1', 'France', 'Paris');
        $createdAt = new DateTimeImmutable('2026-01-31 10:00:00');

        $session = Session::create(
            $familyId,
            $userId,
            $tenantId,
            $deviceInfo,
            $location,
            $createdAt,
        );

        self::assertTrue($session->familyId->equals($familyId));
        self::assertTrue($session->userId->equals($userId));
        self::assertTrue($session->tenantId->equals($tenantId));
        self::assertSame($deviceInfo, $session->deviceInfo);
        self::assertSame($location, $session->location);
        self::assertEquals($createdAt, $session->createdAt);
        self::assertEquals($createdAt, $session->lastActivityAt);
    }

    #[Test]
    public function updateActivityUpdatesLastActivityTimestamp(): void
    {
        $session = $this->createSession();
        $newActivityAt = new DateTimeImmutable('2026-01-31 14:30:00');

        $updatedSession = $session->updateActivity($newActivityAt);

        self::assertEquals($newActivityAt, $updatedSession->lastActivityAt);
        self::assertEquals($session->createdAt, $updatedSession->createdAt);
        self::assertTrue($session->familyId->equals($updatedSession->familyId));
    }

    #[Test]
    public function isCurrentReturnsTrueForMatchingFamilyId(): void
    {
        $familyId = TokenFamilyId::generate();
        $session = Session::create(
            $familyId,
            UserId::generate(),
            TenantId::fromString(self::TENANT_ID),
            DeviceInfo::fromUserAgent('Mozilla/5.0'),
            Location::unknown(),
            new DateTimeImmutable(),
        );

        self::assertTrue($session->isCurrent($familyId));
        self::assertFalse($session->isCurrent(TokenFamilyId::generate()));
    }

    #[Test]
    public function belongsToUserReturnsTrueForMatchingUserId(): void
    {
        $userId = UserId::generate();
        $session = Session::create(
            TokenFamilyId::generate(),
            $userId,
            TenantId::fromString(self::TENANT_ID),
            DeviceInfo::fromUserAgent('Mozilla/5.0'),
            Location::unknown(),
            new DateTimeImmutable(),
        );

        self::assertTrue($session->belongsToUser($userId));
        self::assertFalse($session->belongsToUser(UserId::generate()));
    }

    #[Test]
    public function reconstituteRestoresSessionFromStorage(): void
    {
        $familyId = TokenFamilyId::generate();
        $userId = UserId::generate();
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $deviceInfo = DeviceInfo::fromUserAgent('Mozilla/5.0');
        $location = Location::fromIp('10.0.0.1', 'Germany', 'Berlin');
        $createdAt = new DateTimeImmutable('2026-01-20 08:00:00');
        $lastActivityAt = new DateTimeImmutable('2026-01-31 16:00:00');

        $session = Session::reconstitute(
            $familyId,
            $userId,
            $tenantId,
            $deviceInfo,
            $location,
            $createdAt,
            $lastActivityAt,
        );

        self::assertTrue($session->familyId->equals($familyId));
        self::assertEquals($createdAt, $session->createdAt);
        self::assertEquals($lastActivityAt, $session->lastActivityAt);
    }

    private function createSession(): Session
    {
        return Session::create(
            TokenFamilyId::generate(),
            UserId::generate(),
            TenantId::fromString(self::TENANT_ID),
            DeviceInfo::fromUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0'),
            Location::fromIp('192.168.1.1', 'France', 'Paris'),
            new DateTimeImmutable('2026-01-31 10:00:00'),
        );
    }
}
