//go:build linux

package audio

import (
	"runtime"
	"syscall"
	"unsafe"

	"github.com/jetkvm/kvm/internal/logging"
	"github.com/rs/zerolog"
)

// SchedParam represents scheduling parameters for Linux
type SchedParam struct {
	Priority int32
}

// getPriorityConstants returns priority levels from centralized config
func getPriorityConstants() (audioHigh, audioMedium, audioLow, normal int) {
	config := GetConfig()
	return config.AudioHighPriority, config.AudioMediumPriority, config.AudioLowPriority, config.NormalPriority
}

// getSchedulingPolicies returns scheduling policies from centralized config
func getSchedulingPolicies() (schedNormal, schedFIFO, schedRR int) {
	config := GetConfig()
	return config.SchedNormal, config.SchedFIFO, config.SchedRR
}

// PriorityScheduler manages thread priorities for audio processing
type PriorityScheduler struct {
	logger  zerolog.Logger
	enabled bool
}

// NewPriorityScheduler creates a new priority scheduler
func NewPriorityScheduler() *PriorityScheduler {
	return &PriorityScheduler{
		logger:  logging.GetDefaultLogger().With().Str("component", "priority-scheduler").Logger(),
		enabled: true,
	}
}

// SetThreadPriority sets the priority of the current thread
func (ps *PriorityScheduler) SetThreadPriority(priority int, policy int) error {
	if !ps.enabled {
		return nil
	}

	// Lock to OS thread to ensure we're setting priority for the right thread
	runtime.LockOSThread()

	// Get current thread ID
	tid := syscall.Gettid()

	// Set scheduling parameters
	param := &SchedParam{
		Priority: int32(priority),
	}

	// Use syscall to set scheduler
	_, _, errno := syscall.Syscall(syscall.SYS_SCHED_SETSCHEDULER,
		uintptr(tid),
		uintptr(policy),
		uintptr(unsafe.Pointer(param)))

	if errno != 0 {
		// If we can't set real-time priority, try nice value instead
		schedNormal, _, _ := getSchedulingPolicies()
		if policy != schedNormal {
			ps.logger.Warn().Int("errno", int(errno)).Msg("failed to set real-time priority, falling back to nice")
			return ps.setNicePriority(priority)
		}
		return errno
	}

	ps.logger.Debug().Int("tid", tid).Int("priority", priority).Int("policy", policy).Msg("thread priority set")
	return nil
}

// setNicePriority sets nice value as fallback when real-time scheduling is not available
func (ps *PriorityScheduler) setNicePriority(rtPriority int) error {
	// Convert real-time priority to nice value (inverse relationship)
	// RT priority 80 -> nice -10, RT priority 40 -> nice 0
	niceValue := (40 - rtPriority) / 4
	if niceValue < GetConfig().MinNiceValue {
		niceValue = GetConfig().MinNiceValue
	}
	if niceValue > GetConfig().MaxNiceValue {
		niceValue = GetConfig().MaxNiceValue
	}

	err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, niceValue)
	if err != nil {
		ps.logger.Warn().Err(err).Int("nice", niceValue).Msg("failed to set nice priority")
		return err
	}

	ps.logger.Debug().Int("nice", niceValue).Msg("nice priority set as fallback")
	return nil
}

// SetAudioProcessingPriority sets high priority for audio processing threads
func (ps *PriorityScheduler) SetAudioProcessingPriority() error {
	audioHigh, _, _, _ := getPriorityConstants()
	_, schedFIFO, _ := getSchedulingPolicies()
	return ps.SetThreadPriority(audioHigh, schedFIFO)
}

// SetAudioIOPriority sets medium priority for audio I/O threads
func (ps *PriorityScheduler) SetAudioIOPriority() error {
	_, audioMedium, _, _ := getPriorityConstants()
	_, schedFIFO, _ := getSchedulingPolicies()
	return ps.SetThreadPriority(audioMedium, schedFIFO)
}

// SetAudioBackgroundPriority sets low priority for background audio tasks
func (ps *PriorityScheduler) SetAudioBackgroundPriority() error {
	_, _, audioLow, _ := getPriorityConstants()
	_, schedFIFO, _ := getSchedulingPolicies()
	return ps.SetThreadPriority(audioLow, schedFIFO)
}

// ResetPriority resets thread to normal scheduling
func (ps *PriorityScheduler) ResetPriority() error {
	_, _, _, normal := getPriorityConstants()
	schedNormal, _, _ := getSchedulingPolicies()
	return ps.SetThreadPriority(normal, schedNormal)
}

// Disable disables priority scheduling (useful for testing or fallback)
func (ps *PriorityScheduler) Disable() {
	ps.enabled = false
	ps.logger.Debug().Msg("priority scheduling disabled")
}

// Enable enables priority scheduling
func (ps *PriorityScheduler) Enable() {
	ps.enabled = true
	ps.logger.Debug().Msg("priority scheduling enabled")
}

// Global priority scheduler instance
var globalPriorityScheduler *PriorityScheduler

// GetPriorityScheduler returns the global priority scheduler instance
func GetPriorityScheduler() *PriorityScheduler {
	if globalPriorityScheduler == nil {
		globalPriorityScheduler = NewPriorityScheduler()
	}
	return globalPriorityScheduler
}

// SetAudioThreadPriority is a convenience function to set audio processing priority
func SetAudioThreadPriority() error {
	return GetPriorityScheduler().SetAudioProcessingPriority()
}

// SetAudioIOThreadPriority is a convenience function to set audio I/O priority
func SetAudioIOThreadPriority() error {
	return GetPriorityScheduler().SetAudioIOPriority()
}

// ResetThreadPriority is a convenience function to reset thread priority
func ResetThreadPriority() error {
	return GetPriorityScheduler().ResetPriority()
}
