using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Diagnostics;
using System.Text;

namespace QRRapidoApp.Services.HealthChecks
{
    public class SeqHealthCheck : IHealthCheck
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<SeqHealthCheck> _logger;
        private readonly HttpClient _httpClient;
        
        private readonly int _timeoutSeconds;
        private readonly string _testLogMessage;

        public SeqHealthCheck(
            IConfiguration configuration,
            ILogger<SeqHealthCheck> logger,
            IHttpClientFactory httpClientFactory)
        {
            _configuration = configuration;
            _logger = logger;
            _httpClient = httpClientFactory.CreateClient();
            
            _timeoutSeconds = configuration.GetValue<int>("HealthChecks:Seq:TimeoutSeconds", 3);
            _testLogMessage = configuration.GetValue<string>("HealthChecks:Seq:TestLogMessage", "QRRapido health check test");
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            var stopwatch = Stopwatch.StartNew();
            var data = new Dictionary<string, object>();
            
            var seqUrl = _configuration["Serilog:SeqUrl"];
            if (string.IsNullOrEmpty(seqUrl))
            {
                return HealthCheckResult.Degraded("Seq URL not configured - logging to console only", data: data);
            }
            
            try
            {
                using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(_timeoutSeconds));
                using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
                
                // Test basic connectivity to Seq server
                var pingUrl = $"{seqUrl.TrimEnd('/')}/api";
                var response = await _httpClient.GetAsync(pingUrl, combinedCts.Token);
                
                var latencyMs = stopwatch.ElapsedMilliseconds;
                data["reachable"] = response.IsSuccessStatusCode;
                data["latency"] = $"{latencyMs}ms";
                data["seqUrl"] = seqUrl;
                data["statusCode"] = (int)response.StatusCode;
                
                if (!response.IsSuccessStatusCode)
                {
                    data["error"] = $"HTTP {response.StatusCode}";
                    return HealthCheckResult.Unhealthy($"Seq server not reachable at {seqUrl} (HTTP {response.StatusCode})", data: data);
                }
                
                // Try to send a test log message if we can access the raw events endpoint
                try
                {
                    await SendTestLogAsync(seqUrl, combinedCts.Token);
                    data["lastLog"] = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
                    data["testLogSent"] = true;
                }
                catch (Exception logEx)
                {
                    _logger.LogWarning(logEx, "Failed to send test log to Seq during health check");
                    data["testLogSent"] = false;
                    data["testLogError"] = logEx.Message;
                }
                
                stopwatch.Stop();
                data["totalCheckTimeMs"] = stopwatch.ElapsedMilliseconds;
                
                // Determine health status
                if (latencyMs > 2000)
                {
                    return HealthCheckResult.Degraded($"Seq responding slowly ({latencyMs}ms)", data: data);
                }
                
                return HealthCheckResult.Healthy($"Seq healthy ({latencyMs}ms)", data: data);
            }
            catch (OperationCanceledException)
            {
                data["reachable"] = false;
                data["error"] = "timeout";
                return HealthCheckResult.Unhealthy($"Seq health check timed out after {_timeoutSeconds} seconds", data: data);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Seq health check failed");
                data["reachable"] = false;
                data["error"] = ex.Message;
                return HealthCheckResult.Unhealthy($"Seq health check failed: {ex.Message}", data: data);
            }
        }

        private async Task SendTestLogAsync(string seqUrl, CancellationToken cancellationToken)
        {
            var apiKey = _configuration["Serilog:ApiKey"];
            var eventsUrl = $"{seqUrl.TrimEnd('/')}/api/events/raw";
            
            // Create a simple CLEF (Compact Log Event Format) message
            var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffK");
            var logEntry = $"{{\"@t\":\"{timestamp}\",\"@l\":\"Information\",\"@m\":\"Health check test from QRRapido\",\"ApplicationName\":\"QRRapido\",\"HealthCheck\":true,\"TestMessage\":\"{_testLogMessage}\"}}";
            
            var content = new StringContent(logEntry, Encoding.UTF8, "application/vnd.serilog.clef");
            
            // Add API key if configured
            if (!string.IsNullOrEmpty(apiKey))
            {
                content.Headers.Add("X-Seq-ApiKey", apiKey);
            }
            
            var response = await _httpClient.PostAsync(eventsUrl, content, cancellationToken);
            
            if (!response.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"Failed to send test log to Seq: HTTP {response.StatusCode}");
            }
        }
    }
}