using ColaFlow.Shared.Kernel.Common;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;

namespace ColaFlow.Modules.Identity.Domain.Entities;

/// <summary>
/// Password reset token entity with enhanced security.
/// Lifetime: 1 hour (short expiration for security).
/// Single-use only (cannot be reused).
/// </summary>
public sealed class PasswordResetToken : Entity
{
    public UserId UserId { get; private set; } = null!;
    public string TokenHash { get; private set; } = string.Empty;
    public DateTime ExpiresAt { get; private set; }
    public DateTime? UsedAt { get; private set; }
    public string? IpAddress { get; private set; }
    public string? UserAgent { get; private set; }
    public DateTime CreatedAt { get; private set; }

    // Private constructor for EF Core
    private PasswordResetToken() : base()
    {
    }

    /// <summary>
    /// Factory method to create new password reset token.
    /// </summary>
    /// <param name="userId">User ID requesting password reset</param>
    /// <param name="tokenHash">SHA-256 hash of the reset token</param>
    /// <param name="expiresAt">Expiration time (typically 1 hour from creation)</param>
    /// <param name="ipAddress">IP address of the requester</param>
    /// <param name="userAgent">User agent of the requester</param>
    /// <returns>New password reset token instance</returns>
    public static PasswordResetToken Create(
        UserId userId,
        string tokenHash,
        DateTime expiresAt,
        string? ipAddress = null,
        string? userAgent = null)
    {
        return new PasswordResetToken
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            TokenHash = tokenHash,
            ExpiresAt = expiresAt,
            IpAddress = ipAddress,
            UserAgent = userAgent,
            CreatedAt = DateTime.UtcNow
        };
    }

    /// <summary>
    /// Check if token has expired.
    /// </summary>
    public bool IsExpired => DateTime.UtcNow > ExpiresAt;

    /// <summary>
    /// Check if token has been used.
    /// </summary>
    public bool IsUsed => UsedAt.HasValue;

    /// <summary>
    /// Check if token is valid (not expired and not used).
    /// </summary>
    public bool IsValid => !IsExpired && !IsUsed;

    /// <summary>
    /// Mark the token as used.
    /// Can only be used once for security.
    /// </summary>
    /// <exception cref="InvalidOperationException">Thrown if token is not valid</exception>
    public void MarkAsUsed()
    {
        if (!IsValid)
            throw new InvalidOperationException("Token is not valid for password reset");

        UsedAt = DateTime.UtcNow;
    }
}
