// Example: Converting DeleteItem from Sync to Async
// File: Jellyfin.Server.Implementations\Item\BaseItemRepository.cs

// ==========================================
// BEFORE (Current Synchronous Version)
// ==========================================
public void DeleteItem(params IReadOnlyList<Guid> ids)
{
    if (ids is null || ids.Count == 0 || ids.Any(f => f.Equals(PlaceholderId)))
    {
        throw new ArgumentException("Guid can't be empty or the placeholder id.", nameof(ids));
    }

    using var context = _dbProvider.CreateDbContext();
    using var transaction = context.Database.BeginTransaction();

    var date = (DateTime?)DateTime.UtcNow;
    var relatedItems = ids.SelectMany(f => TraverseHirachyDown(f, context)).ToArray();

    // Remove conflicting UserData
    context.UserData
        .Join(
            context.UserData.WhereOneOrMany(relatedItems, e => e.ItemId),
            placeholder => new { placeholder.UserId, placeholder.CustomDataKey },
            userData => new { userData.UserId, userData.CustomDataKey },
            (placeholder, userData) => placeholder)
        .Where(e => e.ItemId == PlaceholderId)
        .ExecuteDelete();

    // Detach user data
    context.UserData.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteUpdate(e => e
            .SetProperty(f => f.RetentionDate, date)
            .SetProperty(f => f.ItemId, PlaceholderId));

    // Delete related entities
    context.AncestorIds.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
    context.AttachmentStreamInfos.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
    context.BaseItems.WhereOneOrMany(relatedItems, e => e.Id).ExecuteDelete();
    
    var peopleIds = context.PeopleBaseItemMap
        .WhereOneOrMany(relatedItems, e => e.ItemId)
        .Select(f => f.PeopleId)
        .Distinct()
        .ToArray(); // 🔴 SYNC: ToArray()
        
    context.PeopleBaseItemMap.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
    context.Peoples.WhereOneOrMany(peopleIds, e => e.Id)
        .Where(e => e.BaseItems!.Count == 0)
        .ExecuteDelete();
    
    context.SaveChanges(); // 🔴 SYNC: SaveChanges()
    transaction.Commit(); // 🔴 SYNC: Commit()
}

// ==========================================
// AFTER (Async Version)
// ==========================================
public async Task DeleteItemAsync(IReadOnlyList<Guid> ids, CancellationToken cancellationToken = default)
{
    if (ids is null || ids.Count == 0 || ids.Any(f => f.Equals(PlaceholderId)))
    {
        throw new ArgumentException("Guid can't be empty or the placeholder id.", nameof(ids));
    }

    await using var context = _dbProvider.CreateDbContext(); // ✅ await using
    await using var transaction = await context.Database.BeginTransactionAsync(cancellationToken); // ✅ BeginTransactionAsync

    var date = (DateTime?)DateTime.UtcNow;
    
    // ✅ Convert TraverseHirachyDown to async
    var relatedItems = await GetRelatedItemsAsync(ids, context, cancellationToken);

    // Remove conflicting UserData
    await context.UserData
        .Join(
            context.UserData.WhereOneOrMany(relatedItems, e => e.ItemId),
            placeholder => new { placeholder.UserId, placeholder.CustomDataKey },
            userData => new { userData.UserId, userData.CustomDataKey },
            (placeholder, userData) => placeholder)
        .Where(e => e.ItemId == PlaceholderId)
        .ExecuteDeleteAsync(cancellationToken); // ✅ ExecuteDeleteAsync

    // Detach user data
    await context.UserData.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteUpdateAsync(e => e
            .SetProperty(f => f.RetentionDate, date)
            .SetProperty(f => f.ItemId, PlaceholderId), 
            cancellationToken); // ✅ ExecuteUpdateAsync

    // Delete related entities (can be done in parallel if no dependencies)
    await Task.WhenAll(
        context.AncestorIds.WhereOneOrMany(relatedItems, e => e.ItemId)
            .ExecuteDeleteAsync(cancellationToken),
        context.AttachmentStreamInfos.WhereOneOrMany(relatedItems, e => e.ItemId)
            .ExecuteDeleteAsync(cancellationToken)
    );

    // Delete base items
    await context.BaseItems.WhereOneOrMany(relatedItems, e => e.Id)
        .ExecuteDeleteAsync(cancellationToken);
    
    // Get people IDs to check for orphans
    var peopleIds = await context.PeopleBaseItemMap
        .WhereOneOrMany(relatedItems, e => e.ItemId)
        .Select(f => f.PeopleId)
        .Distinct()
        .ToArrayAsync(cancellationToken); // ✅ ToArrayAsync
        
    await context.PeopleBaseItemMap.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteDeleteAsync(cancellationToken);
        
    await context.Peoples.WhereOneOrMany(peopleIds, e => e.Id)
        .Where(e => e.BaseItems!.Count == 0)
        .ExecuteDeleteAsync(cancellationToken);
    
    await context.SaveChangesAsync(cancellationToken); // ✅ SaveChangesAsync
    await transaction.CommitAsync(cancellationToken); // ✅ CommitAsync
}

