<?php

declare(strict_types=1);

namespace App\Administration\Application\Command\ActivateAccount;

use App\Administration\Application\Port\PasswordHasher;
use App\Administration\Domain\Exception\ActivationTokenNotFoundException;
use App\Administration\Domain\Repository\ActivationTokenRepository;
use App\Shared\Domain\Clock;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'command.bus')]
final readonly class ActivateAccountHandler
{
    public function __construct(
        private ActivationTokenRepository $tokenRepository,
        private PasswordHasher $passwordHasher,
        private Clock $clock,
    ) {
    }

    /**
     * @throws ActivationTokenNotFoundException                                         if token does not exist
     * @throws \App\Administration\Domain\Exception\ActivationTokenExpiredException     if token is expired
     * @throws \App\Administration\Domain\Exception\ActivationTokenAlreadyUsedException if token was already used
     */
    public function __invoke(ActivateAccountCommand $command): ActivateAccountResult
    {
        $token = $this->tokenRepository->findByTokenValue($command->tokenValue);

        if ($token === null) {
            throw ActivationTokenNotFoundException::withTokenValue($command->tokenValue);
        }

        $now = $this->clock->now();

        // Validate token can be used (throws if expired or already used)
        // Note: Token is NOT marked as used here - that's deferred to the processor
        // after successful user activation, so failed activations don't burn the token
        $token->validateForUse($now);

        // Hash the password for User model
        $hashedPassword = $this->passwordHasher->hash($command->password);

        return new ActivateAccountResult(
            userId: $token->userId,
            email: $token->email,
            tenantId: $token->tenantId,
            role: $token->role,
            hashedPassword: $hashedPassword,
            studentId: $token->studentId,
            relationshipType: $token->relationshipType,
        );
    }
}
