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

namespace QRRapidoApp.Services.HealthChecks
{
    public class ExternalServicesHealthCheck : IHealthCheck
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<ExternalServicesHealthCheck> _logger;
        private readonly HttpClient _httpClient;
        
        private readonly int _timeoutSeconds;
        private readonly bool _testStripeConnection;
        private readonly bool _testGoogleAuth;
        private readonly bool _testMicrosoftAuth;

        public ExternalServicesHealthCheck(
            IConfiguration configuration,
            ILogger<ExternalServicesHealthCheck> logger,
            IHttpClientFactory httpClientFactory)
        {
            _configuration = configuration;
            _logger = logger;
            _httpClient = httpClientFactory.CreateClient();
            
            _timeoutSeconds = configuration.GetValue<int>("HealthChecks:ExternalServices:TimeoutSeconds", 10);
            _testStripeConnection = configuration.GetValue<bool>("HealthChecks:ExternalServices:TestStripeConnection", true);
            _testGoogleAuth = configuration.GetValue<bool>("HealthChecks:ExternalServices:TestGoogleAuth", false);
            _testMicrosoftAuth = configuration.GetValue<bool>("HealthChecks:ExternalServices:TestMicrosoftAuth", false);
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            var stopwatch = Stopwatch.StartNew();
            var data = new Dictionary<string, object>();
            var services = new List<object>();
            var issues = new List<string>();
            var warnings = new List<string>();
            
            try
            {
                using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(_timeoutSeconds));
                using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
                
                // Test Stripe connection
                if (_testStripeConnection)
                {
                    var stripeResult = await TestStripeConnectionAsync(combinedCts.Token);
                    services.Add(stripeResult);
                    
                    if (stripeResult.GetProperty("status").GetString() == "error")
                    {
                        issues.Add($"Stripe: {stripeResult.GetProperty("error").GetString()}");
                    }
                    else if (stripeResult.GetProperty("status").GetString() == "warning")
                    {
                        warnings.Add($"Stripe: slow response ({stripeResult.GetProperty("latencyMs").GetInt32()}ms)");
                    }
                }
                
                // Test Google OAuth endpoints
                if (_testGoogleAuth)
                {
                    var googleResult = await TestGoogleAuthAsync(combinedCts.Token);
                    services.Add(googleResult);
                    
                    if (googleResult.GetProperty("status").GetString() == "error")
                    {
                        warnings.Add($"Google Auth: {googleResult.GetProperty("error").GetString()}");
                    }
                }
                
                // Test Microsoft OAuth endpoints
                if (_testMicrosoftAuth)
                {
                    var microsoftResult = await TestMicrosoftAuthAsync(combinedCts.Token);
                    services.Add(microsoftResult);
                    
                    if (microsoftResult.GetProperty("status").GetString() == "error")
                    {
                        warnings.Add($"Microsoft Auth: {microsoftResult.GetProperty("error").GetString()}");
                    }
                }
                
                stopwatch.Stop();
                
                data["services"] = services;
                data["totalCheckTimeMs"] = stopwatch.ElapsedMilliseconds;
                data["status"] = DetermineOverallStatus(issues.Count, warnings.Count);
                
                // Return appropriate health status
                if (issues.Any())
                {
                    return HealthCheckResult.Unhealthy($"External service issues: {string.Join(", ", issues)}", data: data);
                }
                
                if (warnings.Any())
                {
                    return HealthCheckResult.Degraded($"External service warnings: {string.Join(", ", warnings)}", data: data);
                }
                
                return HealthCheckResult.Healthy($"All external services healthy ({services.Count} services checked)", data: data);
            }
            catch (OperationCanceledException)
            {
                return HealthCheckResult.Unhealthy($"External services health check timed out after {_timeoutSeconds} seconds", data: data);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "External services health check failed");
                return HealthCheckResult.Unhealthy($"External services health check failed: {ex.Message}", data: data);
            }
        }

        private async Task<JsonElement> TestStripeConnectionAsync(CancellationToken cancellationToken)
        {
            var serviceStopwatch = Stopwatch.StartNew();
            
            try
            {
                var secretKey = _configuration["Stripe:SecretKey"];
                if (string.IsNullOrEmpty(secretKey) || secretKey.StartsWith("sk_test_xxxxx"))
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "stripe",
                        status = "warning",
                        error = "Stripe not configured",
                        latencyMs = serviceStopwatch.ElapsedMilliseconds
                    })).RootElement;
                }
                
                // Test Stripe API connectivity by hitting a simple endpoint
                var request = new HttpRequestMessage(HttpMethod.Get, "https://api.stripe.com/v1/account");
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", secretKey);
                
                var response = await _httpClient.SendAsync(request, cancellationToken);
                serviceStopwatch.Stop();
                
                var latencyMs = serviceStopwatch.ElapsedMilliseconds;
                
                if (response.IsSuccessStatusCode)
                {
                    var status = latencyMs > 2000 ? "warning" : "ok";
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "stripe",
                        status = status,
                        statusCode = (int)response.StatusCode,
                        latencyMs = latencyMs,
                        lastChecked = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
                    })).RootElement;
                }
                else
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "stripe",
                        status = "error",
                        statusCode = (int)response.StatusCode,
                        error = $"HTTP {response.StatusCode}",
                        latencyMs = latencyMs
                    })).RootElement;
                }
            }
            catch (Exception ex)
            {
                serviceStopwatch.Stop();
                _logger.LogWarning(ex, "Failed to test Stripe connection");
                
                return JsonDocument.Parse(JsonSerializer.Serialize(new
                {
                    service = "stripe",
                    status = "error",
                    error = ex.Message,
                    latencyMs = serviceStopwatch.ElapsedMilliseconds
                })).RootElement;
            }
        }

        private async Task<JsonElement> TestGoogleAuthAsync(CancellationToken cancellationToken)
        {
            var serviceStopwatch = Stopwatch.StartNew();
            
            try
            {
                var clientId = _configuration["Authentication:Google:ClientId"];
                if (string.IsNullOrEmpty(clientId) || clientId == "your-google-client-id")
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "google_auth",
                        status = "warning",
                        error = "Google Auth not configured",
                        latencyMs = serviceStopwatch.ElapsedMilliseconds
                    })).RootElement;
                }
                
                // Test Google's OAuth discovery document
                var response = await _httpClient.GetAsync("https://accounts.google.com/.well-known/openid_configuration", cancellationToken);
                serviceStopwatch.Stop();
                
                var latencyMs = serviceStopwatch.ElapsedMilliseconds;
                
                if (response.IsSuccessStatusCode)
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "google_auth",
                        status = "ok",
                        statusCode = (int)response.StatusCode,
                        latencyMs = latencyMs,
                        lastChecked = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
                    })).RootElement;
                }
                else
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "google_auth",
                        status = "error",
                        statusCode = (int)response.StatusCode,
                        error = $"HTTP {response.StatusCode}",
                        latencyMs = latencyMs
                    })).RootElement;
                }
            }
            catch (Exception ex)
            {
                serviceStopwatch.Stop();
                _logger.LogWarning(ex, "Failed to test Google Auth connection");
                
                return JsonDocument.Parse(JsonSerializer.Serialize(new
                {
                    service = "google_auth",
                    status = "error",
                    error = ex.Message,
                    latencyMs = serviceStopwatch.ElapsedMilliseconds
                })).RootElement;
            }
        }

        private async Task<JsonElement> TestMicrosoftAuthAsync(CancellationToken cancellationToken)
        {
            var serviceStopwatch = Stopwatch.StartNew();
            
            try
            {
                var clientId = _configuration["Authentication:Microsoft:ClientId"];
                if (string.IsNullOrEmpty(clientId) || clientId == "your-microsoft-client-id")
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "microsoft_auth",
                        status = "warning",
                        error = "Microsoft Auth not configured",
                        latencyMs = serviceStopwatch.ElapsedMilliseconds
                    })).RootElement;
                }
                
                // Test Microsoft's OAuth discovery document
                var response = await _httpClient.GetAsync("https://login.microsoftonline.com/common/v2.0/.well-known/openid_configuration", cancellationToken);
                serviceStopwatch.Stop();
                
                var latencyMs = serviceStopwatch.ElapsedMilliseconds;
                
                if (response.IsSuccessStatusCode)
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "microsoft_auth",
                        status = "ok",
                        statusCode = (int)response.StatusCode,
                        latencyMs = latencyMs,
                        lastChecked = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
                    })).RootElement;
                }
                else
                {
                    return JsonDocument.Parse(JsonSerializer.Serialize(new
                    {
                        service = "microsoft_auth",
                        status = "error",
                        statusCode = (int)response.StatusCode,
                        error = $"HTTP {response.StatusCode}",
                        latencyMs = latencyMs
                    })).RootElement;
                }
            }
            catch (Exception ex)
            {
                serviceStopwatch.Stop();
                _logger.LogWarning(ex, "Failed to test Microsoft Auth connection");
                
                return JsonDocument.Parse(JsonSerializer.Serialize(new
                {
                    service = "microsoft_auth",
                    status = "error",
                    error = ex.Message,
                    latencyMs = serviceStopwatch.ElapsedMilliseconds
                })).RootElement;
            }
        }

        private string DetermineOverallStatus(int issueCount, int warningCount)
        {
            if (issueCount > 0)
            {
                return "error";
            }
            
            if (warningCount > 0)
            {
                return "warning";
            }
            
            return "ok";
        }
    }
}