using EVCB_OCPP.WEBAPI.Helpers;
using EVCB_OCPP.WEBAPI.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using System.Net.Http;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Threading;
using EVCB_OCPP.WEBAPI.Models.WebAPI;
using Newtonsoft.Json;
using System.Linq;
using System.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Data.SqlClient;
using EVCB_OCPP.WEBAPI.Services;

namespace EVCB_OCPP.WEBAPI.Middleware;

public class APILogMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ApiLogDbService apiLogDbService;
    private readonly SqlConnectionFactory<APILogDBContext> apiLogDbConnectionFactory;

    //private readonly string apiLogConnectionString;

    public APILogMiddleware(RequestDelegate next,
        ApiLogDbService apiLogDbService,
        SqlConnectionFactory<APILogDBContext> apiLogDbConnectionFactory)
    {
        _next = next;
        this.apiLogDbService = apiLogDbService;
        this.apiLogDbConnectionFactory = apiLogDbConnectionFactory;
        //apiLogConnectionString = configuration.GetConnectionString("APILogDBContext");
    }

    public async Task Invoke(HttpContext context)
    {
        var request = context.Request;
        //string uri = request.GetDisplayUrl().ToString();
        CancellationToken cancellationToken = context?.RequestAborted ?? CancellationToken.None;
        var apiLogEntry = CreateApiLogEntryWithRequestData(request);

        //context.Request.Body.Position = 0;
        //await new StreamReader(context.Request.Body).ReadToEndAsync()
        //    .ContinueWith(task =>
        //    {
        //        apiLogEntry.RequestContentBody = task.Result;

        //    }, cancellationToken);
        apiLogEntry.RequestContentBody = context.GetPreLoadBody();

        var originalBodyStream = context.Response.Body;
        var fakeResponseBody = new MemoryStream();
        context.Response.Body = fakeResponseBody;

        var task =  _next(context);
        await task;

        fakeResponseBody.Seek(0, SeekOrigin.Begin);
        await fakeResponseBody.CopyToAsync(originalBodyStream);

        var test = request.Path;

        if (request.GetDisplayUrl().Contains("api/v1/file"))
        {
            fakeResponseBody.Dispose();
            return;
        }

        if (task.IsFaulted)
        {
            var result = new CPOOuterResponse() { StatusCode = (int)CPO_StatusCode.UnexpectedError, StatusMessage = CPO_StatusMessage.ERROR_MSG_Global_UNEXPECTEDERROR };

            apiLogEntry.ResponseContentBody = JsonConvert.SerializeObject(result, EVCBConfiguration.JSONSERIALIZER_FORMAT);
            apiLogEntry.ResponseContentType = "";
            apiLogEntry.ResponseHeaders = "";

            apiLogEntry.ErrorOn = DateTime.Now;
            apiLogEntry.ErrorMsg = task.Exception.ToString().Replace("'", " ");

            apiLogEntry.ResponseStatusCode = (int)HttpStatusCode.InternalServerError;
            apiLogEntry.ResponseTimestamp = DateTime.Now;
            _ = apiLogDbService.Write(apiLogEntry);
            //return request.CreateResponse(HttpStatusCode.InternalServerError, result);
            await context.Response.WriteAsJsonAsync(result);
            return;
        }
        else
        {
            //var response = task.Result;
            var response = context.Response;

            if (fakeResponseBody != null)
            {
                fakeResponseBody.Seek(0, SeekOrigin.Begin);
                var bodyData = new StreamReader(fakeResponseBody).ReadToEnd();
                fakeResponseBody.Seek(0, SeekOrigin.Begin);

                apiLogEntry.ResponseContentBody = bodyData;
                apiLogEntry.ResponseContentType = response.Headers.ContentType;
                apiLogEntry.ResponseHeaders = WebApiHelper.SerializeHeaders(response.Headers);
            }

            int errorMessageIndex = apiLogEntry.ResponseContentBody.IndexOf(",\"ErrorDetail\"");
            //string endTag = apiLogEntry.ResponseContentBody.Last().ToString();

            if (errorMessageIndex > 0)
            {
                string errorMsg = apiLogEntry.ResponseContentBody.Substring(errorMessageIndex).Remove(0, 16);
                apiLogEntry.ResponseContentBody = apiLogEntry.ResponseContentBody.Remove(errorMessageIndex).Remove(0, 1);
                apiLogEntry.ErrorOn = DateTime.Now;
                apiLogEntry.ErrorMsg = errorMsg.Remove(errorMsg.Count() - 2).Replace("'", " ");

                //response = request.CreateResponse(response.StatusCode, JsonConvert.DeserializeObject("{" + apiLogEntry.ResponseContentBody + "}"));
                await response.WriteAsync("{" + apiLogEntry.ResponseContentBody + "}");
            }

            apiLogEntry.ResponseStatusCode = (int)response.StatusCode;
            apiLogEntry.ResponseTimestamp = DateTime.Now;
            _ = apiLogDbService.Write(apiLogEntry);

            fakeResponseBody.Dispose();

            return;
        }
    }

    private ApiLogEntry CreateApiLogEntryWithRequestData(HttpRequest request)
    {
        try
        {
            HttpContext context = request.HttpContext;
            //var routeData = request.GetRouteData();

            return new ApiLogEntry
            {
                Application = "[Api]",
                User = context.User.Identity.Name,
                Machine = Environment.MachineName,
                RequestContentType = context.Request.ContentType,
                //RequestRouteTemplate = routeData.Route.RouteTemplate,
                RequestRouteData = "",
                RequestIpAddress = context.Connection.RemoteIpAddress is null ? "" : context.Connection.RemoteIpAddress.ToString(),
                RequestMethod = request.Method,
                RequestHeaders = WebApiHelper.SerializeHeaders(request.Headers),
                RequestTimestamp = DateTime.Now,
                RequestUri = request.GetDisplayUrl()
            };
        }
        catch (Exception ex)
        {
            //  logger.Debug(" " + ex.ToString());
            return null;
        }

    }
}
