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

using System.Net;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;

namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal;

internal sealed class HttpMultiplexedConnectionMiddleware<TContext> where TContext : notnull
{
    private readonly ServiceContext _serviceContext;
    private readonly IHttpApplication<TContext> _application;
    private readonly HttpProtocols _protocols;
    private readonly bool _addAltSvcHeader;

    public HttpMultiplexedConnectionMiddleware(ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols, bool addAltSvcHeader)
    {
        _serviceContext = serviceContext;
        _application = application;
        _protocols = protocols;
        _addAltSvcHeader = addAltSvcHeader;
    }

    public Task OnConnectionAsync(MultiplexedConnectionContext connectionContext)
    {
        var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>();
        var localEndPoint = connectionContext.LocalEndPoint as IPEndPoint;
        var altSvcHeader = _addAltSvcHeader && localEndPoint != null ? HttpUtilities.GetEndpointAltSvc(localEndPoint, _protocols) : null;

        var httpConnectionContext = new HttpMultiplexedConnectionContext(
            connectionContext.ConnectionId,
            _protocols,
            altSvcHeader,
            connectionContext,
            _serviceContext,
            connectionContext.Features,
            memoryPoolFeature?.MemoryPool ?? System.Buffers.MemoryPool<byte>.Shared,
            localEndPoint,
            connectionContext.RemoteEndPoint as IPEndPoint);

        if (connectionContext.Features.Get<IConnectionMetricsTagsFeature>() is { } metricsTags)
        {
            // HTTP/3 is always TLS 1.3. If multiple versions are support in the future then this value will need to be detected.
            metricsTags.Tags.Add(new KeyValuePair<string, object?>("tls.protocol.version", "1.3"));
        }

        var connection = new HttpConnection(httpConnectionContext);

        return connection.ProcessRequestsAsync(_application);
    }
}
