using System.Globalization;
using System.Text;
using System.Text.Json;
using EntKube.Web.Data;
using k8s;
using Microsoft.EntityFrameworkCore;

namespace EntKube.Web.Services;

/// <summary>
/// Configuration extracted from a kube-prometheus-stack component that tells
/// us where to find the Prometheus and Alertmanager services inside the cluster.
/// </summary>
public class PrometheusConfig
{
    public string Namespace { get; set; } = "monitoring";
    public string ServiceName { get; set; } = "prometheus-kube-prometheus-prometheus";
    public int ServicePort { get; set; } = 9090;
    public string AlertmanagerServiceName { get; set; } = "prometheus-kube-prometheus-alertmanager";
    public int AlertmanagerServicePort { get; set; } = 9093;
}

/// <summary>
/// A single instant-query result from Prometheus — one metric with its labels and value.
/// </summary>
public class PrometheusMetricResult
{
    public Dictionary<string, string> Labels { get; set; } = new();
    public double Value { get; set; }
    public DateTime Timestamp { get; set; }
}

/// <summary>
/// A time-series result from a Prometheus range query — a set of data points over time.
/// </summary>
public class PrometheusTimeSeries
{
    public Dictionary<string, string> Labels { get; set; } = new();
    public List<TimeSeriesDataPoint> DataPoints { get; set; } = [];
}

/// <summary>
/// A single data point in a time series — a timestamp/value pair.
/// </summary>
public class TimeSeriesDataPoint
{
    public DateTime Timestamp { get; set; }
    public double Value { get; set; }
}

/// <summary>
/// Summary of cluster health metrics retrieved from Prometheus.
/// </summary>
public class ClusterHealthSummary
{
    public double CpuUsagePercent { get; set; }
    public double MemoryUsagePercent { get; set; }
    public int TotalNodes { get; set; }
    public int ReadyNodes { get; set; }
    public int TotalPods { get; set; }
    public int RunningPods { get; set; }
    public int PendingPods { get; set; }
    public int FailedPods { get; set; }
    public double DiskUsagePercent { get; set; }
    public List<NodeHealthInfo> Nodes { get; set; } = [];
    public DateTime QueriedAt { get; set; } = DateTime.UtcNow;
}

/// <summary>
/// Per-node health information within a cluster.
/// </summary>
public class NodeHealthInfo
{
    public string Name { get; set; } = "";
    public bool Ready { get; set; }
    public double CpuUsagePercent { get; set; }
    public double MemoryUsagePercent { get; set; }
}

/// <summary>
/// An alert from Alertmanager.
/// </summary>
public class AlertInfo
{
    public string Name { get; set; } = "";
    public string Severity { get; set; } = "";
    public string Summary { get; set; } = "";
    public string Description { get; set; } = "";
    public string State { get; set; } = "";
    public string Fingerprint { get; set; } = "";
    public DateTime StartsAt { get; set; }
    public DateTime EndsAt { get; set; }
    public Dictionary<string, string> Labels { get; set; } = new();
}

/// <summary>
/// A silence from Alertmanager.
/// </summary>
public class SilenceInfo
{
    public string Id { get; set; } = "";
    public string State { get; set; } = "";
    public string Comment { get; set; } = "";
    public string CreatedBy { get; set; } = "";
    public DateTime StartsAt { get; set; }
    public DateTime EndsAt { get; set; }
    public List<SilenceMatcher> Matchers { get; set; } = [];
}

/// <summary>
/// A matcher used in an Alertmanager silence to select which alerts to suppress.
/// </summary>
public class SilenceMatcher
{
    public string Name { get; set; } = "";
    public string Value { get; set; } = "";
    public bool IsRegex { get; set; }
    public bool IsEqual { get; set; } = true;
}

/// <summary>
/// Connects to a kube-prometheus-stack running in a cluster and retrieves
/// health metrics, time-series data, alerts, and silences via port-forwarded
/// or direct service access through the Kubernetes API proxy.
///
/// The service locates the Prometheus component on the cluster, builds a
/// Kubernetes client from the stored kubeconfig, and queries the Prometheus
/// HTTP API via the K8s API server proxy endpoint.
/// </summary>
public class PrometheusService(IDbContextFactory<ApplicationDbContext> dbFactory)
{
    // ──────── Public API ────────

