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

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 sealed class CookiesWrapperFeature : IResponseCookiesFeature
    {
        public CookiesWrapperFeature(ResponseCookiesWrapper wrapper)
        {
            Cookies = wrapper;
        }

        public IResponseCookies Cookies { get; }
    }
}
