using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Nest;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Text.Json;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace BMA.EHR.Recurit.Exam.Service.Core
{
    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IConfiguration _configuration;

        private string Uri = "";
        private string IndexFormat = "";
        private string SystemName = "";
        private string APIKey = "";

        public RequestLoggingMiddleware(RequestDelegate next, IConfiguration configuration)
        {
            _next = next;
            _configuration = configuration;

            Uri = _configuration["ElasticConfiguration:Uri"] ?? "http://192.168.1.40:9200";
            IndexFormat = _configuration["ElasticConfiguration:IndexFormat"] ?? "bma-ehr-log-index";
            SystemName = _configuration["ElasticConfiguration:SystemName"] ?? "Unknown";
        }

        protected async Task<string> GetExternalAPIAsync(string apiPath, string accessToken, string apiKey)
        {
            try
            {
                using (var client = new HttpClient())
                {
                    // Set timeout to 30 seconds instead of default 100 seconds
                    client.Timeout = TimeSpan.FromSeconds(30);
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
                    client.DefaultRequestHeaders.Add("api_key", apiKey);

                    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
                    var _res = await client.GetAsync(apiPath, cts.Token);
                    if (_res.IsSuccessStatusCode)
                    {
                        var _result = await _res.Content.ReadAsStringAsync();
                        return _result;
                    }
                    return string.Empty;
                }
            }
            catch (TaskCanceledException)
            {
                // Log timeout but don't throw - return empty result instead
                Console.WriteLine($"API call timed out: {apiPath}");
                return string.Empty;
            }
            catch (Exception ex)
            {
                // Log other exceptions but don't throw - return empty result instead  
                Console.WriteLine($"API call failed: {apiPath}, Error: {ex.Message}");
                return string.Empty;
            }
        }

        public async Task<GetProfileByKeycloakIdLocal?> GetProfileByKeycloakIdAsync(Guid keycloakId, string? accessToken)
        {
            try
            {
                var apiPath = $"{_configuration["API"]}/org/dotnet/keycloak/{keycloakId}";
                var apiKey = _configuration["API_KEY"] ?? "";

                var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey);
                if (!string.IsNullOrEmpty(apiResult))
                {
                    var raw = JsonConvert.DeserializeObject<GetProfileByKeycloakIdResultLocal>(apiResult);
                    if (raw != null)
                        return raw.Result;
                }

                return null;
            }
            catch (Exception ex)
            {
                // Log exception but don't throw - return null instead
                Console.WriteLine($"GetProfileByKeycloakIdAsync failed for {keycloakId}: {ex.Message}");
                return null;
            }
        }

        public async Task Invoke(HttpContext context)
        {
            var settings = new ConnectionSettings(new Uri(Uri))
            .DefaultIndex(IndexFormat);

            var client = new ElasticClient(settings);


            var startTime = DateTime.UtcNow;
            var stopwatch = Stopwatch.StartNew();
            string? responseBodyJson = null;
            string? requestBodyJson = null;

            string requestBody = await ReadRequestBodyAsync(context);
            if (requestBody != "")
            {
                if (context.Request.HasFormContentType)
                {
                    var form = await context.Request.ReadFormAsync(); // อ่าน form-data

                    var formData = new Dictionary<string, object>();
                    foreach (var field in form)
                    {
                        formData[field.Key] = field.Value.ToString();
                    }
                    // อ่านไฟล์ที่ถูกส่งมา (ถ้ามี)
                    if (form.Files.Count > 0)
                    {
                        var fileDataList = new List<object>();

                        foreach (var file in form.Files)
                        {
                            fileDataList.Add(new
                            {
                                FileName = file.FileName,
                                ContentType = file.ContentType,
                                Size = file.Length
                            });
                        }

                        formData["Files"] = fileDataList;
                    }

                    requestBodyJson = JsonSerializer.Serialize(formData, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = true, Converters = { new DateTimeFixConverter() } });
                }
                else
                {
                    requestBodyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(requestBody), new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = true, Converters = { new DateTimeFixConverter() } });
                }
            }




            var originalBodyStream = context.Response.Body;


            using (var memoryStream = new MemoryStream())
            {
                // เปลี่ยน stream ของ Response เพื่อให้สามารถอ่านได้
                context.Response.Body = memoryStream;



                var keycloakId = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? Guid.Empty.ToString("D");
                var token = context.Request.Headers["Authorization"];

                var pf = await GetProfileByKeycloakIdAsync(Guid.Parse(keycloakId), token);

                await _next(context); // ดำเนินการต่อไปยัง Middleware อื่น ๆ

                stopwatch.Stop();
                var processTime = stopwatch.ElapsedMilliseconds;
                var endTime = DateTime.UtcNow;

                var logType = context.Response.StatusCode switch
                {
                    >= 500 => "error",
                    >= 400 => "warning",
                    _ => "info"
                };

                string? message = null;

                // อ่านข้อมูลจาก Response หลังจากที่ได้ถูกส่งออกไป
                memoryStream.Seek(0, SeekOrigin.Begin);
                var responseBody = new StreamReader(memoryStream).ReadToEnd();
                //if (responseBody != "")
                //    responseBodyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = true, Converters = { new DateTimeFixConverter() } });

                //var json = JsonSerializer.Deserialize<JsonElement>(responseBody);
                //if (json.ValueKind == JsonValueKind.Array)
                //{
                //    message = "success";
                //}
                //else
                //{
                //    if (json.TryGetProperty("message", out var messageElement))
                //    {
                //        message = messageElement.GetString();
                //    }
                //}

                if (!string.IsNullOrEmpty(responseBody))
                {
                    var contentType = context.Response.ContentType ?? "";

                    if (contentType.Equals(
                        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                        StringComparison.OrdinalIgnoreCase))
                    {
                        // Excel
                        responseBodyJson = $"Excel file (Length={memoryStream.Length} bytes)";
                        message = "success";
                    }
                    else if (contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
                    {
                        // JSON
                        try
                        {
                            responseBodyJson = JsonSerializer.Serialize(
                                JsonSerializer.Deserialize<object>(responseBody),
                                new JsonSerializerOptions
                                {
                                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
                                    WriteIndented = true,
                                    Converters = { new DateTimeFixConverter() }
                                });

                            var json = JsonSerializer.Deserialize<JsonElement>(responseBody);
                            if (json.ValueKind == JsonValueKind.Array)
                                message = "success";
                            else if (json.TryGetProperty("message", out var messageElement))
                                message = messageElement.GetString();
                        }
                        catch
                        {
                            // fallback ถ้า deserialize ไม่ได้
                            responseBodyJson = responseBody;
                            message = "success";
                        }
                    }
                    else
                    {
                        // plain text / HTML / binary อื่น
                        responseBodyJson = responseBody;
                        message = "success";
                    }
                }

                var logData = new
                {
                    logType = logType,
                    ip = context.Connection.RemoteIpAddress?.ToString(),
                    rootId = pf == null ? null : pf.RootId,
                    systemName = SystemName,
                    startTimeStamp = startTime.ToString("o"),
                    endTimeStamp = endTime.ToString("o"),
                    processTime = processTime,
                    host = context.Request.Host.Value,
                    method = context.Request.Method,
                    endpoint = context.Request.Path + context.Request.QueryString,
                    responseCode = context.Response.StatusCode == 304 ? "200" : context.Response.StatusCode.ToString(),
                    responseDescription = message,
                    input = requestBodyJson,
                    output = responseBodyJson,

                    userId = keycloakId,
                    userName = $"{pf?.Prefix ?? ""}{pf?.FirstName ?? ""} {pf?.LastName ?? ""}",
                    user = pf?.CitizenId ?? ""

                };

                // เขียนข้อมูลกลับไปยัง original Response body
                memoryStream.Seek(0, SeekOrigin.Begin);
                await memoryStream.CopyToAsync(originalBodyStream);

                client.IndexDocument(logData);

            }


            //Log.Information("API Request Log: {@LogData}", logData);
        }

        private async Task<string> ReadRequestBodyAsync(HttpContext context)
        {
            context.Request.EnableBuffering();
            using var reader = new StreamReader(context.Request.Body, leaveOpen: true);
            var body = await reader.ReadToEndAsync();
            context.Request.Body.Position = 0;
            return body;
        }
    }

    public class GetProfileByKeycloakIdLocal
    {
        public Guid Id { get; set; }

        public string? Prefix { get; set; }
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
        public string? CitizenId { get; set; }

        public string? Root { get; set; }
        public string? Child1 { get; set; }
        public string? Child2 { get; set; }
        public string? Child3 { get; set; }
        public string? Child4 { get; set; }
        public Guid? RootId { get; set; }
        public Guid? Child1Id { get; set; }
        public Guid? Child2Id { get; set; }
        public Guid? Child3Id { get; set; }
        public Guid? Child4Id { get; set; }
        public Guid? RootDnaId { get; set; }
        public Guid? Child1DnaId { get; set; }
        public Guid? Child2DnaId { get; set; }
        public Guid? Child3DnaId { get; set; }
        public Guid? Child4DnaId { get; set; }
        public double? Amount { get; set; }
        public double? PositionSalaryAmount { get; set; }
        public string? Commander { get; set; }

        public Guid? CommanderId { get; set; }

        public Guid? CommanderKeycloak { get; set; }

    }

    public class GetProfileByKeycloakIdResultLocal
    {
        public string Message { get; set; } = string.Empty;

        public int Status { get; set; } = -1;

        public GetProfileByKeycloakIdLocal? Result { get; set; }
    }
}
