using System.Data.Common;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Tiku.Infrastructure.Observability;

public sealed class DatabasePerformanceInterceptor(
    ILogger<DatabasePerformanceInterceptor> logger) : DbCommandInterceptor
{
    private const double SlowCommandMilliseconds = 500;

    public override DbDataReader ReaderExecuted(
        DbCommand command,
        CommandExecutedEventData eventData,
        DbDataReader result)
    {
        Record(command, eventData.Duration, "reader");
        return result;
    }

    public override ValueTask<DbDataReader> ReaderExecutedAsync(
        DbCommand command,
        CommandExecutedEventData eventData,
        DbDataReader result,
        CancellationToken cancellationToken = default)
    {
        Record(command, eventData.Duration, "reader");
        return ValueTask.FromResult(result);
    }

    public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
    {
        Record(command, eventData.Duration, "non_query");
        return result;
    }

    public override ValueTask<int> NonQueryExecutedAsync(
        DbCommand command,
        CommandExecutedEventData eventData,
        int result,
        CancellationToken cancellationToken = default)
    {
        Record(command, eventData.Duration, "non_query");
        return ValueTask.FromResult(result);
    }

    public override object? ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object? result)
    {
        Record(command, eventData.Duration, "scalar");
        return result;
    }

    public override ValueTask<object?> ScalarExecutedAsync(
        DbCommand command,
        CommandExecutedEventData eventData,
        object? result,
        CancellationToken cancellationToken = default)
    {
        Record(command, eventData.Duration, "scalar");
        return ValueTask.FromResult(result);
    }

    private void Record(DbCommand command, TimeSpan duration, string operation)
    {
        var fingerprint = DatabasePerformanceTelemetry.Fingerprint(command.CommandText);
        var tags = new TagList { { "db.operation", operation } };
        DatabasePerformanceTelemetry.CommandCounter.Add(1, tags);
        DatabasePerformanceTelemetry.CommandDuration.Record(
            duration.TotalMilliseconds,
            tags);
        DatabaseRequestMetrics.Record(duration, fingerprint);

        if (duration.TotalMilliseconds >= SlowCommandMilliseconds)
            logger.LogWarning(
                "Slow database command {CommandFingerprint} ({Operation}) completed in {ElapsedMilliseconds:F1} ms.",
                fingerprint,
                operation,
                duration.TotalMilliseconds);
    }
}