using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Diagnostics;
using System.Text.Json;
using System.Threading;

namespace QRRapidoApp.Controllers
{
    [ApiController]
    [Route("health")]
    public class HealthController : ControllerBase
    {
        private readonly HealthCheckService _healthCheckService;
        private readonly IConfiguration _configuration;
        private readonly ILogger<HealthController> _logger;
        private readonly string _applicationName;
        private readonly string _version;
        private static readonly DateTime _startTime = System.Diagnostics.Process.GetCurrentProcess().StartTime.ToUniversalTime();

        public HealthController(
            HealthCheckService healthCheckService,
            IConfiguration configuration,
            ILogger<HealthController> logger)
        {
            _healthCheckService = healthCheckService;
            _configuration = configuration;
            _logger = logger;
            _applicationName = configuration["ApplicationName"] ?? "QRRapido";
            _version = configuration["App:Version"] ?? "1.0.0";
        }

        /// <summary>
        /// Comprehensive health check with detailed information
        /// GET /health/detailed
        /// </summary>
        [HttpGet("detailed")]
        public async Task<IActionResult> GetDetailedHealth()
        {
            try
            {
                var stopwatch = Stopwatch.StartNew();
                var healthReport = await _healthCheckService.CheckHealthAsync();
                stopwatch.Stop();

                var uptime = DateTime.UtcNow - _startTime;
                var overallStatus = DetermineOverallStatus(healthReport.Status);

                var response = new
                {
                    applicationName = _applicationName,
                    status = overallStatus,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    uptime = $"{uptime.Days}d {uptime.Hours}h {uptime.Minutes}m",
                    totalDuration = $"{stopwatch.ElapsedMilliseconds}ms",
                    checks = new
                    {
                        mongodb = ExtractCheckResult(healthReport, "mongodb"),
                        resources = ExtractCheckResult(healthReport, "resources"),
                        externalServices = ExtractCheckResult(healthReport, "external_services")
                    },
                    version = _version,
                    environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"
                };

                var statusCode = overallStatus switch
                {
                    "unhealthy" => 503,
                    "degraded" => 200, // Still return 200 for degraded to avoid false alarms
                    _ => 200
                };

                _logger.LogInformation("Detailed health check completed - Status: {Status}, Duration: {Duration}ms", 
                    overallStatus, stopwatch.ElapsedMilliseconds);

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Detailed health check failed");
                return StatusCode(503, new
                {
                    applicationName = _applicationName,
                    status = "unhealthy",
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    error = "Health check system failure",
                    version = _version
                });
            }
        }

        /// <summary>
        /// MongoDB-specific health check
        /// GET /health/mongodb
        /// </summary>
        [HttpGet("mongodb")]
        public async Task<IActionResult> GetMongoDbHealth()
        {
            try
            {
                var healthReport = await _healthCheckService.CheckHealthAsync(check => check.Name == "mongodb");
                var mongoCheck = healthReport.Entries.FirstOrDefault(e => e.Key == "mongodb");
                
                if (mongoCheck.Key == null)
                {
                    return StatusCode(503, new { status = "unhealthy", error = "MongoDB health check not found" });
                }

                var statusCode = mongoCheck.Value.Status == HealthStatus.Healthy ? 200 : 503;
                var response = ExtractCheckResult(healthReport, "mongodb");

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "MongoDB health check failed");
                return StatusCode(503, new { status = "unhealthy", error = ex.Message });
            }
        }

        /// <summary>
        /// System resources health check
        /// GET /health/resources
        /// </summary>
        [HttpGet("resources")]
        public async Task<IActionResult> GetResourcesHealth()
        {
            try
            {
                var healthReport = await _healthCheckService.CheckHealthAsync(check => check.Name == "resources");
                var resourceCheck = healthReport.Entries.FirstOrDefault(e => e.Key == "resources");
                
                if (resourceCheck.Key == null)
                {
                    return StatusCode(503, new { status = "unhealthy", error = "Resources health check not found" });
                }

                var statusCode = resourceCheck.Value.Status == HealthStatus.Unhealthy ? 503 : 200;
                var response = ExtractCheckResult(healthReport, "resources");

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Resources health check failed");
                return StatusCode(503, new { status = "unhealthy", error = ex.Message });
            }
        }

