using Application;
using Application.Abstractions.Chat;
using Application.Abstractions.Crypto;
using Infrastructure;
using SharedKernel;
using System.Reflection;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Hosting.WindowsServices;
using Serilog;
using Web.Api;
using Web.Api.Common;
using Web.Api.Extensions;
using Web.Api.Hubs;
using Web.Api.Services;

var builder = WebApplication.CreateBuilder(args);
var settings = builder.Configuration.Get<AppSettings>()!;

if (!WindowsServiceHelpers.IsWindowsService())
{
    Console.Title = settings.App.Name;
}
Console.WriteLine($"ENV={builder.Environment.EnvironmentName}");
Console.WriteLine($"현재 시간: {DateTime.Now} / {TimeZoneInfo.Local.Id}");

builder.Host.UseWindowsService();

// Serilog — Production에서만 파일 로그
if (builder.Environment.IsProduction())
{
    Log.Logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .WriteTo.File("logs/webapi-.log",
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 30,
            outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
        .CreateLogger();
    builder.Host.UseSerilog();
}
builder.Services.Configure<AppSettings>(builder.Configuration);

builder.Services.AddApiForwardedHeaders(settings);   // Cloudflare → IIS reverse proxy 헤더 신뢰

builder.Services
    .AddApplication()
    .AddPresentation()
    .AddApiInfrastructure(builder.Configuration);

// 모든 DateTime 응답에 UTC(Z) 마커 강제 (SQL datetime2 + EF Kind=Unspecified → JS 로컬시간 오해 방지)
builder.Services.ConfigureHttpJsonOptions(opts =>
{
    opts.SerializerOptions.Converters.Add(new UtcDateTimeConverter());
    opts.SerializerOptions.Converters.Add(new UtcNullableDateTimeConverter());
});

// CORS
builder.Services.AddCors(options =>
{
    options.AddPolicy(settings.CorsPolicy.Name, policy =>
    {
        policy
            .WithOrigins(settings.CorsPolicy.AllowedOrigins.ToArray())
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials()
            .SetPreflightMaxAge(TimeSpan.FromSeconds(settings.CorsPolicy.PreflightMaxAgeSeconds));
    });
});

// SignalR
builder.Services.AddSignalR();
builder.Services.AddSingleton<ICryptoHubService, CryptoHubService>();

// Chat
builder.Services.AddSingleton<IChatHubService, ChatHubService>();
builder.Services.AddHostedService<KickSubscriberService>();

// Response 압축 (Brotli/Gzip)
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

// Output cache 정책 (endpoint 별 .CacheOutput("PublicShort") opt-in)
builder.Services.AddOutputCache(options =>
{
    options.AddPolicy("PublicShort", b => b.Cache().Expire(TimeSpan.FromSeconds(30)).SetVaryByQuery("*"));
});

// Rate limiter (endpoint 별 .RequireRateLimiting("api") opt-in)
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api", w =>
    {
        w.PermitLimit = 100;
        w.Window = TimeSpan.FromMinutes(1);
        w.QueueLimit = 0;
    });
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});

// Endpoints
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
builder.Logging.AddConsole();

/**
 * =======================================================================================================================================================
 */

var app = builder.Build();

/**
 * =======================================================================================================================================================
 */

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 상태 확인\
app.MapHealthChecks("/health");

// ForwardedHeaders 는 다른 미들웨어보다 먼저 — Exception/Cors/Auth 가 client IP 를 알아야 정확.
app.UseForwardedHeaders();

app.UseExceptionHandler();
app.UseResponseCompression();
app.UseCors(settings.CorsPolicy.Name);
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseOutputCache();
app.UseRateLimiter();
app.MapEndpoints();
app.MapHub<CryptoHub>("/hubs/crypto");
app.MapHub<ChatHub>("/hubs/chat");

// 서버 시작 시 이전 채팅 접속자 정보 초기화 (stale 연결 제거)
using (var scope = app.Services.CreateScope())
{
    await scope.ServiceProvider.GetRequiredService<IChatConnectionTracker>().ClearAllAsync();
}

app.Run();