package common

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

// DiskRateSample stores a snapshot of cumulative disk counters at a point in time.
type DiskRateSample struct {
	Timestamp  time.Time
	TotalIOOps float64 // reads + writes completed
	TotalBytes float64 // read + written bytes
}

// DiskRates holds computed IOPS and throughput rates.
type DiskRates struct {
	IOPS          float64 // I/O operations per second
	ThroughputMBs float64 // megabytes per second (1 MB = 1,000,000 bytes)
}

// DiskRateCalculator computes IOPS and throughput rates from cumulative counters
// using a 2-point sliding window per key (shared pool + pod).
type DiskRateCalculator struct {
	mu      sync.Mutex
	samples map[string]*DiskRateSample // key → latest sample
}

func NewDiskRateCalculator() *DiskRateCalculator {
	return &DiskRateCalculator{
		samples: make(map[string]*DiskRateSample),
	}
}

// AddSampleAndComputeRate stores a new sample and, if a previous sample exists,
// computes the rate between the two. Returns nil if this is the first sample.
func (c *DiskRateCalculator) AddSampleAndComputeRate(key string, sample DiskRateSample) (*DiskRates, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	prev, hasPrev := c.samples[key]
	c.samples[key] = &sample

	if !hasPrev {
		return nil, nil //nolint:nilnil // First sample: insufficient data to compute rate.
	}

	elapsed := sample.Timestamp.Sub(prev.Timestamp).Seconds()
	if elapsed <= 0 {
		return nil, fmt.Errorf("non-positive time delta between samples for key %s", key)
	}

	deltaOps := sample.TotalIOOps - prev.TotalIOOps
	deltaBytes := sample.TotalBytes - prev.TotalBytes

	// Counter reset detection: if delta is negative, counters were reset (e.g., pod restart).
	// Discard this sample pair and wait for the next one.
	if deltaOps < 0 || deltaBytes < 0 {
		return nil, nil //nolint:nilnil // Counter reset detected; need one more sample to compute rate.
	}

	return &DiskRates{
		IOPS:          deltaOps / elapsed,
		ThroughputMBs: deltaBytes / elapsed / 1_000_000,
	}, nil
}

// Reset removes all stored samples.
func (c *DiskRateCalculator) Reset() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.samples = make(map[string]*DiskRateSample)
}
