using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Mvp.Foundation.Configuration.Rendering.AppSettings;
using Mvp.Foundation.DataFetching.GraphQL;
using Sitecore.AspNet.RenderingEngine;
using Sitecore.AspNet.RenderingEngine.Configuration;
using Sitecore.AspNet.RenderingEngine.Middleware;
using Sitecore.LayoutService.Client;
using Sitecore.LayoutService.Client.Exceptions;
using Sitecore.LayoutService.Client.Request;
using Sitecore.LayoutService.Client.Response;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;

namespace Mvp.Project.MvpSite.Middleware
{
    public class CustomRenderingEngineMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ISitecoreLayoutRequestMapper _requestMapper;
        private readonly ISitecoreLayoutClient _layoutService;
        private readonly RenderingEngineOptions _options;
        private readonly IGraphQLClientFactory _graphQLClientFactory;
        private readonly IGraphQLRequestBuilder _graphQLRequestBuilder;
        private readonly IConfiguration _configuration;

        public CustomRenderingEngineMiddleware(RequestDelegate next, ISitecoreLayoutClient layoutService, ISitecoreLayoutRequestMapper requestMapper, IOptions<RenderingEngineOptions> options, IGraphQLClientFactory graphQLClientFactory, IGraphQLRequestBuilder graphQLRequestBuilder, IConfiguration configuration)
        {
            _next = next;
            _requestMapper = requestMapper;
            _options = options.Value;
            _graphQLClientFactory = graphQLClientFactory;
            _graphQLRequestBuilder = graphQLRequestBuilder;
            _layoutService = layoutService;
            _configuration= configuration;
        }              

        public async Task Invoke(HttpContext httpContext, IViewComponentHelper viewComponentHelper, IHtmlHelper htmlHelper)
        {
            SitecoreLayoutRequest sitecoreLayoutRequest = _requestMapper.Map(httpContext.Request);

            if (httpContext.GetSitecoreRenderingContext() == null)
            {
                SitecoreLayoutResponse response = await GetSitecoreLayoutResponse(httpContext).ConfigureAwait(continueOnCapturedContext: false);

                //Check not found page and set status code - start
                if (response.HasErrors)
                {
                    foreach (SitecoreLayoutServiceClientException error in response.Errors)
                    {
                        switch (error)
                        {
                            case ItemNotFoundSitecoreLayoutServiceClientException:
                                httpContext.Request.Path = _configuration.GetSection(MvpSiteSettings.Key).Get<MvpSiteSettings>().NotFoundPage;
                                sitecoreLayoutRequest = _requestMapper.Map(httpContext.Request);
                                response = await GetSitecoreLayoutResponse(httpContext).ConfigureAwait(continueOnCapturedContext: false);
                                if (response.Request.Path() == _configuration.GetSection(MvpSiteSettings.Key).Get<MvpSiteSettings>().NotFoundPage) { httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; }
                                break;

                            default:
                                throw error;
                        }
                    }
                }
                //Check not found page and set status code - end

                SitecoreRenderingContext renderingContext = new()
                {
                    Response = response,
                    RenderingHelpers = new RenderingHelpers(viewComponentHelper, htmlHelper)
                };
                httpContext.SetSitecoreRenderingContext(renderingContext);
            }
            else
            {
                httpContext.GetSitecoreRenderingContext().RenderingHelpers = new RenderingHelpers(viewComponentHelper, htmlHelper);
            }

            foreach (Action<HttpContext> postRenderingAction in (IEnumerable<Action<HttpContext>>)_options.PostRenderingActions)
                postRenderingAction(httpContext);

            httpContext.Items.Add(nameof(RenderingEngineMiddleware), null);

            await _next(httpContext).ConfigureAwait(continueOnCapturedContext: false);
        }

        private async Task<SitecoreLayoutResponse> GetSitecoreLayoutResponse(HttpContext httpContext)
        {
            SitecoreLayoutRequest sitecoreLayoutRequest = _requestMapper.Map(httpContext.Request);
            return await _layoutService.Request(sitecoreLayoutRequest).ConfigureAwait(continueOnCapturedContext: false);
        }
    }
}