using System;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Haoliang.Core.Services;

namespace Haoliang.Api.Middleware
{
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<LoggingMiddleware> _logger;
        private readonly ILoggingService _loggingService;

        public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger, ILoggingService loggingService)
        {
            _next = next;
            _logger = logger;
            _loggingService = loggingService;
        }

        public async Task Invoke(HttpContext context)
        {
            var originalBodyStream = context.Response.Body;
            
            try
            {
                // Log request
                await LogRequestAsync(context);

                // Capture response
                using (var responseBody = new MemoryStream())
                {
                    context.Response.Body = responseBody;

                    await _next(context);

                    // Log response
                    await LogResponseAsync(context, responseBody);

                    // Copy the response body to the original stream
                    responseBody.Seek(0, SeekOrigin.Begin);
                    await responseBody.CopyToAsync(originalBodyStream);
                }
            }
            catch (Exception ex)
            {
                await _loggingService.LogErrorAsync($"Unhandled exception in logging middleware: {ex.Message}", ex);
                throw;
            }
        }

        private async Task LogRequestAsync(HttpContext context)
        {
            try
            {
                var request = context.Request;
                
                // Don't log request body for sensitive endpoints like login
                var shouldLogBody = !request.Path.ToString().Contains("/login") && 
                                   !request.Path.ToString().Contains("/auth");

                var requestBody = shouldLogBody ? await GetRequestBodyAsync(request) : "[REDACTED]";

                var logData = new
                {
                    Method = request.Method,
                    Path = request.Path,
                    QueryString = request.QueryString.ToString(),
                    Headers = GetSanitizedHeaders(request.Headers),
                    Body = requestBody,
                    UserAgent = request.Headers["User-Agent"].ToString(),
                    RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(),
                    Timestamp = DateTime.Now
                };

                await _loggingService.LogInfoAsync($"Incoming request: {JsonSerializer.Serialize(logData)}");
                _logger.LogInformation("Incoming request: {Request}", JsonSerializer.Serialize(logData));
            }
            catch (Exception ex)
            {
                await _loggingService.LogErrorAsync($"Error logging request: {ex.Message}", ex);
            }
        }

        private async Task LogResponseAsync(HttpContext context, MemoryStream responseBody)
        {
            try
            {
                var response = context.Response;
                var responseBodyContent = await GetResponseBodyAsync(responseBody);

                var logData = new
                {
                    StatusCode = response.StatusCode,
                    Headers = GetSanitizedHeaders(response.Headers),
                    Body = responseBodyContent,
                    Timestamp = DateTime.Now
                };

                await _loggingService.LogInfoAsync($"Outgoing response: {JsonSerializer.Serialize(logData)}");
                _logger.LogInformation("Outgoing response: {Response}", JsonSerializer.Serialize(logData));
            }
            catch (Exception ex)
            {
                await _loggingService.LogErrorAsync($"Error logging response: {ex.Message}", ex);
            }
        }

        private async Task<string> GetRequestBodyAsync(HttpRequest request)
        {
            request.EnableBuffering();
            
            using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
            {
                var body = await reader.ReadToEndAsync();
                request.Body.Seek(0, SeekOrigin.Begin);
                return body;
            }
        }

        private async Task<string> GetResponseBodyAsync(MemoryStream responseBody)
        {
            responseBody.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(responseBody, Encoding.UTF8))
            {
                var body = await reader.ReadToEndAsync();
                return body;
            }
        }

        private object GetSanitizedHeaders(IHeaderDictionary headers)
        {
            var sensitiveHeaders = new[] { "authorization", "cookie", "set-cookie", "password", "token" };
            var sanitizedHeaders = new Dictionary<string, string>();

            foreach (var header in headers)
            {
                if (sensitiveHeaders.Contains(header.Key.ToLower()))
                {
                    sanitizedHeaders[header.Key] = "[REDACTED]";
                }
                else
                {
                    sanitizedHeaders[header.Key] = header.Value.ToString();
                }
            }

            return sanitizedHeaders;
        }
    }

    public static class LoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<LoggingMiddleware>();
        }
    }
}