using System.Diagnostics;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using StellaOps.Scanner.WebService.Diagnostics;
using StellaOps.Scanner.WebService.Options;

namespace StellaOps.Scanner.WebService.Endpoints;

internal static class HealthEndpoints
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);

    public static void MapHealthEndpoints(this IEndpointRouteBuilder endpoints)
    {
        ArgumentNullException.ThrowIfNull(endpoints);

        var group = endpoints.MapGroup("/");
        group.MapGet("/healthz", HandleHealth)
            .WithName("scanner.health")
            .Produces<HealthDocument>(StatusCodes.Status200OK)
            .AllowAnonymous();

        group.MapGet("/readyz", HandleReady)
            .WithName("scanner.ready")
            .Produces<ReadyDocument>(StatusCodes.Status200OK)
            .AllowAnonymous();
    }

    private static IResult HandleHealth(
        ServiceStatus status,
        IOptions<ScannerWebServiceOptions> options,
        HttpContext context)
    {
        ApplyNoCache(context.Response);

        var snapshot = status.CreateSnapshot();
        var uptimeSeconds = Math.Max((snapshot.CapturedAt - snapshot.StartedAt).TotalSeconds, 0d);

        var telemetry = new TelemetrySnapshot(
            Enabled: options.Value.Telemetry.Enabled,
            Logging: options.Value.Telemetry.EnableLogging,
            Metrics: options.Value.Telemetry.EnableMetrics,
            Tracing: options.Value.Telemetry.EnableTracing);

        var document = new HealthDocument(
            Status: "healthy",
            StartedAt: snapshot.StartedAt,
            CapturedAt: snapshot.CapturedAt,
            UptimeSeconds: uptimeSeconds,
            Telemetry: telemetry);

        return Json(document, StatusCodes.Status200OK);
    }

    private static async Task<IResult> HandleReady(
        ServiceStatus status,
        HttpContext context,
        CancellationToken cancellationToken)
    {
        ApplyNoCache(context.Response);

        await Task.CompletedTask;

        status.RecordReadyCheck(success: true, latency: TimeSpan.Zero, error: null);
        var snapshot = status.CreateSnapshot();
        var ready = snapshot.Ready;

        var document = new ReadyDocument(
            Status: ready.IsReady ? "ready" : "unready",
            CheckedAt: ready.CheckedAt,
            LatencyMs: ready.Latency?.TotalMilliseconds,
            Error: ready.Error);

        return Json(document, StatusCodes.Status200OK);
    }

    private static void ApplyNoCache(HttpResponse response)
    {
        response.Headers.CacheControl = "no-store, no-cache, max-age=0, must-revalidate";
        response.Headers.Pragma = "no-cache";
        response.Headers["Expires"] = "0";
    }

    private static IResult Json<T>(T value, int statusCode)
    {
        var payload = JsonSerializer.Serialize(value, JsonOptions);
        return Results.Content(payload, "application/json", Encoding.UTF8, statusCode);
    }

    internal sealed record TelemetrySnapshot(
        bool Enabled,
        bool Logging,
        bool Metrics,
        bool Tracing);

    internal sealed record HealthDocument(
        string Status,
        DateTimeOffset StartedAt,
        DateTimeOffset CapturedAt,
        double UptimeSeconds,
        TelemetrySnapshot Telemetry);

    internal sealed record ReadyDocument(
        string Status,
        DateTimeOffset CheckedAt,
        double? LatencyMs,
        string? Error);
}
