package doppler

import (
	"fmt"
	"math"
	"sync"
	"time"

	"SatMaster/backend/propagator"
)

const (
	SpeedOfLight = 299792.458 // km/s
)

// Calculator computes Doppler-shifted frequencies.
type Calculator struct {
	mu          sync.RWMutex
	nominalDown float64 // Hz
	nominalUp   float64 // Hz
	obsLat      float64
	obsLon      float64
	obsAlt      float64
}

func NewCalculator() *Calculator {
	return &Calculator{}
}

func (c *Calculator) SetObserver(lat, lon, altM float64) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.obsLat = lat
	c.obsLon = lon
	c.obsAlt = altM
}

func (c *Calculator) SetNominal(downHz, upHz float64) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.nominalDown = downHz
	c.nominalUp = upHz
}

// Correct computes Doppler-corrected downlink and uplink frequencies.
// Returns (downlinkHz, uplinkHz).
func (c *Calculator) Correct(pos *propagator.SatPosition, obs propagator.Observer, _ time.Time) (float64, float64) {
	c.mu.RLock()
	nomDown := c.nominalDown
	nomUp := c.nominalUp
	c.mu.RUnlock()

	if nomDown == 0 && nomUp == 0 {
		return 0, 0
	}
	if pos == nil {
		return nomDown, nomUp
	}

	// Range rate in km/s (positive = receding, negative = approaching)
	rr := pos.RangeRate

	// Doppler factor: f_received = f_nominal * (1 - v/c)
	// For downlink: satellite is the transmitter
	dopplerFactor := 1.0 - rr/SpeedOfLight

	correctedDown := nomDown * dopplerFactor
	// For uplink: we pre-correct in reverse so the satellite receives nominal
	correctedUp := nomUp / dopplerFactor

	return correctedDown, correctedUp
}

// ShiftHz returns the Doppler shift in Hz for a given nominal frequency.
func ShiftHz(nominalHz, rangeRateKmS float64) float64 {
	return nominalHz * (-rangeRateKmS / SpeedOfLight)
}

// RangeRateFromPositions computes range rate from two consecutive positions.
func RangeRateFromPositions(prev, curr *propagator.SatPosition, dt float64) float64 {
	prevRange := prev.Range
	currRange := curr.Range
	return (currRange - prevRange) / dt
}

// FormatShift formats a Doppler shift in Hz for display.
func FormatShift(shiftHz float64) string {
	if math.Abs(shiftHz) >= 1000 {
		return fmt.Sprintf("%+.2f kHz", shiftHz/1000)
	}
	return fmt.Sprintf("%+.0f Hz", shiftHz)
}
