// 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.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.CookiePolicy
{
    /// <summary>
    /// Initializes a new instance of <see cref="CookiePolicyMiddleware"/>.
    /// </summary>
    public class CookiePolicyMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        /// <summary>
        /// Initializes a new instance of <see cref="CookiePolicyMiddleware"/>.
        /// </summary>
        /// <param name="next">A reference to the next item in the application pipeline.</param>
        /// <param name="options">Accessor to <see cref="CookiePolicyOptions"/>.</param>
        /// <param name="factory">The <see cref="ILoggerFactory"/>.</param>
        public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options, ILoggerFactory factory)
        {
            Options = options.Value;
            _next = next ?? throw new ArgumentNullException(nameof(next));
            _logger = factory.CreateLogger<CookiePolicyMiddleware>();
        }

        /// <summary>
        /// Initializes a new instance of <see cref="CookiePolicyMiddleware"/>.
        /// </summary>
        /// <param name="next">A reference to the next item in the application pipeline.</param>
        /// <param name="options">Accessor to <see cref="CookiePolicyOptions"/>.</param>
        public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options)
        {
            Options = options.Value;
            _next = next;
            _logger = NullLogger.Instance;
        }

        /// <summary>
        /// Gets or sets the <see cref="CookiePolicyOptions"/>.
        /// </summary>
        public CookiePolicyOptions Options { get; set; }

        /// <summary>
        /// Invokes the middleware.
        /// </summary>
        /// <param name="context">The <see cref="HttpContext" />.</param>
        public Task Invoke(HttpContext context)
        {
            var feature = context.Features.Get<IResponseCookiesFeature>() ?? new ResponseCookiesFeature(context.Features);
            var wrapper = new ResponseCookiesWrapper(context, Options, feature, _logger);
            context.Features.Set<IResponseCookiesFeature>(new CookiesWrapperFeature(wrapper));
            context.Features.Set<ITrackingConsentFeature>(wrapper);

            return _next(context);
        }

        private class CookiesWrapperFeature : IResponseCookiesFeature
        {
            public CookiesWrapperFeature(ResponseCookiesWrapper wrapper)
            {
                Cookies = wrapper;
            }

            public IResponseCookies Cookies { get; }
        }
    }
}
