using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace NATS.Server.Monitoring;

/// <summary>
/// HTTP monitoring server providing /healthz, /varz, and other monitoring endpoints.
/// Corresponds to Go server/monitor.go HTTP server setup.
/// </summary>
public sealed class MonitorServer : IAsyncDisposable
{
    private readonly WebApplication _app;
    private readonly ILogger<MonitorServer> _logger;
    private readonly VarzHandler _varzHandler;
    private readonly ConnzHandler _connzHandler;
    private readonly SubszHandler _subszHandler;
    private readonly JszHandler _jszHandler;
    private readonly RoutezHandler _routezHandler;
    private readonly GatewayzHandler _gatewayzHandler;
    private readonly LeafzHandler _leafzHandler;
    private readonly AccountzHandler _accountzHandler;
    private readonly PprofHandler _pprofHandler;

    public MonitorServer(NatsServer server, NatsOptions options, ServerStats stats, ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<MonitorServer>();

        var builder = WebApplication.CreateSlimBuilder();
        builder.WebHost.UseUrls($"http://{options.MonitorHost}:{options.MonitorPort}");
        builder.Logging.ClearProviders();

        _app = builder.Build();
        var basePath = options.MonitorBasePath ?? "";

        _varzHandler = new VarzHandler(server, options);
        _connzHandler = new ConnzHandler(server);
        _subszHandler = new SubszHandler(server);
        _jszHandler = new JszHandler(server, options);
        _routezHandler = new RoutezHandler(server);
        _gatewayzHandler = new GatewayzHandler(server);
        _leafzHandler = new LeafzHandler(server);
        _accountzHandler = new AccountzHandler(server);
        _pprofHandler = new PprofHandler();

        _app.MapGet(basePath + "/", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/", 1, (_, v) => v + 1);
            return Results.Ok(new
            {
                endpoints = new[]
                {
                    "/varz", "/connz", "/healthz", "/routez",
                    "/gatewayz", "/leafz", "/subz", "/accountz", "/jsz",
                },
            });
        });
        _app.MapGet(basePath + "/healthz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/healthz", 1, (_, v) => v + 1);
            return Results.Ok("ok");
        });
        _app.MapGet(basePath + "/varz", async (HttpContext ctx) =>
        {
            stats.HttpReqStats.AddOrUpdate("/varz", 1, (_, v) => v + 1);
            return Results.Ok(await _varzHandler.HandleVarzAsync(ctx.RequestAborted));
        });

        _app.MapGet(basePath + "/connz", (HttpContext ctx) =>
        {
            stats.HttpReqStats.AddOrUpdate("/connz", 1, (_, v) => v + 1);
            return Results.Ok(_connzHandler.HandleConnz(ctx));
        });

        _app.MapGet(basePath + "/routez", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/routez", 1, (_, v) => v + 1);
            return Results.Ok(_routezHandler.Build());
        });
        _app.MapGet(basePath + "/gatewayz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/gatewayz", 1, (_, v) => v + 1);
            return Results.Ok(_gatewayzHandler.Build());
        });
        _app.MapGet(basePath + "/leafz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/leafz", 1, (_, v) => v + 1);
            return Results.Ok(_leafzHandler.Build());
        });
        _app.MapGet(basePath + "/subz", (HttpContext ctx) =>
        {
            stats.HttpReqStats.AddOrUpdate("/subz", 1, (_, v) => v + 1);
            return Results.Ok(_subszHandler.HandleSubsz(ctx));
        });
        _app.MapGet(basePath + "/subscriptionsz", (HttpContext ctx) =>
        {
            stats.HttpReqStats.AddOrUpdate("/subscriptionsz", 1, (_, v) => v + 1);
            return Results.Ok(_subszHandler.HandleSubsz(ctx));
        });
        _app.MapGet(basePath + "/accountz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/accountz", 1, (_, v) => v + 1);
            return Results.Ok(_accountzHandler.Build());
        });
        _app.MapGet(basePath + "/accstatz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/accstatz", 1, (_, v) => v + 1);
            return Results.Ok(_accountzHandler.BuildStats());
        });
        _app.MapGet(basePath + "/jsz", () =>
        {
            stats.HttpReqStats.AddOrUpdate("/jsz", 1, (_, v) => v + 1);
            return Results.Ok(_jszHandler.Build());
        });

        if (options.ProfPort > 0)
        {
            _app.MapGet(basePath + "/debug/pprof", () =>
            {
                stats.HttpReqStats.AddOrUpdate("/debug/pprof", 1, (_, v) => v + 1);
                return Results.Text(_pprofHandler.Index(), "text/plain");
            });

            _app.MapGet(basePath + "/debug/pprof/profile", (HttpContext ctx) =>
            {
                stats.HttpReqStats.AddOrUpdate("/debug/pprof/profile", 1, (_, v) => v + 1);
                var seconds = 30;
                if (ctx.Request.Query.TryGetValue("seconds", out var values)
                    && int.TryParse(values.ToString(), out var parsed))
                {
                    seconds = parsed;
                }

                return Results.File(_pprofHandler.CaptureCpuProfile(seconds), "application/json");
            });
        }
    }

    public async Task StartAsync(CancellationToken ct)
    {
        await _app.StartAsync(ct);
        _logger.LogInformation("Monitoring listening on {Urls}", string.Join(", ", _app.Urls));
    }

    public async ValueTask DisposeAsync()
    {
        await _app.StopAsync();
        await _app.DisposeAsync();
        _varzHandler.Dispose();
    }
}
