using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Tiku.Api.Contracts;
using Tiku.Application.Security;
using Tiku.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using MassTransit.EntityFrameworkCoreIntegration;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Tiku.Infrastructure.Messaging;

namespace Tiku.Api.Controllers;

[ApiController]
[AllowAnonymous]
[Produces("application/json")]
[Route("api/health")]
public sealed class HealthController(
    TikuDbContext dbContext,
    IRedisSecurityStore redisSecurityStore,
    MessagingOptions messagingOptions,
    HealthCheckService healthCheckService) : ControllerBase
{
    [HttpGet]
    [EndpointSummary("健康检查")]
    [EndpointDescription("用于本地、CI 和部署环境 smoke test 的轻量健康检查。")]
    [ProducesResponseType<HealthResponseDto>(StatusCodes.Status200OK)]
    public ActionResult<HealthResponseDto> Get()
    {
        return Ok(new HealthResponseDto(
            "ok",
            "tiku-api",
            DateTimeOffset.UtcNow));
    }

    [HttpGet("ready")]
    [EndpointSummary("依赖就绪检查")]
    public async Task<ActionResult<object>> Ready(CancellationToken cancellationToken)
    {
        var database = await dbContext.Database.CanConnectAsync(cancellationToken);
        var redis = !redisSecurityStore.IsConfigured || await redisSecurityStore.PingAsync(cancellationToken);
        var rabbitHealth = await healthCheckService.CheckHealthAsync(
            registration => registration.Tags.Contains("ready"),
            cancellationToken);
        var rabbitMq = !messagingOptions.IsConfigured || rabbitHealth.Status == HealthStatus.Healthy;
        var outboxPending = database
            ? await dbContext.Set<OutboxMessage>().CountAsync(cancellationToken)
            : -1;
        var outboxOldestSentTime = database
            ? await dbContext.Set<OutboxMessage>()
                .Select(message => (DateTime?)message.SentTime)
                .MinAsync(cancellationToken)
            : null;
        var outboxOldestAgeSeconds = outboxOldestSentTime is null
            ? 0
            : Math.Max(0, (DateTimeOffset.UtcNow - new DateTimeOffset(outboxOldestSentTime.Value)).TotalSeconds);
        var outboxAlert = outboxPending >= messagingOptions.OutboxBacklogAlertCount ||
                          outboxOldestAgeSeconds >= messagingOptions.OutboxOldestMessageAlertSeconds;
        var ready = database && redis && rabbitMq;
        var response = new
        {
            status = ready ? "ready" : "not_ready",
            database,
            redis = new { configured = redisSecurityStore.IsConfigured, ready = redis },
            rabbitMq = new { configured = messagingOptions.IsConfigured, ready = rabbitMq },
            outbox = new
            {
                pending = outboxPending,
                oldestAgeSeconds = Math.Round(outboxOldestAgeSeconds, 1),
                alert = outboxAlert,
                backlogAlertCount = messagingOptions.OutboxBacklogAlertCount,
                oldestMessageAlertSeconds = messagingOptions.OutboxOldestMessageAlertSeconds
            },
            checkedAt = DateTimeOffset.UtcNow
        };
        return ready ? Ok(response) : StatusCode(StatusCodes.Status503ServiceUnavailable, response);
    }
}