    /// <summary>
    /// Retrieves a health summary for the cluster by querying key Prometheus
    /// metrics (CPU, memory, nodes, pods, disk). Returns a failure result if
    /// the cluster isn't found, has no Prometheus component, or lacks kubeconfig.
    /// </summary>
    public async Task<KubernetesOperationResult<ClusterHealthSummary>> GetClusterHealthAsync(
        Guid clusterId, CancellationToken ct = default)
    {
        using ApplicationDbContext db = dbFactory.CreateDbContext();

        // Load the cluster with its components to find the prometheus instance.

        KubernetesCluster? cluster = await db.KubernetesClusters
            .Include(c => c.Components)
            .FirstOrDefaultAsync(c => c.Id == clusterId, ct);

        if (cluster is null)
        {
            return KubernetesOperationResult<ClusterHealthSummary>.Failure("Cluster not found.");
        }

        if (string.IsNullOrWhiteSpace(cluster.Kubeconfig))
        {
            return KubernetesOperationResult<ClusterHealthSummary>.Failure(
                "No kubeconfig configured for this cluster.");
        }

        // Find the prometheus component on this cluster.

        ClusterComponent? prometheusComponent = cluster.Components.FirstOrDefault(c =>
            c.Name.Contains("prometheus", StringComparison.OrdinalIgnoreCase));

        if (prometheusComponent is null)
        {
            return KubernetesOperationResult<ClusterHealthSummary>.Failure(
                "No prometheus component found on this cluster.");
        }

        PrometheusConfig config = GetPrometheusConfig(prometheusComponent) ?? new PrometheusConfig();

        try
        {
            Kubernetes client = CreateClient(cluster.Kubeconfig);
            string baseUrl = $"/api/v1/namespaces/{config.Namespace}/services/{config.ServiceName}:{config.ServicePort}/proxy";

            // Query key health metrics in parallel.

            Task<string> cpuTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=100 - (avg(rate(node_cpu_seconds_total{{mode=\"idle\"}}[5m])) * 100)", ct);
            Task<string> memTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=100 - (avg(node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100)", ct);
            Task<string> nodesTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=count(kube_node_info)", ct);
            Task<string> readyNodesTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=count(kube_node_status_condition{{condition=\"Ready\",status=\"true\"}})", ct);
            Task<string> podsTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=count(kube_pod_info)", ct);
            Task<string> runningPodsTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=sum(kube_pod_status_phase{{phase=\"Running\"}})", ct);
            Task<string> diskTask = ProxyGetAsync(client, $"{baseUrl}/api/v1/query?query=100 - (avg(node_filesystem_avail_bytes{{mountpoint=\"/\"}} / node_filesystem_size_bytes{{mountpoint=\"/\"}}) * 100)", ct);

            await Task.WhenAll(cpuTask, memTask, nodesTask, readyNodesTask, podsTask, runningPodsTask, diskTask);

            ClusterHealthSummary health = new()
            {
                CpuUsagePercent = ExtractScalarValue(await cpuTask),
                MemoryUsagePercent = ExtractScalarValue(await memTask),
                TotalNodes = (int)ExtractScalarValue(await nodesTask),
                ReadyNodes = (int)ExtractScalarValue(await readyNodesTask),
                TotalPods = (int)ExtractScalarValue(await podsTask),
                RunningPods = (int)ExtractScalarValue(await runningPodsTask),
                DiskUsagePercent = ExtractScalarValue(await diskTask),
                QueriedAt = DateTime.UtcNow
            };

            return KubernetesOperationResult<ClusterHealthSummary>.Success(health);
        }
        catch (Exception ex)
        {
            return KubernetesOperationResult<ClusterHealthSummary>.Failure(
                $"Failed to query Prometheus: {ex.Message}");
        }
    }

    /// <summary>
    /// Queries a Prometheus range query over the given duration and returns time-series data.
    /// </summary>
    public async Task<KubernetesOperationResult<List<PrometheusTimeSeries>>> GetMetricRangeAsync(
        Guid clusterId, string query, TimeSpan duration, CancellationToken ct = default)
    {
        using ApplicationDbContext db = dbFactory.CreateDbContext();

        KubernetesCluster? cluster = await db.KubernetesClusters
            .Include(c => c.Components)
            .FirstOrDefaultAsync(c => c.Id == clusterId, ct);

        if (cluster is null)
        {
            return KubernetesOperationResult<List<PrometheusTimeSeries>>.Failure("Cluster not found.");
        }

        if (string.IsNullOrWhiteSpace(cluster.Kubeconfig))
        {
            return KubernetesOperationResult<List<PrometheusTimeSeries>>.Failure("No kubeconfig.");
        }

        ClusterComponent? prometheusComponent = cluster.Components.FirstOrDefault(c =>
            c.Name.Contains("prometheus", StringComparison.OrdinalIgnoreCase));

        if (prometheusComponent is null)
        {
            return KubernetesOperationResult<List<PrometheusTimeSeries>>.Failure("No prometheus component.");
        }

        PrometheusConfig config = GetPrometheusConfig(prometheusComponent) ?? new PrometheusConfig();

        try
        {
            Kubernetes client = CreateClient(cluster.Kubeconfig);
            string baseUrl = $"/api/v1/namespaces/{config.Namespace}/services/{config.ServiceName}:{config.ServicePort}/proxy";

            long end = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            long start = end - (long)duration.TotalSeconds;
            int step = Math.Max(15, (int)(duration.TotalSeconds / 100));

            string encodedQuery = Uri.EscapeDataString(query);
            string url = $"{baseUrl}/api/v1/query_range?query={encodedQuery}&start={start}&end={end}&step={step}";

            string json = await ProxyGetAsync(client, url, ct);
            List<PrometheusTimeSeries> series = ParseRangeQueryResult(json);

            return KubernetesOperationResult<List<PrometheusTimeSeries>>.Success(series);
        }
        catch (Exception ex)
        {
            return KubernetesOperationResult<List<PrometheusTimeSeries>>.Failure(
                $"Range query failed: {ex.Message}");
        }
    }

    /// <summary>
    /// Retrieves active alerts from Alertmanager.
    /// </summary>
    public async Task<KubernetesOperationResult<List<AlertInfo>>> GetAlertsAsync(
        Guid clusterId, CancellationToken ct = default)
    {
        using ApplicationDbContext db = dbFactory.CreateDbContext();

        KubernetesCluster? cluster = await db.KubernetesClusters
            .Include(c => c.Components)
            .FirstOrDefaultAsync(c => c.Id == clusterId, ct);

        if (cluster is null)
        {
            return KubernetesOperationResult<List<AlertInfo>>.Failure("Cluster not found.");
        }

        if (string.IsNullOrWhiteSpace(cluster.Kubeconfig))
        {
            return KubernetesOperationResult<List<AlertInfo>>.Failure("No kubeconfig.");
        }

        ClusterComponent? prometheusComponent = cluster.Components.FirstOrDefault(c =>
            c.Name.Contains("prometheus", StringComparison.OrdinalIgnoreCase));

        if (prometheusComponent is null)
        {
            return KubernetesOperationResult<List<AlertInfo>>.Failure("No prometheus component.");
        }

        PrometheusConfig config = GetPrometheusConfig(prometheusComponent) ?? new PrometheusConfig();

        try
        {
            Kubernetes client = CreateClient(cluster.Kubeconfig);
            string baseUrl = $"/api/v1/namespaces/{config.Namespace}/services/{config.AlertmanagerServiceName}:{config.AlertmanagerServicePort}/proxy";

            string json = await ProxyGetAsync(client, $"{baseUrl}/api/v2/alerts", ct);
            List<AlertInfo> alerts = ParseAlertmanagerAlerts(json);

            return KubernetesOperationResult<List<AlertInfo>>.Success(alerts);
        }
        catch (Exception ex)
        {
            return KubernetesOperationResult<List<AlertInfo>>.Failure(
                $"Failed to query Alertmanager: {ex.Message}");
        }
    }

    /// <summary>
    /// Retrieves silences from Alertmanager.
    /// </summary>
    public async Task<KubernetesOperationResult<List<SilenceInfo>>> GetSilencesAsync(
        Guid clusterId, CancellationToken ct = default)
    {
        using ApplicationDbContext db = dbFactory.CreateDbContext();

        KubernetesCluster? cluster = await db.KubernetesClusters
            .Include(c => c.Components)
            .FirstOrDefaultAsync(c => c.Id == clusterId, ct);

        if (cluster is null)
        {
            return KubernetesOperationResult<List<SilenceInfo>>.Failure("Cluster not found.");
        }

        if (string.IsNullOrWhiteSpace(cluster.Kubeconfig))
        {
            return KubernetesOperationResult<List<SilenceInfo>>.Failure("No kubeconfig.");
        }

        ClusterComponent? prometheusComponent = cluster.Components.FirstOrDefault(c =>
            c.Name.Contains("prometheus", StringComparison.OrdinalIgnoreCase));

        if (prometheusComponent is null)
        {
            return KubernetesOperationResult<List<SilenceInfo>>.Failure("No prometheus component.");
        }

        PrometheusConfig config = GetPrometheusConfig(prometheusComponent) ?? new PrometheusConfig();

        try
        {
            Kubernetes client = CreateClient(cluster.Kubeconfig);
            string baseUrl = $"/api/v1/namespaces/{config.Namespace}/services/{config.AlertmanagerServiceName}:{config.AlertmanagerServicePort}/proxy";

            string json = await ProxyGetAsync(client, $"{baseUrl}/api/v2/silences", ct);
            List<SilenceInfo> silences = ParseAlertmanagerSilences(json);

            return KubernetesOperationResult<List<SilenceInfo>>.Success(silences);
        }
        catch (Exception ex)
        {
            return KubernetesOperationResult<List<SilenceInfo>>.Failure(
                $"Failed to query Alertmanager silences: {ex.Message}");
        }
    }

    /// <summary>
    /// Creates a new silence in Alertmanager.
    /// </summary>
    public async Task<KubernetesOperationResult> CreateSilenceAsync(
        Guid clusterId, string comment, string createdBy, TimeSpan duration,
        List<SilenceMatcher> matchers, CancellationToken ct = default)
    {
        using ApplicationDbContext db = dbFactory.CreateDbContext();

        KubernetesCluster? cluster = await db.KubernetesClusters
            .Include(c => c.Components)
            .FirstOrDefaultAsync(c => c.Id == clusterId, ct);

        if (cluster is null)
        {
            return KubernetesOperationResult.Failure("Cluster not found.");
        }

        if (string.IsNullOrWhiteSpace(cluster.Kubeconfig))
        {
            return KubernetesOperationResult.Failure("No kubeconfig.");
        }

        ClusterComponent? prometheusComponent = cluster.Components.FirstOrDefault(c =>
            c.Name.Contains("prometheus", StringComparison.OrdinalIgnoreCase));

        if (prometheusComponent is null)
        {
            return KubernetesOperationResult.Failure("No prometheus component.");
        }

        PrometheusConfig config = GetPrometheusConfig(prometheusComponent) ?? new PrometheusConfig();

        try
        {
            Kubernetes client = CreateClient(cluster.Kubeconfig);
            string baseUrl = $"/api/v1/namespaces/{config.Namespace}/services/{config.AlertmanagerServiceName}:{config.AlertmanagerServicePort}/proxy";

            object silencePayload = new
            {
                matchers = matchers.Select(m => new
                {
                    name = m.Name,
                    value = m.Value,
                    isRegex = m.IsRegex,
                    isEqual = m.IsEqual
                }).ToArray(),
                startsAt = DateTime.UtcNow.ToString("o"),
                endsAt = DateTime.UtcNow.Add(duration).ToString("o"),
                createdBy,
                comment
            };

            string body = JsonSerializer.Serialize(silencePayload);
            await ProxyPostAsync(client, $"{baseUrl}/api/v2/silences", body, ct);

            return KubernetesOperationResult.Success();
        }
        catch (Exception ex)
        {
            return KubernetesOperationResult.Failure($"Failed to create silence: {ex.Message}");
        }
    }

    // ──────── Static Parsing Methods ────────

    /// <summary>
    /// Parses a Prometheus instant query response (vector or scalar) into metric results.
    /// </summary>
    public static List<PrometheusMetricResult> ParseInstantQueryResult(string json)
    {
        List<PrometheusMetricResult> results = [];

        try
        {
            using JsonDocument doc = JsonDocument.Parse(json);
            JsonElement root = doc.RootElement;

            if (root.GetProperty("status").GetString() != "success")
            {
                return results;
            }

            JsonElement data = root.GetProperty("data");
            string resultType = data.GetProperty("resultType").GetString() ?? "";

            if (resultType == "vector")
            {
                foreach (JsonElement item in data.GetProperty("result").EnumerateArray())
                {
                    PrometheusMetricResult metric = new();

                    if (item.TryGetProperty("metric", out JsonElement metricLabels))
                    {
                        foreach (JsonProperty prop in metricLabels.EnumerateObject())
                        {
                            metric.Labels[prop.Name] = prop.Value.GetString() ?? "";
                        }
                    }

                    JsonElement value = item.GetProperty("value");
                    metric.Timestamp = DateTimeOffset.FromUnixTimeSeconds((long)value[0].GetDouble()).UtcDateTime;
                    metric.Value = double.Parse(value[1].GetString() ?? "0", CultureInfo.InvariantCulture);

                    results.Add(metric);
                }
            }
            else if (resultType == "scalar")
            {
                JsonElement result = data.GetProperty("result");
                PrometheusMetricResult metric = new()
                {
                    Timestamp = DateTimeOffset.FromUnixTimeSeconds((long)result[0].GetDouble()).UtcDateTime,
                    Value = double.Parse(result[1].GetString() ?? "0", CultureInfo.InvariantCulture)
                };
                results.Add(metric);
            }
        }
        catch
        {
            // Graceful degradation — return empty on parse failure.
        }

        return results;
    }

    /// <summary>
    /// Parses a Prometheus range query response (matrix) into time-series data.
    /// </summary>
    public static List<PrometheusTimeSeries> ParseRangeQueryResult(string json)
    {
        List<PrometheusTimeSeries> results = [];

        try
        {
            using JsonDocument doc = JsonDocument.Parse(json);
            JsonElement root = doc.RootElement;

            if (root.GetProperty("status").GetString() != "success")
            {
                return results;
            }

            JsonElement data = root.GetProperty("data");

            if (data.GetProperty("resultType").GetString() != "matrix")
            {
                return results;
            }

            foreach (JsonElement item in data.GetProperty("result").EnumerateArray())
            {
                PrometheusTimeSeries series = new();

                if (item.TryGetProperty("metric", out JsonElement metricLabels))
                {
                    foreach (JsonProperty prop in metricLabels.EnumerateObject())
                    {
                        series.Labels[prop.Name] = prop.Value.GetString() ?? "";
                    }
                }

                if (item.TryGetProperty("values", out JsonElement values))
                {
                    foreach (JsonElement point in values.EnumerateArray())
                    {
                        TimeSeriesDataPoint dp = new()
                        {
                            Timestamp = DateTimeOffset.FromUnixTimeSeconds((long)point[0].GetDouble()).UtcDateTime,
                            Value = double.Parse(point[1].GetString() ?? "0", CultureInfo.InvariantCulture)
                        };
                        series.DataPoints.Add(dp);
                    }
                }

                results.Add(series);
            }
        }
        catch
        {
            // Graceful degradation.
        }

        return results;
    }

    /// <summary>
    /// Parses an Alertmanager /api/v2/alerts JSON response into AlertInfo objects.
    /// </summary>
    public static List<AlertInfo> ParseAlertmanagerAlerts(string json)
    {
        List<AlertInfo> results = [];

        try
        {
            using JsonDocument doc = JsonDocument.Parse(json);

            if (doc.RootElement.ValueKind != JsonValueKind.Array)
            {
                return results;
            }

            foreach (JsonElement alert in doc.RootElement.EnumerateArray())
            {
                AlertInfo info = new();

                if (alert.TryGetProperty("labels", out JsonElement labels))
                {
                    foreach (JsonProperty prop in labels.EnumerateObject())
                    {
                        info.Labels[prop.Name] = prop.Value.GetString() ?? "";
                    }

                    info.Name = info.Labels.GetValueOrDefault("alertname") ?? "";
                    info.Severity = info.Labels.GetValueOrDefault("severity") ?? "";
                }

                if (alert.TryGetProperty("annotations", out JsonElement annotations))
                {
                    info.Summary = annotations.TryGetProperty("summary", out JsonElement s)
                        ? s.GetString() ?? "" : "";
                    info.Description = annotations.TryGetProperty("description", out JsonElement d)
                        ? d.GetString() ?? "" : "";
                }

                if (alert.TryGetProperty("startsAt", out JsonElement startsAt))
                {
                    DateTime.TryParse(startsAt.GetString(), out DateTime parsed);
                    info.StartsAt = parsed;
                }

                if (alert.TryGetProperty("endsAt", out JsonElement endsAt))
                {
                    DateTime.TryParse(endsAt.GetString(), out DateTime parsed);
                    info.EndsAt = parsed;
                }

                if (alert.TryGetProperty("status", out JsonElement status)
                    && status.TryGetProperty("state", out JsonElement state))
                {
                    info.State = state.GetString() ?? "";
                }

                if (alert.TryGetProperty("fingerprint", out JsonElement fp))
                {
                    info.Fingerprint = fp.GetString() ?? "";
                }

                results.Add(info);
            }
        }
        catch
        {
            // Graceful degradation.
        }

        return results;
    }

    /// <summary>
    /// Parses an Alertmanager /api/v2/silences JSON response into SilenceInfo objects.
    /// </summary>
    public static List<SilenceInfo> ParseAlertmanagerSilences(string json)
    {
        List<SilenceInfo> results = [];

        try
        {
            using JsonDocument doc = JsonDocument.Parse(json);

            if (doc.RootElement.ValueKind != JsonValueKind.Array)
            {
                return results;
            }

            foreach (JsonElement silence in doc.RootElement.EnumerateArray())
            {
                SilenceInfo info = new();

                if (silence.TryGetProperty("id", out JsonElement id))
                {
                    info.Id = id.GetString() ?? "";
                }

                if (silence.TryGetProperty("status", out JsonElement status)
                    && status.TryGetProperty("state", out JsonElement state))
                {
                    info.State = state.GetString() ?? "";
                }

                if (silence.TryGetProperty("comment", out JsonElement comment))
                {
                    info.Comment = comment.GetString() ?? "";
                }

                if (silence.TryGetProperty("createdBy", out JsonElement createdBy))
                {
                    info.CreatedBy = createdBy.GetString() ?? "";
                }

                if (silence.TryGetProperty("startsAt", out JsonElement startsAt))
                {
                    DateTime.TryParse(startsAt.GetString(), out DateTime parsed);
                    info.StartsAt = parsed;
                }

                if (silence.TryGetProperty("endsAt", out JsonElement endsAt))
                {
                    DateTime.TryParse(endsAt.GetString(), out DateTime parsed);
                    info.EndsAt = parsed;
                }

                if (silence.TryGetProperty("matchers", out JsonElement matchers)
                    && matchers.ValueKind == JsonValueKind.Array)
                {
                    foreach (JsonElement m in matchers.EnumerateArray())
                    {
                        SilenceMatcher matcher = new()
                        {
                            Name = m.TryGetProperty("name", out JsonElement n) ? n.GetString() ?? "" : "",
                            Value = m.TryGetProperty("value", out JsonElement v) ? v.GetString() ?? "" : "",
                            IsRegex = m.TryGetProperty("isRegex", out JsonElement r) && r.GetBoolean(),
                            IsEqual = !m.TryGetProperty("isEqual", out JsonElement e) || e.GetBoolean()
                        };
                        info.Matchers.Add(matcher);
                    }
                }

                results.Add(info);
            }
        }
        catch
        {
            // Graceful degradation.
        }

        return results;
    }

    /// <summary>
    /// Extracts Prometheus configuration from a component's Configuration JSON.
    /// Falls back to standard kube-prometheus-stack defaults if parsing fails.
    /// </summary>
    public static PrometheusConfig? GetPrometheusConfig(ClusterComponent component)
    {
        if (string.IsNullOrWhiteSpace(component.Configuration))
        {
            return new PrometheusConfig();
        }

        try
        {
            PrometheusConfig? config = JsonSerializer.Deserialize<PrometheusConfig>(
                component.Configuration,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            return config ?? new PrometheusConfig();
        }
        catch
        {
            return new PrometheusConfig();
        }
    }

    // ──────── Internal Helpers ────────

    private static double ExtractScalarValue(string json)
    {
        List<PrometheusMetricResult> results = ParseInstantQueryResult(json);
        return results.Count > 0 ? results[0].Value : 0;
    }

    private static Kubernetes CreateClient(string kubeconfig)
    {
        using MemoryStream stream = new(Encoding.UTF8.GetBytes(kubeconfig));
        KubernetesClientConfiguration config = KubernetesClientConfiguration.BuildConfigFromConfigFile(stream);
        return new Kubernetes(config);
    }

    private static async Task<string> ProxyGetAsync(Kubernetes client, string path, CancellationToken ct)
    {
        // Use the K8s API server proxy to reach in-cluster services.

        Uri requestUri = new(client.BaseUri, path);
        HttpResponseMessage response = await client.HttpClient.GetAsync(requestUri, ct);
        return await response.Content.ReadAsStringAsync(ct);
    }

    private static async Task ProxyPostAsync(Kubernetes client, string path, string body, CancellationToken ct)
    {
        Uri requestUri = new(client.BaseUri, path);
        StringContent content = new(body, Encoding.UTF8, "application/json");
        await client.HttpClient.PostAsync(requestUri, content, ct);
    }
}
