using System.Security.Cryptography;
using StorageService.Domain.SeedWork;

namespace StorageService.Domain.AggregatesModel.FileShareAggregate;

/// <summary>
/// EN: File share aggregate root - represents a share link for a file.
/// VI: FileShare aggregate root - đại diện cho link chia sẻ của file.
/// </summary>
public class FileShare : Entity, IAggregateRoot
{
    /// <summary>EN: File being shared / VI: File được chia sẻ</summary>
    public Guid FileId { get; private set; }

    /// <summary>EN: User who created the share / VI: User tạo share</summary>
    public string SharedBy { get; private set; } = default!;

    /// <summary>EN: User the file is shared with (null = public link) / VI: User được share (null = link công khai)</summary>
    public string? SharedWith { get; private set; }

    /// <summary>EN: Share permission level / VI: Mức quyền chia sẻ</summary>
    public SharePermission Permission { get; private set; }

    /// <summary>EN: Unique share token / VI: Token chia sẻ duy nhất</summary>
    public string ShareToken { get; private set; } = default!;

    /// <summary>EN: Optional password hash / VI: Hash mật khẩu (tùy chọn)</summary>
    public string? PasswordHash { get; private set; }

    /// <summary>EN: Share expiration date / VI: Ngày hết hạn chia sẻ</summary>
    public DateTime? ExpiresAt { get; private set; }

    /// <summary>EN: Maximum download count / VI: Số lần tải xuống tối đa</summary>
    public int? MaxDownloads { get; private set; }

    /// <summary>EN: Current download count / VI: Số lần đã tải xuống</summary>
    public int DownloadCount { get; private set; }

    /// <summary>EN: Share status / VI: Trạng thái chia sẻ</summary>
    public FileShareStatus Status { get; private set; }

    /// <summary>EN: Created timestamp / VI: Thời gian tạo</summary>
    public DateTime CreatedAt { get; private set; }

    /// <summary>EN: Revoked timestamp / VI: Thời gian thu hồi</summary>
    public DateTime? RevokedAt { get; private set; }

    // EN: For EF Core / VI: Cho EF Core
    protected FileShare() { }

    /// <summary>
    /// EN: Create a new file share.
    /// VI: Tạo file share mới.
    /// </summary>
    public FileShare(
        Guid fileId,
        string sharedBy,
        SharePermission permission,
        string? sharedWith = null,
        string? password = null,
        DateTime? expiresAt = null,
        int? maxDownloads = null)
    {
        FileId = fileId;
        SharedBy = sharedBy;
        SharedWith = sharedWith;
        Permission = permission;
        ShareToken = GenerateShareToken();
        ExpiresAt = expiresAt;
        MaxDownloads = maxDownloads;
        DownloadCount = 0;
        Status = FileShareStatus.Active;
        CreatedAt = DateTime.UtcNow;

        if (!string.IsNullOrEmpty(password))
        {
            PasswordHash = HashPassword(password);
        }
    }

    /// <summary>
    /// EN: Check if share is valid (not expired, not revoked, not limit reached).
    /// VI: Kiểm tra share có hợp lệ (chưa hết hạn, chưa bị thu hồi, chưa đạt giới hạn).
    /// </summary>
    public bool IsValid()
    {
        if (Status != FileShareStatus.Active)
            return false;

        if (ExpiresAt.HasValue && DateTime.UtcNow > ExpiresAt.Value)
        {
            Status = FileShareStatus.Expired;
            return false;
        }

        if (MaxDownloads.HasValue && DownloadCount >= MaxDownloads.Value)
        {
            Status = FileShareStatus.LimitReached;
            return false;
        }

        return true;
    }

    /// <summary>
    /// EN: Validate password if share is password-protected.
    /// VI: Xác thực mật khẩu nếu share có bảo vệ bằng mật khẩu.
    /// </summary>
    public bool ValidatePassword(string? password)
    {
        if (string.IsNullOrEmpty(PasswordHash))
            return true; // EN: No password required / VI: Không yêu cầu mật khẩu

        if (string.IsNullOrEmpty(password))
            return false;

        return VerifyPassword(password, PasswordHash);
    }

    /// <summary>
    /// EN: Increment download count.
    /// VI: Tăng số lần tải xuống.
    /// </summary>
    public void IncrementDownloadCount()
    {
        DownloadCount++;

        if (MaxDownloads.HasValue && DownloadCount >= MaxDownloads.Value)
        {
            Status = FileShareStatus.LimitReached;
        }
    }

    /// <summary>
    /// EN: Revoke the share.
    /// VI: Thu hồi chia sẻ.
    /// </summary>
    public void Revoke()
    {
        Status = FileShareStatus.Revoked;
        RevokedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Generate a unique share token.
    /// VI: Tạo token chia sẻ duy nhất.
    /// </summary>
    private static string GenerateShareToken()
    {
        var bytes = RandomNumberGenerator.GetBytes(32);
        return Convert.ToBase64String(bytes)
            .Replace("+", "-")
            .Replace("/", "_")
            .Replace("=", "");
    }

    /// <summary>
    /// EN: Hash password using PBKDF2.
    /// VI: Hash mật khẩu sử dụng PBKDF2.
    /// </summary>
    private static string HashPassword(string password)
    {
        var salt = RandomNumberGenerator.GetBytes(16);
        var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, 100000, HashAlgorithmName.SHA256, 32);
        
        var combined = new byte[salt.Length + hash.Length];
        Buffer.BlockCopy(salt, 0, combined, 0, salt.Length);
        Buffer.BlockCopy(hash, 0, combined, salt.Length, hash.Length);
        
        return Convert.ToBase64String(combined);
    }

    /// <summary>
    /// EN: Verify password against hash.
    /// VI: Xác thực mật khẩu với hash.
    /// </summary>
    private static bool VerifyPassword(string password, string storedHash)
    {
        var combined = Convert.FromBase64String(storedHash);
        var salt = new byte[16];
        var storedHashBytes = new byte[32];
        
        Buffer.BlockCopy(combined, 0, salt, 0, 16);
        Buffer.BlockCopy(combined, 16, storedHashBytes, 0, 32);
        
        var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, 100000, HashAlgorithmName.SHA256, 32);
        
        return CryptographicOperations.FixedTimeEquals(hash, storedHashBytes);
    }
}
