using Baya.Infrastructure.Monitoring.HealthChecks;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;

namespace Baya.Infrastructure.Monitoring.Configurations;

/// <summary>
/// Health checks split into <b>liveness</b> (is the process up?) and <b>readiness</b> (can it serve traffic —
/// are its dependencies reachable?), refinement-phase-9 §9.2. An orchestrator restarts on a failed liveness
/// probe but only routes traffic on a passing readiness probe, so a broken log DB or object storage takes the
/// instance out of rotation without a restart loop.
/// <list type="bullet">
///   <item><c>/healthz/live</c> — process only (always healthy while the host runs); never touches a dependency.</item>
///   <item><c>/healthz/ready</c> — the app DB, the log DB (deployed only), and the object-storage write probe.</item>
///   <item><c>/HealthCheck</c> — the aggregate (every check), retained for backward compatibility.</item>
/// </list>
/// </summary>
public static class HealthCheckConfigurations
{
    private const string Ready = "ready";
    private const string Live = "live";

    public static WebApplicationBuilder ConfigureHealthChecks(this WebApplicationBuilder builder)
    {
        var checks = builder.Services.AddHealthChecks();

        // Liveness: the process answers. Deliberately dependency-free so a dependency outage never restarts a
        // healthy instance — that is readiness's job.
        checks.AddCheck("self", () => HealthCheckResult.Healthy(), tags: [Live]);

        // Readiness: the system of record.
        checks.AddSqlServer(builder.Configuration.GetConnectionString("SqlServer")!, name: "sql-app", tags: [Ready]);

        // The Serilog SQL sink only runs in deployed environments (Development/Testing log to console/file), and
        // its connection string is a placeholder there — so only gate readiness on it where it is actually used.
        if (!builder.Environment.IsDevelopment() && !builder.Environment.IsEnvironment("Testing"))
        {
            var logDb = builder.Configuration.GetConnectionString("logDb");
            if (!string.IsNullOrWhiteSpace(logDb))
                checks.AddSqlServer(logDb, name: "sql-logs", tags: [Ready]);
        }

        // Object storage (verification docs, avatars, invoice PDFs) — a real write round-trip.
        checks.AddCheck<ObjectStorageWriteHealthCheck>("object-storage", tags: [Ready]);

        // Redis (ICacheService / IDistributedLock) is single-process today (refinement-phase-7); when it becomes a
        // real external dependency for a multi-instance deployment, add a "redis" check tagged Ready here.

        return builder;
    }

    public static WebApplication UseHealthChecks(this WebApplication app)
    {
        app.MapHealthChecks("/healthz/live", new HealthCheckOptions
        {
            Predicate = check => check.Tags.Contains(Live),
            ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        }).ShortCircuit();

        app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
        {
            Predicate = check => check.Tags.Contains(Ready),
            ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        }).ShortCircuit();

        // Aggregate endpoint (all checks) — retained for backward compatibility with existing probes/dashboards.
        app.MapHealthChecks("/HealthCheck", new HealthCheckOptions
        {
            Predicate = _ => true,
            ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        }).ShortCircuit();

        return app;
    }
}
