package storage

import (
	"sort"
	"time"
)

// ClusterSeries is one cluster's daily disk usage and volume server count,
// aligned to the shared date axis of the enclosing ClusterSizeSeries.
type ClusterSeries struct {
	ClusterId string   `json:"cluster_id"`
	Disk      []uint64 `json:"disk"`
	Servers   []uint64 `json:"servers"`
}

// OtherSeries is the clusters beyond the caller's limit, summed per day so a
// stacked chart still adds up to the fleet total.
type OtherSeries struct {
	Count   int      `json:"count"`
	Disk    []uint64 `json:"disk"`
	Servers []uint64 `json:"servers"`
}

// ClusterSizeSeries is per-cluster disk usage and volume server count over
// time: one value per cluster per day, largest cluster first, ranked by disk on
// their most recent day. Both metrics share one ranking so a cluster keeps its
// place, and its colour, across the charts drawn from this.
type ClusterSizeSeries struct {
	Dates        []string        `json:"dates"`
	Clusters     []ClusterSeries `json:"clusters"`
	Other        *OtherSeries    `json:"other,omitempty"`
	ClusterCount int             `json:"cluster_count"`
	TotalDisk    uint64          `json:"total_disk"`    // across all clusters on the last day
	TotalServers uint64          `json:"total_servers"` // across all clusters on the last day
}

// GetClusterSizeSeries returns the last `days` days of per-cluster disk usage
// and volume server counts across confirmed clusters. Clusters beyond `limit`
// are folded into Other.
func (s *PrometheusStorage) GetClusterSizeSeries(days, limit int) ClusterSizeSeries {
	s.mu.RLock()
	defer s.mu.RUnlock()

	histories := s.seriesHistories()
	axis := newDailySeries(days, histories)
	activeSince := time.Now().UTC().AddDate(0, 0, -activeDays).Unix()
	last := len(axis.dates) - 1
	series := ClusterSizeSeries{Dates: axis.dates}

	for id, history := range histories {
		disk, ok := align(axis, history, activeSince, diskBytes)
		if !ok {
			continue
		}
		servers, _ := align(axis, history, activeSince, serverCount)
		series.Clusters = append(series.Clusters, ClusterSeries{ClusterId: id, Disk: disk, Servers: servers})
		series.TotalDisk += disk[last]
		series.TotalServers += servers[last]
	}
	series.ClusterCount = len(series.Clusters)

	// Rank by the latest day so the stack reads largest-first at its right
	// edge, tie-breaking on id to keep the order stable across refreshes.
	sort.Slice(series.Clusters, func(i, j int) bool {
		a, b := series.Clusters[i], series.Clusters[j]
		if a.Disk[last] != b.Disk[last] {
			return a.Disk[last] > b.Disk[last]
		}
		return a.ClusterId < b.ClusterId
	})

	if limit > 0 && len(series.Clusters) > limit {
		other := OtherSeries{
			Count:   len(series.Clusters) - limit,
			Disk:    make([]uint64, len(axis.dates)),
			Servers: make([]uint64, len(axis.dates)),
		}
		for _, c := range series.Clusters[limit:] {
			for i, v := range c.Disk {
				other.Disk[i] += v
			}
			for i, v := range c.Servers {
				other.Servers[i] += v
			}
		}
		series.Clusters = series.Clusters[:limit]
		series.Other = &other
	}
	return series
}