        /// <summary>
        /// External services health check
        /// GET /health/external
        /// </summary>
        [HttpGet("external")]
        public async Task<IActionResult> GetExternalServicesHealth()
        {
            try
            {
                var healthReport = await _healthCheckService.CheckHealthAsync(check => check.Name == "external_services");
                var externalCheck = healthReport.Entries.FirstOrDefault(e => e.Key == "external_services");
                
                if (externalCheck.Key == null)
                {
                    return StatusCode(503, new { status = "unhealthy", error = "External services health check not found" });
                }

                var statusCode = externalCheck.Value.Status == HealthStatus.Unhealthy ? 503 : 200;
                var response = ExtractCheckResult(healthReport, "external_services");

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "External services health check failed");
                return StatusCode(503, new { status = "unhealthy", error = ex.Message });
            }
        }

        /// <summary>
        /// Simple health check - just overall status
        /// GET /health/simple or GET /health
        /// </summary>
        [HttpGet("simple")]
        [HttpGet("")]
        public async Task<IActionResult> GetSimpleHealth()
        {
            try
            {
                var healthReport = await _healthCheckService.CheckHealthAsync();
                var overallStatus = DetermineOverallStatus(healthReport.Status);
                
                var statusCode = overallStatus switch
                {
                    "unhealthy" => 503,
                    _ => 200
                };

                var response = new
                {
                    status = overallStatus,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
                };

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Simple health check failed");
                return StatusCode(503, new
                {
                    status = "unhealthy",
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    error = ex.Message
                });
            }
        }

        /// <summary>
        /// Health check for Uptime Kuma monitoring
        /// GET /health/uptime-kuma
        /// </summary>
        [HttpGet("uptime-kuma")]
        public async Task<IActionResult> GetUptimeKumaHealth()
        {
            try
            {
                var healthReport = await _healthCheckService.CheckHealthAsync();
                var overallStatus = DetermineOverallStatus(healthReport.Status);
                
                // For Uptime Kuma, we want to return 200 OK for healthy/degraded and 503 for unhealthy
                var statusCode = overallStatus == "unhealthy" ? 503 : 200;
                
                var uptimeMinutes = (DateTime.UtcNow - _startTime).TotalMinutes;
                var memoryBytes = GC.GetTotalMemory(false);
                var memoryMB = memoryBytes / 1024.0 / 1024.0;

                var response = new
                {
                    status = overallStatus,
                    application = _applicationName,
                    version = _version,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    uptime = System.FormattableString.Invariant($"{(DateTime.UtcNow - _startTime).TotalHours:F1}h"),
                    // Include critical metrics for monitoring
                    metrics = new
                    {
                        mongodb_connected = GetCheckStatus(healthReport, "mongodb") == "healthy",
                        resources_ok = GetCheckStatus(healthReport, "resources") == "healthy",
                        external_services_ok = GetCheckStatus(healthReport, "external_services") == "healthy"
                    }
                };

                _logger.LogInformation("[HEALTH] Memory: {memoryBytes} MemoryMB: {memoryMB} ThreadPool: {threadPool} ProcessId: {processId} ActiveConnections: {activeConnections} UptimeMinutes: {uptimeMinutes}",
                    memoryBytes,
                    memoryMB,
                    ThreadPool.ThreadCount,
                    Process.GetCurrentProcess().Id,
                    HttpContext.Connection?.Id ?? "null",
                    Math.Round(uptimeMinutes, 1));

                return StatusCode(statusCode, response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Uptime Kuma health check failed");
                return StatusCode(503, new
                {
                    status = "unhealthy",
                    application = _applicationName,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    error = "Health check system failure"
                });
            }
        }

        private object ExtractCheckResult(HealthReport healthReport, string checkName)
        {
            if (!healthReport.Entries.TryGetValue(checkName, out var entry))
            {
                return new
                {
                    status = "not_configured",
                    error = $"Health check '{checkName}' not found"
                };
            }

            var baseResult = new Dictionary<string, object>
            {
                ["status"] = entry.Status.ToString().ToLower(),
                ["duration"] = $"{entry.Duration.TotalMilliseconds}ms",
                ["description"] = entry.Description ?? ""
            };

            // Add all custom data from the health check
            if (entry.Data != null)
            {
                foreach (var kvp in entry.Data)
                {
                    baseResult[kvp.Key] = kvp.Value;
                }
            }

            if (entry.Exception != null)
            {
                baseResult["error"] = entry.Exception.Message;
            }

            return baseResult;
        }

        private string DetermineOverallStatus(HealthStatus healthStatus)
        {
            return healthStatus switch
            {
                HealthStatus.Healthy => "healthy",
                HealthStatus.Degraded => "degraded",
                HealthStatus.Unhealthy => "unhealthy",
                _ => "unknown"
            };
        }

        private string GetCheckStatus(HealthReport healthReport, string checkName)
        {
            if (healthReport.Entries.TryGetValue(checkName, out var entry))
            {
                return entry.Status.ToString().ToLower();
            }
            return "not_configured";
        }
    }
}