<?php

declare(strict_types=1);

namespace App\Administration\Domain\Repository;

use App\Administration\Domain\Model\PasswordResetToken\PasswordResetToken;
use App\Administration\Domain\Model\PasswordResetToken\PasswordResetTokenId;
use DateTimeImmutable;

interface PasswordResetTokenRepository
{
    public function save(PasswordResetToken $token): void;

    /**
     * Find a token by its unique token value.
     * Use getByTokenValue() when you expect the token to exist.
     */
    public function findByTokenValue(string $tokenValue): ?PasswordResetToken;

    /**
     * Get a token by its unique token value.
     *
     * @throws \App\Administration\Domain\Exception\PasswordResetTokenNotFoundException if token does not exist
     */
    public function getByTokenValue(string $tokenValue): PasswordResetToken;

    /**
     * Get a token by its ID.
     *
     * @throws \App\Administration\Domain\Exception\PasswordResetTokenNotFoundException if token does not exist
     */
    public function get(PasswordResetTokenId $id): PasswordResetToken;

    /**
     * Delete a token (after use or for cleanup).
     */
    public function delete(PasswordResetTokenId $id): void;

    /**
     * Delete a token by its token value.
     */
    public function deleteByTokenValue(string $tokenValue): void;

    /**
     * Find an existing valid (not used, not expired) token for a user.
     * Returns null if no valid token exists.
     */
    public function findValidTokenForUser(string $userId): ?PasswordResetToken;

    /**
     * Atomically consume a token: validate it and mark it as used.
     *
     * This operation is protected against concurrent double-use by using a lock.
     * If two requests try to consume the same token simultaneously, only one
     * will succeed; the other will see the token as already used.
     *
     * @throws \App\Administration\Domain\Exception\PasswordResetTokenNotFoundException    if token does not exist
     * @throws \App\Administration\Domain\Exception\PasswordResetTokenExpiredException     if token has expired
     * @throws \App\Administration\Domain\Exception\PasswordResetTokenAlreadyUsedException if token was already used
     */
    public function consumeIfValid(string $tokenValue, DateTimeImmutable $at): PasswordResetToken;
}
