using System.Security.Cryptography;
namespace Halo.Core.Auth;
/// Eine laufende Anmeldung per Code — für den Fernseher.
///
///
///
/// Ein Passwort auf der Apple-TV-Fernbedienung einzutippen ist eine Zumutung,
/// die man selbst jedes Mal spürt. Stattdessen: sechsstelliger Code auf dem
/// Bildschirm, Bestätigung am Telefon.
///
///
/// Der Code allein holt nie ein Token ab. Dafür gibt es den Abholschein,
/// den nur das anfragende Gerät kennt und der hier nur als Fingerabdruck
/// liegt. Ohne ihn könnte jemand sechsstellige Zahlen durchprobieren — eine
/// Million Versuche sind nichts, und am Ende stünde ein fremdes Gerät in der
/// Mediathek.
///
///
public sealed class AuthCode
{
/// Kurz genug zum Vorlesen, lang genug zum Nichterraten.
public const int CodeLength = 6;
/// So lange, wie jemand braucht, um zum Telefon zu greifen.
public static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5);
public Guid Id { get; init; } = Guid.CreateVersion7();
public required string Code { get; init; }
public required string HandleFingerprint { get; init; }
public required string DeviceName { get; init; }
public required string DeviceId { get; init; }
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
public required DateTimeOffset ExpiresAt { get; init; }
/// Wer bestätigt hat — null, solange niemand.
public Guid? ApprovedByUserId { get; set; }
/// Wann das Token abgeholt wurde. Danach ist der Code verbraucht.
public DateTimeOffset? ConsumedAt { get; set; }
public bool IsExpired(DateTimeOffset now) => now >= ExpiresAt;
/// Ob jetzt ein Token herausgegeben werden darf.
public bool CanBeRedeemed(DateTimeOffset now) =>
ApprovedByUserId is not null && ConsumedAt is null && !IsExpired(now);
/// Sechs Ziffern, gleichverteilt gezogen.
///
///
/// und nicht : der
/// Code ist ein Zugang. Die obere Grenze ist ausschliesslich, führende
/// Nullen bleiben erhalten.
///
public static string NewCode() =>
RandomNumberGenerator.GetInt32(0, 1_000_000).ToString("D" + CodeLength);
}