// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Components.Common.Tests;
using Aspire.StackExchange.Redis.Tests;
using Microsoft.DotNet.RemoteExecutor;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Instrumentation.StackExchangeRedis;
using OpenTelemetry.Trace;
using Xunit;

namespace Aspire.StackExchange.Redis.DistributedCaching.Tests;

public class DistributedCacheConformanceTests : ConformanceTests
{
    // Schema only references Aspire.StackExchange.Redis' schema so nothing
    // specific to check here
    protected override (string json, string error)[] InvalidJsonToErrorMessage => Array.Empty<(string json, string error)>();

    protected override void RegisterComponent(HostApplicationBuilder builder, Action<StackExchangeRedisSettings>? configure = null, string? key = null)
    {
        if (key is null)
        {
            builder.AddRedisDistributedCache("redis", configure);
        }
        else
        {
            builder.AddKeyedRedisDistributedCache(key, configure);
        }
    }

    public DistributedCacheConformanceTests(RedisContainerFixture containerFixture) : base(containerFixture)
    {
    }

    [Fact]
    [RequiresDocker]
    public void WorksWithOpenTelemetryTracing()
    {
        RemoteExecutor.Invoke(async (connectionString) =>
        {
            var builder = Host.CreateEmptyApplicationBuilder(null);
            builder.Configuration.AddInMemoryCollection([
                new KeyValuePair<string, string?>("ConnectionStrings:redis", connectionString)
            ]);

            builder.AddRedisDistributedCache("redis");

            using var notifier = new ActivityNotifier();
            builder.Services.AddOpenTelemetry().WithTracing(builder => builder.AddProcessor(notifier));
            // set the FlushInterval to to zero so the Activity gets created immediately
            builder.Services.Configure<StackExchangeRedisInstrumentationOptions>(options => options.FlushInterval = TimeSpan.Zero);

            using var host = builder.Build();
            // We start the host to make it build TracerProvider.
            // If we don't, nothing gets reported!
            host.Start();

            var cache = host.Services.GetRequiredService<IDistributedCache>();
            await cache.GetAsync("myFakeKey", CancellationToken.None);

            // read the first 3 activities
            var activityList = await notifier.TakeAsync(3, TimeSpan.FromSeconds(10));
            Assert.Equal(3, activityList.Count);
            Assert.Collection(activityList,
                // https://github.com/dotnet/aspnetcore/pull/54239 added 2 CLIENT activities on the first call
                activity =>
                {
                    Assert.Equal("CLIENT", activity.OperationName);
                    Assert.Contains(activity.Tags, kvp => kvp.Key == "db.system" && kvp.Value == "redis");
                },
                activity =>
                {
                    Assert.Equal("CLIENT", activity.OperationName);
                    Assert.Contains(activity.Tags, kvp => kvp.Key == "db.system" && kvp.Value == "redis");
                },
                activity =>
                {
                    Assert.Equal("HMGET", activity.OperationName);
                    Assert.Contains(activity.Tags, kvp => kvp.Key == "db.system" && kvp.Value == "redis");
                });
        }, ConnectionString).Dispose();
    }
}
