using GmRelay.Web.Health;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Npgsql;
using System.Net;
using System.Text.Json;

namespace GmRelay.Bot.Tests.Web;

public sealed class WebHealthEndpointTests
{
    private static WebApplication CreateTestApp(HealthStatus npgsqlStatus)
    {
        var builder = WebApplication.CreateBuilder();
        builder.WebHost.UseTestServer();
        builder.Services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
            .AddCheck("npgsql", () => new HealthCheckResult(npgsqlStatus));

        var app = builder.Build();
        app.MapHealthChecks("/health", new HealthCheckOptions
        {
            ResponseWriter = async (context, report) =>
            {
                context.Response.ContentType = "application/json";
                var response = new
                {
                    status = report.Status == HealthStatus.Healthy ? "healthy" : "unhealthy",
                    timestamp = DateTimeOffset.UtcNow.ToString("O")
                };
                await context.Response.WriteAsJsonAsync(response);
            }
        });
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });

        return app;
    }

    [Fact]
    public async Task HealthEndpoint_ShouldReturn200AndJson_WhenHealthy()
    {
        await using var app = CreateTestApp(HealthStatus.Healthy);
        await app.StartAsync();
        using var client = app.GetTestClient();

        var response = await client.GetAsync("/health");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var content = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(content);
        Assert.Equal("healthy", doc.RootElement.GetProperty("status").GetString());
        Assert.NotNull(doc.RootElement.GetProperty("timestamp").GetString());
    }

    [Fact]
    public async Task HealthEndpoint_ShouldReturn503_WhenDatabaseUnavailable()
    {
        await using var app = CreateTestApp(HealthStatus.Unhealthy);
        await app.StartAsync();
        using var client = app.GetTestClient();

        var response = await client.GetAsync("/health");

        Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
    }

    [Fact]
    public async Task NpgsqlHealthCheck_ShouldReturnUnhealthy_WhenDatabaseIsInaccessible()
    {
        var dataSource = NpgsqlDataSource.Create("Host=localhost;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=fake");
        var healthCheck = new NpgsqlHealthCheck(dataSource);

        var result = await healthCheck.CheckHealthAsync(new HealthCheckContext());

        Assert.Equal(HealthStatus.Unhealthy, result.Status);
        Assert.NotNull(result.Exception);
    }
}
