package metrics

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"
	metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
)

// Client wraps the Kubernetes metrics client for fetching pod resource metrics
type Client struct {
	metricsCli *metricsv.Clientset
}

// NewClient creates a new metrics client from the given REST config
func NewClient(config *rest.Config) (*Client, error) {
	metricsCli, err := metricsv.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("failed to create metrics client: %w", err)
	}
	return &Client{metricsCli: metricsCli}, nil
}

// GetPodCPUAndMemory returns the CPU cores used and memory bytes used for a pod
// CPU is returned in cores (e.g., 0.5 means 500m CPU)
// Memory is returned in bytes
func (c *Client) GetPodCPUAndMemory(ctx context.Context, namespace, podName string) (float64, uint64, error) {
	if c.metricsCli == nil {
		return 0, 0, fmt.Errorf("metrics client not initialized")
	}

	podMetrics, err := c.metricsCli.MetricsV1beta1().PodMetricses(namespace).Get(ctx, podName, metav1.GetOptions{})
	if err != nil {
		return 0, 0, fmt.Errorf("failed to get metrics for pod %s/%s: %w", namespace, podName, err)
	}

	var totalCPU resource.Quantity
	var totalMemory resource.Quantity

	for _, container := range podMetrics.Containers {
		if cpu := container.Usage.Cpu(); cpu != nil {
			totalCPU.Add(*cpu)
		}
		if memory := container.Usage.Memory(); memory != nil {
			totalMemory.Add(*memory)
		}
	}

	// Convert millicores to cores
	cpuCores := float64(totalCPU.MilliValue()) / 1000.0
	memoryBytes := uint64(totalMemory.Value())

	return cpuCores, memoryBytes, nil
}

// GetPodMetrics fetches metrics for all containers in a pod
func (c *Client) GetPodMetrics(ctx context.Context, namespace, podName string) (*PodMetrics, error) {
	if c.metricsCli == nil {
		return nil, fmt.Errorf("metrics client not initialized")
	}

	podMetrics, err := c.metricsCli.MetricsV1beta1().PodMetricses(namespace).Get(ctx, podName, metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get metrics for pod %s/%s: %w", namespace, podName, err)
	}

	metrics := &PodMetrics{
		ContainerMetrics: make([]ContainerMetrics, 0, len(podMetrics.Containers)),
	}

	for _, container := range podMetrics.Containers {
		var cpuCores float64
		var memoryBytes uint64

		if cpu := container.Usage.Cpu(); cpu != nil {
			cpuCores = float64(cpu.MilliValue()) / 1000.0
		}
		if memory := container.Usage.Memory(); memory != nil {
			memoryBytes = uint64(memory.Value())
		}

		metrics.ContainerMetrics = append(metrics.ContainerMetrics, ContainerMetrics{
			Name:            container.Name,
			CPUUsedCores:    cpuCores,
			MemoryUsedBytes: memoryBytes,
		})

		// Aggregate totals
		metrics.TotalCPUUsedCores += cpuCores
		metrics.TotalMemoryUsedBytes += memoryBytes
	}

	return metrics, nil
}

// PodMetrics contains resource usage metrics for a pod
type PodMetrics struct {
	ContainerMetrics     []ContainerMetrics
	TotalCPUUsedCores    float64
	TotalMemoryUsedBytes uint64
}

// ContainerMetrics contains resource usage for a single container
type ContainerMetrics struct {
	Name            string
	CPUUsedCores    float64
	MemoryUsedBytes uint64
}

// IsMetricsServerAvailable checks if the metrics server is available by attempting to list pod metrics
func (c *Client) IsMetricsServerAvailable(ctx context.Context) bool {
	if c.metricsCli == nil {
		return false
	}
	// Try to list metrics in any namespace with a limit of 1
	_, err := c.metricsCli.MetricsV1beta1().PodMetricses(corev1.NamespaceAll).List(ctx, metav1.ListOptions{Limit: 1})
	return err == nil
}
