namespace SIGCM2.Domain.Entities;

public sealed class RefreshToken
{
    public int Id { get; init; }
    public int UsuarioId { get; init; }
    public string TokenHash { get; init; } = null!;
    public Guid FamilyId { get; init; }
    public DateTime IssuedAt { get; init; }
    public DateTime ExpiresAt { get; init; }
    public DateTime? RevokedAt { get; private set; }
    public int? ReplacedById { get; private set; }
    public string CreatedByIp { get; init; } = null!;
    public string? UserAgent { get; init; }

    /// <summary>Factory for a brand-new session (login). Generates a new FamilyId.</summary>
    public static RefreshToken IssueForNewFamily(
        int usuarioId,
        string tokenHash,
        DateTime now,
        TimeSpan ttl,
        string createdByIp,
        string? userAgent)
        => new()
        {
            UsuarioId = usuarioId,
            TokenHash = tokenHash,
            FamilyId = Guid.NewGuid(),
            IssuedAt = now,
            ExpiresAt = now + ttl,
            CreatedByIp = createdByIp,
            UserAgent = userAgent,
        };

    /// <summary>Factory for a rotation. Inherits FamilyId and ExpiresAt (absolute TTL).</summary>
    public static RefreshToken IssueRotation(
        RefreshToken previous,
        string newTokenHash,
        DateTime now,
        string createdByIp,
        string? userAgent)
        => new()
        {
            UsuarioId = previous.UsuarioId,
            TokenHash = newTokenHash,
            FamilyId = previous.FamilyId,
            IssuedAt = now,
            ExpiresAt = previous.ExpiresAt,  // ABSOLUTE — inherited from original
            CreatedByIp = createdByIp,
            UserAgent = userAgent,
        };

    /// <summary>Returns true if ExpiresAt &lt;= now (expired).</summary>
    public bool IsExpired(DateTime now) => now >= ExpiresAt;

    /// <summary>Returns true if the token has been explicitly revoked.</summary>
    public bool IsRevoked => RevokedAt.HasValue;

    /// <summary>Returns true if the token is neither revoked nor expired.</summary>
    public bool IsActive(DateTime now) => !IsRevoked && !IsExpired(now);

    /// <summary>
    /// Marks the token as revoked with the given timestamp and optional successor id.
    /// Should only be called by the repository when reconstructing or persisting state.
    /// This is NOT the way to revoke in business logic — use the repository SQL methods.
    /// </summary>
    public void MarkAsPersistedRevocation(DateTime revokedAt, int? replacedById)
    {
        RevokedAt = revokedAt;
        ReplacedById = replacedById;
    }
}
