using ColaFlow.Shared.Kernel.Common;

namespace ColaFlow.Modules.Identity.Domain.Aggregates.Users;

/// <summary>
/// Refresh Token entity for secure token rotation
/// </summary>
public sealed class RefreshToken : Entity
{
    public string TokenHash { get; private set; } = null!;
    public UserId UserId { get; private set; } = null!;
    public Guid TenantId { get; private set; }

    // Token lifecycle
    public DateTime ExpiresAt { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? RevokedAt { get; private set; }
    public string? RevokedReason { get; private set; }

    // Security tracking
    public string? IpAddress { get; private set; }
    public string? UserAgent { get; private set; }

    // Token rotation (token family tracking)
    public string? ReplacedByToken { get; private set; }

    // Navigation properties
    public string? DeviceInfo { get; private set; }

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

    // Factory method
    public static RefreshToken Create(
        string tokenHash,
        UserId userId,
        Guid tenantId,
        DateTime expiresAt,
        string? ipAddress = null,
        string? userAgent = null,
        string? deviceInfo = null)
    {
        if (string.IsNullOrWhiteSpace(tokenHash))
            throw new ArgumentException("Token hash cannot be empty", nameof(tokenHash));

        if (expiresAt <= DateTime.UtcNow)
            throw new ArgumentException("Expiration date must be in the future", nameof(expiresAt));

        return new RefreshToken
        {
            Id = Guid.NewGuid(),
            TokenHash = tokenHash,
            UserId = userId,
            TenantId = tenantId,
            ExpiresAt = expiresAt,
            CreatedAt = DateTime.UtcNow,
            IpAddress = ipAddress,
            UserAgent = userAgent,
            DeviceInfo = deviceInfo
        };
    }

    // Business methods
    public bool IsExpired() => DateTime.UtcNow >= ExpiresAt;

    public bool IsRevoked() => RevokedAt.HasValue;

    public bool IsActive() => !IsExpired() && !IsRevoked();

    public void Revoke(string reason)
    {
        if (IsRevoked())
            throw new InvalidOperationException("Token is already revoked");

        RevokedAt = DateTime.UtcNow;
        RevokedReason = reason;
    }

    public void MarkAsReplaced(string newTokenHash)
    {
        if (string.IsNullOrWhiteSpace(newTokenHash))
            throw new ArgumentException("New token hash cannot be empty", nameof(newTokenHash));

        ReplacedByToken = newTokenHash;
        RevokedAt = DateTime.UtcNow;
        RevokedReason = "Token rotated";
    }
}
