using AipGateway.AIP;
using AipGateway.API.Application.Modules;
using AipGateway.API.Application.UtilityServices;
using AipGateway.API.Domain.Common.Utilities;
using AipGateway.API.Domain.Entities;
using AipGateway.API.Domain.IRepositories.IGenericRepositories;
using AipGateway.API.Domain.Models;
using AipGateway.API.Domain.Models.Request;
using AipGateway.API.Domain.Models.Response;
using AipGateway.API.Infrastructure.DataAccess;
using AipGateway.API.Infrastructure.Persistence;
using Azure.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net;
using System.Text;

namespace AipGateway.API.Application.Pipeline.Middlewares.Logging
{
    public class RequestResponseLogging
    {
        private readonly ILogger<RequestResponseLogging> _log;
        private readonly RequestDelegate _next;
        public RequestResponseLogging(ILogger<RequestResponseLogging> logger, RequestDelegate next)
        {
            _log = logger;
            _next = next;
            _log.LogError("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@: RequestResponseLogging");
        }

        //you can not inject it as a constructor parameter in Middleware because only Singleton services can be resolved by constructor injection in Middleware.
        public async Task Invoke(HttpContext context, IUnitOfWork unitOfWork)
        {
            //_logger.LogError("RequestResponseLogging Invoke Before:");
            string requestUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path} {context.Request.QueryString}".Trim();
            if (requestUrl.Contains(GlobalConstants.API_ENDPOINT))
            {
                string guid = GlobalConstants.GetApiGuid(context);
                var apiLog = await FormatRequest(context);
                var originalBodyStream = context.Response.Body;

                string response = "";
                using (var responseBody = new MemoryStream())
                {
                    context.Response.Body = responseBody;

                    try
                    {
                        await _next.Invoke(context);

                        response = await FormatResponse(context.Response);
                        apiLog.ResponseStatusCode = context.Response.StatusCode;
                        if (context.Response.StatusCode != (int)HttpStatusCode.OK)
                        {
                            var errorModel = JsonConvert.DeserializeObject<ApiErrorResponseModel>(response);
                            if (errorModel != null)
                            {
                                apiLog.ErrorCode = errorModel.errorCode;
                                apiLog.ErrorMessage = errorModel.Message;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        apiLog.ResponseStatusCode = context.Response.StatusCode;
                    }

                    apiLog.ApiGuid = guid;
                    apiLog.ResponseAt = DateTime.Now;
                    TimeSpan timeDiff = apiLog.ResponseAt - apiLog.RequestAt;
                    apiLog.ResponseTime = timeDiff.Milliseconds;

                    if (!response.ToLower().Contains("healthy") || !requestUrl.ToLower().Contains("swagger"))
                    {
                        await unitOfWork.AipApiCallLogRepository.AddAsync(apiLog);
                        unitOfWork.Complete();
                    }

                    if (context.Request.Method == HttpMethod.Post.ToString())
                    {
                        int apiId = GlobalConstants.GetApiId(context);

                        if ((apiId >= GlobalConstants.API_FILE_INFO && apiId <= GlobalConstants.API_FILE_DELETE_LABEL_PROTECTION) ||
                            (apiId >= GlobalConstants.API_STREAM_INFO || apiId <= GlobalConstants.API_STREAM_DELETE_LABEL_PROTECTION))
                        {
                            TbAipFileJobLog fileJobLog = new TbAipFileJobLog
                            {
                                FileId = guid,

                                FileName = "",
                                FileExt = "",
                                FileOwner = "",
                                FileLabelGuid = "",
                                FileProtectionGuid = "",
                                FileSize = 0,

                                NewFileName = string.Empty,
                                NewFileExt = string.Empty,
                                NewFileOwner = string.Empty,
                                NewFileLabelGuid = string.Empty,
                                NewFileProtectionGuid = string.Empty,
                                NewFileSize = 0,

                                ApiGuid = guid,
                                ApiId = apiId,
                                ServerIpAddr = context.Connection.RemoteIpAddress?.ToString(),

                                JobOwner = string.Empty,
                                ApiKey = string.Empty,
                                DecryptKey = string.Empty,

                                JobResult = 0,
                                JobMessage = string.Empty,

                                JobTime = apiLog.ResponseTime,
                            };

                            RequestBase? req = context.Items[GlobalConstants.API_REQUEST] as RequestBase;
                            if (req != null)
                            {
                                fileJobLog.ApiKey = req.apiKey;
                                fileJobLog.DecryptKey = req.DecryptKey;
                                fileJobLog.JobOwner = req.email;
                            }
                            ResponseBase? res = context.Items[GlobalConstants.API_RESULT] as ResponseBase;
                            if (res != null)
                            {
                                fileJobLog.JobResult = res.errorCode;
                                fileJobLog.JobMessage = res.errorMessage;

                                fileJobLog.FileName = res.dispFileName;
                                fileJobLog.FileOwner = res.FileOwner;
                                fileJobLog.FileExt = res.FileExt;
                                fileJobLog.FileSize = res.FileSize;
                                fileJobLog.FileLabelGuid = res.FileLabelGuid;
                                fileJobLog.FileProtectionGuid = res.FileProtectionGuid;

                                fileJobLog.NewFileName = res.NewFileName;
                                fileJobLog.NewFileOwner = res.NewFileOwner;
                                fileJobLog.NewFileExt = res.NewFileExt;
                                fileJobLog.NewFileSize = res.NewFileSize;
                                fileJobLog.NewFileLabelGuid = res.NewFileLabelGuid;
                                fileJobLog.NewFileProtectionGuid = res.NewFileProtectionGuid;
                            }

                            await unitOfWork.AipFileJobLogRepository.AddAsync(fileJobLog);
                            unitOfWork.Complete();
                        }   // API

                    }   // POST

                    await responseBody.CopyToAsync(originalBodyStream);
                }
            }
            else
            {
                await _next.Invoke(context);
            }
            _log.LogError("RequestResponseLogging.........................................................................");
        }

        private static async Task<TbAipApiCallLog> FormatRequest(HttpContext context)
        {
            context.Request.EnableBuffering();

            var buffer = new byte[Convert.ToInt32(context.Request.ContentLength)];

            await context.Request.Body.ReadAsync(buffer);

            var bodyAsText = Encoding.UTF8.GetString(buffer);
            context.Request.Body.Position = 0;

            var logDto = new TbAipApiCallLog
            {
                RequestAt = DateTime.Now,
                ApiEndPoint = context.Request.Path,
                IPAddress = context.Connection.RemoteIpAddress?.ToString(),
            };
            //context.Items["RequestLog"] = logDto;
            return logDto;
        }

        private static async Task<string> FormatResponse(HttpResponse response)
        {
            response.Body.Seek(0, SeekOrigin.Begin);

            string text = await new StreamReader(response.Body).ReadToEndAsync();

            response.Body.Seek(0, SeekOrigin.Begin);

            return text;
        }
    }


}

