package dashboard

import (
	"fmt"
	"sort"
	"sync"
	"time"
)

// MetricSnapshot represents a point-in-time metric value
type MetricSnapshot struct {
	Timestamp time.Time
	Value     float64
	Name      string
}

// MetricsAggregator aggregates Prometheus metrics for dashboard display
type MetricsAggregator struct {
	mu       sync.RWMutex
	metrics  map[string][]MetricSnapshot
	ttl      time.Duration
	maxSize  int
}

// NewMetricsAggregator creates a new metrics aggregator
func NewMetricsAggregator(ttl time.Duration, maxSize int) *MetricsAggregator {
	return &MetricsAggregator{
		metrics:  make(map[string][]MetricSnapshot),
		ttl:      ttl,
		maxSize:  maxSize,
	}
}

// Record records a metric value
func (ma *MetricsAggregator) Record(name string, value float64) {
	ma.mu.Lock()
	defer ma.mu.Unlock()

	snapshot := MetricSnapshot{
		Timestamp: time.Now(),
		Value:     value,
		Name:      name,
	}

	ma.metrics[name] = append(ma.metrics[name], snapshot)

	// Trim old entries
	if len(ma.metrics[name]) > ma.maxSize {
		ma.metrics[name] = ma.metrics[name][1:]
	}
}

// GetTimeSeries retrieves metric time series
func (ma *MetricsAggregator) GetTimeSeries(name string) []MetricSnapshot {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists {
		return []MetricSnapshot{}
	}

	result := make([]MetricSnapshot, len(snapshots))
	copy(result, snapshots)
	return result
}

// GetPercentile calculates percentile for a metric
func (ma *MetricsAggregator) GetPercentile(name string, percentile float64) (float64, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists || len(snapshots) == 0 {
		return 0, fmt.Errorf("metric not found: %s", name)
	}

	values := make([]float64, len(snapshots))
	for i, s := range snapshots {
		values[i] = s.Value
	}

	sort.Float64s(values)

	index := int(float64(len(values)) * percentile / 100)
	if index >= len(values) {
		index = len(values) - 1
	}

	return values[index], nil
}

// GetAverage calculates average for a metric
func (ma *MetricsAggregator) GetAverage(name string) (float64, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists || len(snapshots) == 0 {
		return 0, fmt.Errorf("metric not found: %s", name)
	}

	sum := 0.0
	for _, s := range snapshots {
		sum += s.Value
	}

	return sum / float64(len(snapshots)), nil
}

// GetMax returns maximum value for a metric
func (ma *MetricsAggregator) GetMax(name string) (float64, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists || len(snapshots) == 0 {
		return 0, fmt.Errorf("metric not found: %s", name)
	}

	max := snapshots[0].Value
	for _, s := range snapshots {
		if s.Value > max {
			max = s.Value
		}
	}

	return max, nil
}

// GetMin returns minimum value for a metric
func (ma *MetricsAggregator) GetMin(name string) (float64, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists || len(snapshots) == 0 {
		return 0, fmt.Errorf("metric not found: %s", name)
	}

	min := snapshots[0].Value
	for _, s := range snapshots {
		if s.Value < min {
			min = s.Value
		}
	}

	return min, nil
}

// GetMetricNames returns all recorded metric names
func (ma *MetricsAggregator) GetMetricNames() []string {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	names := make([]string, 0, len(ma.metrics))
	for name := range ma.metrics {
		names = append(names, name)
	}

	return names
}

// GetLatest returns the latest snapshot for a metric
func (ma *MetricsAggregator) GetLatest(name string) (MetricSnapshot, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists || len(snapshots) == 0 {
		return MetricSnapshot{}, fmt.Errorf("metric not found: %s", name)
	}

	return snapshots[len(snapshots)-1], nil
}

// Clear clears all metrics
func (ma *MetricsAggregator) Clear() {
	ma.mu.Lock()
	defer ma.mu.Unlock()

	ma.metrics = make(map[string][]MetricSnapshot)
}

// GetCountInRange returns count of metrics within a time range
func (ma *MetricsAggregator) GetCountInRange(name string, start, end time.Time) (int, error) {
	ma.mu.RLock()
	defer ma.mu.RUnlock()

	snapshots, exists := ma.metrics[name]
	if !exists {
		return 0, fmt.Errorf("metric not found: %s", name)
	}

	count := 0
	for _, s := range snapshots {
		if s.Timestamp.After(start) && s.Timestamp.Before(end) {
			count++
		}
	}

	return count, nil
}
