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

using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Net;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features;

namespace Microsoft.AspNetCore.Connections
{
    /// <summary>
    /// The default implementation for the <see cref="ConnectionContext"/>.
    /// </summary>
    public class DefaultConnectionContext : ConnectionContext,
                                            IConnectionIdFeature,
                                            IConnectionItemsFeature,
                                            IConnectionTransportFeature,
                                            IConnectionUserFeature,
                                            IConnectionLifetimeFeature,
                                            IConnectionEndPointFeature
    {
        private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource();

        /// <summary>
        /// Creates the <see cref="DefaultConnectionContext"/> without Pipes to avoid upfront allocations.
        /// The caller is expected to set the <see cref="Transport"/> and <see cref="Application"/> pipes manually.
        /// </summary>
        public DefaultConnectionContext() :
            this(Guid.NewGuid().ToString())
        {
        }

        /// <summary>
        /// Creates the <see cref="DefaultConnectionContext"/> without Pipes to avoid upfront allocations.
        /// The caller is expected to set the <see cref="Transport"/> and <see cref="Application"/> pipes manually.
        /// </summary>
        /// <param name="id">The <see cref="ConnectionId"/>.</param>
        public DefaultConnectionContext(string id)
        {
            ConnectionId = id;

            Features = new FeatureCollection();
            Features.Set<IConnectionUserFeature>(this);
            Features.Set<IConnectionItemsFeature>(this);
            Features.Set<IConnectionIdFeature>(this);
            Features.Set<IConnectionTransportFeature>(this);
            Features.Set<IConnectionLifetimeFeature>(this);
            Features.Set<IConnectionEndPointFeature>(this);

            ConnectionClosed = _connectionClosedTokenSource.Token;
        }


        /// <summary>
        /// Creates the DefaultConnectionContext with the given <paramref name="transport"/> and <paramref name="application"/> pipes.
        /// </summary>
        /// <param name="id">The <see cref="ConnectionId"/>.</param>
        /// <param name="transport">The <see cref="Transport"/>.</param>
        /// <param name="application">The <see cref="Application"/>.</param>
        public DefaultConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application)
            : this(id)
        {
            Transport = transport;
            Application = application;
        }

        /// <inheritdoc />
        public override string ConnectionId { get; set; }

        /// <inheritdoc />
        public override IFeatureCollection Features { get; }

        /// <inheritdoc />
        public ClaimsPrincipal? User { get; set; }

        /// <inheritdoc />
        public override IDictionary<object, object?> Items { get; set; } = new ConnectionItems();

        /// <inheritdoc />
        public IDuplexPipe? Application { get; set; }

        /// <inheritdoc />
        public override IDuplexPipe Transport { get; set; } = default!;

        /// <inheritdoc />
        public override CancellationToken ConnectionClosed { get; set; }

        /// <inheritdoc />
        public override EndPoint? LocalEndPoint { get; set; }

        /// <inheritdoc />
        public override EndPoint? RemoteEndPoint { get; set; }

        /// <inheritdoc />
        public override void Abort(ConnectionAbortedException abortReason)
        {
            ThreadPool.UnsafeQueueUserWorkItem(cts => ((CancellationTokenSource)cts!).Cancel(), _connectionClosedTokenSource);
        }

        /// <inheritdoc />
        public override ValueTask DisposeAsync()
        {
            _connectionClosedTokenSource.Dispose();
            return base.DisposeAsync();
        }
    }
}