// Helper method - also converted to async
private async Task<Guid[]> GetRelatedItemsAsync(
    IReadOnlyList<Guid> ids, 
    JellyfinDbContext context, 
    CancellationToken cancellationToken)
{
    var relatedItems = new HashSet<Guid>();
    foreach (var id in ids)
    {
        var related = await TraverseHirachyDownAsync(id, context, cancellationToken);
        foreach (var item in related)
        {
            relatedItems.Add(item);
        }
    }
    return relatedItems.ToArray();
}

// ==========================================
// Interface Change Required
// ==========================================

// In IItemRepository.cs:

// BEFORE:
void DeleteItem(params IReadOnlyList<Guid> ids);

// AFTER:
Task DeleteItemAsync(IReadOnlyList<Guid> ids, CancellationToken cancellationToken = default);

// ==========================================
// Calling Code Changes
// ==========================================

// In LibraryManager.cs or similar:

// BEFORE:
public void DeleteItem(BaseItem item)
{
    _itemRepository.DeleteItem(new[] { item.Id });
}

// AFTER:
public async Task DeleteItemAsync(BaseItem item, CancellationToken cancellationToken = default)
{
    await _itemRepository.DeleteItemAsync(new[] { item.Id }, cancellationToken);
}

// ==========================================
// API Controller Changes
// ==========================================

// In ItemsController.cs:

// BEFORE:
[HttpDelete("{id}")]
public ActionResult DeleteItem([FromRoute] Guid id)
{
    var item = _libraryManager.GetItem(id);
    if (item is null)
    {
        return NotFound();
    }
    
    _libraryManager.DeleteItem(item);
    return NoContent();
}

// AFTER:
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteItem(
    [FromRoute] Guid id, 
    CancellationToken cancellationToken)
{
    var item = await _libraryManager.GetItemAsync(id, cancellationToken);
    if (item is null)
    {
        return NotFound();
    }
    
    await _libraryManager.DeleteItemAsync(item, cancellationToken);
    return NoContent();
}

// ==========================================
// Performance Optimization: Parallel Deletes
// ==========================================

// When deleting multiple independent entities, use Task.WhenAll:

// BEFORE (Sequential):
context.AncestorIds.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
context.AttachmentStreamInfos.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
context.BaseItemImageInfos.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();
context.Chapters.WhereOneOrMany(relatedItems, e => e.ItemId).ExecuteDelete();

// AFTER (Parallel):
await Task.WhenAll(
    context.AncestorIds.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteDeleteAsync(cancellationToken),
    context.AttachmentStreamInfos.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteDeleteAsync(cancellationToken),
    context.BaseItemImageInfos.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteDeleteAsync(cancellationToken),
    context.Chapters.WhereOneOrMany(relatedItems, e => e.ItemId)
        .ExecuteDeleteAsync(cancellationToken)
);

// ⚡ This can significantly improve performance for operations with many independent deletes!

// ==========================================
// Testing the Async Version
// ==========================================

[Fact]
public async Task DeleteItemAsync_ValidId_DeletesSuccessfully()
{
    // Arrange
    var itemId = Guid.NewGuid();
    await _repository.SaveItemAsync(new BaseItem { Id = itemId }, CancellationToken.None);
    
    // Act
    await _repository.DeleteItemAsync(new[] { itemId }, CancellationToken.None);
    
    // Assert
    var item = await _repository.RetrieveItemAsync(itemId, CancellationToken.None);
    Assert.Null(item);
}

[Fact]
public async Task DeleteItemAsync_Cancelled_ThrowsOperationCanceledException()
{
    // Arrange
    var itemId = Guid.NewGuid();
    var cts = new CancellationTokenSource();
    cts.Cancel();
    
    // Act & Assert
    await Assert.ThrowsAsync<OperationCanceledException>(
        async () => await _repository.DeleteItemAsync(new[] { itemId }, cts.Token)
    );
}
