using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.Model;
using Serilog;
using Serilog.Context;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Formatting.Compact;
using System;
using System.Diagnostics;
using System.IO;

namespace Mirea.Api.Endpoint.Configuration.Core.Startup;

public static class LoggerConfiguration
{
    public static IHostBuilder AddCustomSerilog(this IHostBuilder hostBuilder)
    {
        return hostBuilder.UseSerilog((context, _, configuration) =>
        {
            var generalConfig = context.Configuration.Get<GeneralConfig>()?.LogSettings;
            configuration
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .Enrich.FromLogContext()
                .WriteTo.Console(
                    outputTemplate:
                    "[{Level:u3}] [{Timestamp:dd.MM.yyyy HH:mm:ss}] {Message:lj}{NewLine}{Exception}");

            if (generalConfig?.EnableLogToFile == true)
            {
                generalConfig.LogFilePath = PathBuilder.Combine(generalConfig.LogFilePath ?? string.Empty);

                if (!string.IsNullOrEmpty(generalConfig.LogFilePath) && Directory.Exists(generalConfig.LogFilePath))
                    Directory.CreateDirectory(generalConfig.LogFilePath);

                configuration.WriteTo.File(
                    new CompactJsonFormatter(),
                    PathBuilder.Combine(
                        generalConfig.LogFilePath!,
                        generalConfig.LogFileName + ".json"
                    ),
                    LogEventLevel.Debug,
                    rollingInterval: RollingInterval.Day);
            }

            if (generalConfig != null && !string.IsNullOrEmpty(generalConfig.ApiServerSeq) &&
                Uri.TryCreate(generalConfig.ApiServerSeq, UriKind.Absolute, out var _))
                configuration.WriteTo.Seq(generalConfig.ApiServerSeq, apiKey: generalConfig.ApiKeySeq);

            configuration
                .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Authorization", LogEventLevel.Warning);

            configuration.Filter.ByExcluding(Matching.WithProperty<string>("SourceContext", sc =>
                sc.Contains("Microsoft.EntityFrameworkCore.Database.Command")));
        });
    }

    public static IApplicationBuilder UseCustomSerilog(this IApplicationBuilder app)
    {
        return app.Use(async (context, next) =>
        {
            var traceId = Activity.Current?.Id ?? context.TraceIdentifier;

            using (LogContext.PushProperty("TraceId", traceId))
            using (LogContext.PushProperty("UserAgent", context.Request.Headers.UserAgent.ToString()))
            using (LogContext.PushProperty("RemoteIPAddress", context.Connection.RemoteIpAddress?.ToString()))
            {
                await next();
            }
        }).UseSerilogRequestLogging(options =>
        {
            options.MessageTemplate = "[{RequestMethod}] {RequestPath} [Client {RemoteIPAddress}] [{StatusCode}] in {Elapsed:0.0000} ms";

            options.GetLevel = (httpContext, elapsed, ex) =>
            {
                if (httpContext.Request.Path.StartsWithSegments("/health"))
                    return LogEventLevel.Verbose;

                return elapsed >= 2500 || ex != null
                    ? LogEventLevel.Warning
                    : elapsed >= 1000
                        ? LogEventLevel.Information
                        : LogEventLevel.Debug;
            };

            options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
                {
                    diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
                    diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
                    diagnosticContext.Set("UserAgent", httpContext.Request.Headers.UserAgent);
                    diagnosticContext.Set("RemoteIPAddress", httpContext.Connection.RemoteIpAddress?.ToString() ?? string.Empty);
                };
        });
    }
}