//go:build cgo || arm
// +build cgo arm

package audio

import (
	"errors"
	"fmt"
	"time"
)

// Validation errors
var (
	ErrInvalidAudioQuality = errors.New("invalid audio quality level")
	ErrInvalidFrameSize    = errors.New("invalid frame size")
	ErrInvalidFrameData    = errors.New("invalid frame data")
	ErrFrameDataEmpty      = errors.New("invalid frame data: frame data is empty")
	ErrFrameDataTooLarge   = errors.New("invalid frame data: exceeds maximum")
	ErrInvalidBufferSize   = errors.New("invalid buffer size")

	ErrInvalidLatency         = errors.New("invalid latency value")
	ErrInvalidConfiguration   = errors.New("invalid configuration")
	ErrInvalidSocketConfig    = errors.New("invalid socket configuration")
	ErrInvalidMetricsInterval = errors.New("invalid metrics interval")
	ErrInvalidSampleRate      = errors.New("invalid sample rate")
	ErrInvalidChannels        = errors.New("invalid channels")
	ErrInvalidBitrate         = errors.New("invalid bitrate")
	ErrInvalidFrameDuration   = errors.New("invalid frame duration")
	ErrInvalidOffset          = errors.New("invalid offset")
	ErrInvalidLength          = errors.New("invalid length")
)

// ValidateAudioQuality validates audio quality enum values with enhanced checks
func ValidateAudioQuality(quality AudioQuality) error {
	// Validate enum range
	if quality < AudioQualityLow || quality > AudioQualityUltra {
		return fmt.Errorf("%w: quality value %d outside valid range [%d, %d]",
			ErrInvalidAudioQuality, int(quality), int(AudioQualityLow), int(AudioQualityUltra))
	}
	return nil
}

// ValidateZeroCopyFrame validates zero-copy audio frame
// Optimized to use cached max frame size
func ValidateZeroCopyFrame(frame *ZeroCopyAudioFrame) error {
	if frame == nil {
		return ErrInvalidFrameData
	}
	data := frame.Data()
	if len(data) == 0 {
		return ErrInvalidFrameData
	}

	// Fast path: use cached max frame size
	maxFrameSize := cachedMaxFrameSize
	if maxFrameSize == 0 {
		// Fallback: get from cache
		cache := GetCachedConfig()
		maxFrameSize = int(cache.maxAudioFrameSize.Load())
		if maxFrameSize == 0 {
			// Last resort: update cache
			cache.Update()
			maxFrameSize = int(cache.maxAudioFrameSize.Load())
		}
		// Cache globally for next calls
		cachedMaxFrameSize = maxFrameSize
	}

	if len(data) > maxFrameSize {
		return ErrInvalidFrameSize
	}
	return nil
}

// ValidateBufferSize validates buffer size parameters with enhanced boundary checks
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateBufferSize(size int) error {
	if size <= 0 {
		return fmt.Errorf("%w: buffer size %d must be positive", ErrInvalidBufferSize, size)
	}

	// Fast path: Check against cached max frame size
	cache := GetCachedConfig()
	maxFrameSize := int(cache.maxAudioFrameSize.Load())

	// Most common case: validating a buffer that's sized for audio frames
	if maxFrameSize > 0 && size <= maxFrameSize {
		return nil
	}

	// Slower path: full validation against SocketMaxBuffer
	config := GetConfig()
	// Use SocketMaxBuffer as the upper limit for general buffer validation
	// This allows for socket buffers while still preventing extremely large allocations
	if size > config.SocketMaxBuffer {
		return fmt.Errorf("%w: buffer size %d exceeds maximum %d",
			ErrInvalidBufferSize, size, config.SocketMaxBuffer)
	}
	return nil
}

// ValidateLatency validates latency duration values with reasonable bounds
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateLatency(latency time.Duration) error {
	if latency < 0 {
		return fmt.Errorf("%w: latency %v cannot be negative", ErrInvalidLatency, latency)
	}

	// Fast path: check against cached max latency
	cache := GetCachedConfig()
	maxLatency := time.Duration(cache.maxLatency.Load())

	// If we have a valid cached value, use it
	if maxLatency > 0 {
		minLatency := time.Millisecond // Minimum reasonable latency
		if latency > 0 && latency < minLatency {
			return fmt.Errorf("%w: latency %v below minimum %v",
				ErrInvalidLatency, latency, minLatency)
		}
		if latency > maxLatency {
			return fmt.Errorf("%w: latency %v exceeds maximum %v",
				ErrInvalidLatency, latency, maxLatency)
		}
		return nil
	}

	// Slower path: full validation with GetConfig()
	config := GetConfig()
	minLatency := time.Millisecond // Minimum reasonable latency
	if latency > 0 && latency < minLatency {
		return fmt.Errorf("%w: latency %v below minimum %v",
			ErrInvalidLatency, latency, minLatency)
	}
	if latency > config.MaxLatency {
		return fmt.Errorf("%w: latency %v exceeds maximum %v",
			ErrInvalidLatency, latency, config.MaxLatency)
	}
	return nil
}

// ValidateMetricsInterval validates metrics update interval
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateMetricsInterval(interval time.Duration) error {
	// Fast path: check against cached values
	cache := GetCachedConfig()
	minInterval := time.Duration(cache.minMetricsUpdateInterval.Load())
	maxInterval := time.Duration(cache.maxMetricsUpdateInterval.Load())

	// If we have valid cached values, use them
	if minInterval > 0 && maxInterval > 0 {
		if interval < minInterval {
			return fmt.Errorf("%w: interval %v below minimum %v",
				ErrInvalidMetricsInterval, interval, minInterval)
		}
		if interval > maxInterval {
			return fmt.Errorf("%w: interval %v exceeds maximum %v",
				ErrInvalidMetricsInterval, interval, maxInterval)
		}
		return nil
	}

	// Slower path: full validation with GetConfig()
	config := GetConfig()
	minInterval = config.MinMetricsUpdateInterval
	maxInterval = config.MaxMetricsUpdateInterval
	if interval < minInterval {
		return ErrInvalidMetricsInterval
	}
	if interval > maxInterval {
		return ErrInvalidMetricsInterval
	}
	return nil
}

// ValidateAdaptiveBufferConfig validates adaptive buffer configuration
func ValidateAdaptiveBufferConfig(minSize, maxSize, defaultSize int) error {
	if minSize <= 0 || maxSize <= 0 || defaultSize <= 0 {
		return ErrInvalidBufferSize
	}
	if minSize >= maxSize {
		return ErrInvalidBufferSize
	}
	if defaultSize < minSize || defaultSize > maxSize {
		return ErrInvalidBufferSize
	}
	// Validate against global limits
	maxBuffer := GetConfig().SocketMaxBuffer
	if maxSize > maxBuffer {
		return ErrInvalidBufferSize
	}
	return nil
}

// ValidateInputIPCConfig validates input IPC configuration
func ValidateInputIPCConfig(sampleRate, channels, frameSize int) error {
	// Use config values
	config := GetConfig()
	minSampleRate := config.MinSampleRate
	maxSampleRate := config.MaxSampleRate
	maxChannels := config.MaxChannels
	if sampleRate < minSampleRate || sampleRate > maxSampleRate {
		return ErrInvalidSampleRate
	}
	if channels < 1 || channels > maxChannels {
		return ErrInvalidChannels
	}
	if frameSize <= 0 {
		return ErrInvalidFrameSize
	}
	return nil
}

// ValidateOutputIPCConfig validates output IPC configuration
func ValidateOutputIPCConfig(sampleRate, channels, frameSize int) error {
	// Use config values
	config := GetConfig()
	minSampleRate := config.MinSampleRate
	maxSampleRate := config.MaxSampleRate
	maxChannels := config.MaxChannels
	if sampleRate < minSampleRate || sampleRate > maxSampleRate {
		return ErrInvalidSampleRate
	}
	if channels < 1 || channels > maxChannels {
		return ErrInvalidChannels
	}
	if frameSize <= 0 {
		return ErrInvalidFrameSize
	}
	return nil
}

// ValidateLatencyConfig validates latency monitor configuration
func ValidateLatencyConfig(config LatencyConfig) error {
	if err := ValidateLatency(config.TargetLatency); err != nil {
		return err
	}
	if err := ValidateLatency(config.MaxLatency); err != nil {
		return err
	}
	if config.TargetLatency >= config.MaxLatency {
		return ErrInvalidLatency
	}
	if err := ValidateMetricsInterval(config.OptimizationInterval); err != nil {
		return err
	}
	if config.HistorySize <= 0 {
		return ErrInvalidBufferSize
	}
	if config.JitterThreshold < 0 {
		return ErrInvalidLatency
	}
	if config.AdaptiveThreshold < 0 || config.AdaptiveThreshold > 1.0 {
		return ErrInvalidConfiguration
	}
	return nil
}

// ValidateSampleRate validates audio sample rate values
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateSampleRate(sampleRate int) error {
	if sampleRate <= 0 {
		return fmt.Errorf("%w: sample rate %d must be positive", ErrInvalidSampleRate, sampleRate)
	}

	// Fast path: Check against cached sample rate first
	cache := GetCachedConfig()
	cachedRate := int(cache.sampleRate.Load())

	// Most common case: validating against the current sample rate
	if sampleRate == cachedRate {
		return nil
	}

	// Slower path: check against all valid rates
	config := GetConfig()
	validRates := config.ValidSampleRates
	for _, rate := range validRates {
		if sampleRate == rate {
			return nil
		}
	}
	return fmt.Errorf("%w: sample rate %d not in supported rates %v",
		ErrInvalidSampleRate, sampleRate, validRates)
}

// ValidateChannelCount validates audio channel count
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateChannelCount(channels int) error {
	if channels <= 0 {
		return fmt.Errorf("%w: channel count %d must be positive", ErrInvalidChannels, channels)
	}

	// Fast path: Check against cached channels first
	cache := GetCachedConfig()
	cachedChannels := int(cache.channels.Load())

	// Most common case: validating against the current channel count
	if channels == cachedChannels {
		return nil
	}

	// Fast path: Check against cached max channels
	cachedMaxChannels := int(cache.maxChannels.Load())
	if cachedMaxChannels > 0 && channels <= cachedMaxChannels {
		return nil
	}

	// Slow path: Update cache and validate
	cache.Update()
	updatedMaxChannels := int(cache.maxChannels.Load())
	if channels > updatedMaxChannels {
		return fmt.Errorf("%w: channel count %d exceeds maximum %d",
			ErrInvalidChannels, channels, updatedMaxChannels)
	}
	return nil
}

// ValidateBitrate validates audio bitrate values (expects kbps)
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateBitrate(bitrate int) error {
	if bitrate <= 0 {
		return fmt.Errorf("%w: bitrate %d must be positive", ErrInvalidBitrate, bitrate)
	}

	// Fast path: Check against cached bitrate values
	cache := GetCachedConfig()
	minBitrate := int(cache.minOpusBitrate.Load())
	maxBitrate := int(cache.maxOpusBitrate.Load())

	// If we have valid cached values, use them
	if minBitrate > 0 && maxBitrate > 0 {
		// Convert kbps to bps for comparison with config limits
		bitrateInBps := bitrate * 1000
		if bitrateInBps < minBitrate {
			return fmt.Errorf("%w: bitrate %d kbps (%d bps) below minimum %d bps",
				ErrInvalidBitrate, bitrate, bitrateInBps, minBitrate)
		}
		if bitrateInBps > maxBitrate {
			return fmt.Errorf("%w: bitrate %d kbps (%d bps) exceeds maximum %d bps",
				ErrInvalidBitrate, bitrate, bitrateInBps, maxBitrate)
		}
		return nil
	}

	// Slower path: full validation with GetConfig()
	config := GetConfig()
	// Convert kbps to bps for comparison with config limits
	bitrateInBps := bitrate * 1000
	if bitrateInBps < config.MinOpusBitrate {
		return fmt.Errorf("%w: bitrate %d kbps (%d bps) below minimum %d bps",
			ErrInvalidBitrate, bitrate, bitrateInBps, config.MinOpusBitrate)
	}
	if bitrateInBps > config.MaxOpusBitrate {
		return fmt.Errorf("%w: bitrate %d kbps (%d bps) exceeds maximum %d bps",
			ErrInvalidBitrate, bitrate, bitrateInBps, config.MaxOpusBitrate)
	}
	return nil
}

// ValidateFrameDuration validates frame duration values
// Optimized to use AudioConfigCache for frequently accessed values
func ValidateFrameDuration(duration time.Duration) error {
	if duration <= 0 {
		return fmt.Errorf("%w: frame duration %v must be positive", ErrInvalidFrameDuration, duration)
	}

	// Fast path: Check against cached frame size first
	cache := GetCachedConfig()

	// Convert frameSize (samples) to duration for comparison
	// Note: This calculation should match how frameSize is converted to duration elsewhere
	cachedFrameSize := int(cache.frameSize.Load())
	cachedSampleRate := int(cache.sampleRate.Load())

	// Only do this calculation if we have valid cached values
	if cachedFrameSize > 0 && cachedSampleRate > 0 {
		cachedDuration := time.Duration(cachedFrameSize) * time.Second / time.Duration(cachedSampleRate)

		// Most common case: validating against the current frame duration
		if duration == cachedDuration {
			return nil
		}
	}

	// Fast path: Check against cached min/max frame duration
	cachedMinDuration := time.Duration(cache.minFrameDuration.Load())
	cachedMaxDuration := time.Duration(cache.maxFrameDuration.Load())

	if cachedMinDuration > 0 && cachedMaxDuration > 0 {
		if duration < cachedMinDuration {
			return fmt.Errorf("%w: frame duration %v below minimum %v",
				ErrInvalidFrameDuration, duration, cachedMinDuration)
		}
		if duration > cachedMaxDuration {
			return fmt.Errorf("%w: frame duration %v exceeds maximum %v",
				ErrInvalidFrameDuration, duration, cachedMaxDuration)
		}
		return nil
	}

	// Slow path: Update cache and validate
	cache.Update()
	updatedMinDuration := time.Duration(cache.minFrameDuration.Load())
	updatedMaxDuration := time.Duration(cache.maxFrameDuration.Load())

	if duration < updatedMinDuration {
		return fmt.Errorf("%w: frame duration %v below minimum %v",
			ErrInvalidFrameDuration, duration, updatedMinDuration)
	}
	if duration > updatedMaxDuration {
		return fmt.Errorf("%w: frame duration %v exceeds maximum %v",
			ErrInvalidFrameDuration, duration, updatedMaxDuration)
	}
	return nil
}

// ValidateAudioConfigComplete performs comprehensive audio configuration validation
// Uses optimized validation functions that leverage AudioConfigCache
func ValidateAudioConfigComplete(config AudioConfig) error {
	// Fast path: Check if all values match the current cached configuration
	cache := GetCachedConfig()
	cachedSampleRate := int(cache.sampleRate.Load())
	cachedChannels := int(cache.channels.Load())
	cachedBitrate := int(cache.opusBitrate.Load()) / 1000 // Convert from bps to kbps
	cachedFrameSize := int(cache.frameSize.Load())

	// Only do this calculation if we have valid cached values
	if cachedSampleRate > 0 && cachedChannels > 0 && cachedBitrate > 0 && cachedFrameSize > 0 {
		cachedDuration := time.Duration(cachedFrameSize) * time.Second / time.Duration(cachedSampleRate)

		// Most common case: validating the current configuration
		if config.SampleRate == cachedSampleRate &&
			config.Channels == cachedChannels &&
			config.Bitrate == cachedBitrate &&
			config.FrameSize == cachedDuration {
			return nil
		}
	}

	// Slower path: validate each parameter individually
	if err := ValidateAudioQuality(config.Quality); err != nil {
		return fmt.Errorf("quality validation failed: %w", err)
	}
	if err := ValidateBitrate(config.Bitrate); err != nil {
		return fmt.Errorf("bitrate validation failed: %w", err)
	}
	if err := ValidateSampleRate(config.SampleRate); err != nil {
		return fmt.Errorf("sample rate validation failed: %w", err)
	}
	if err := ValidateChannelCount(config.Channels); err != nil {
		return fmt.Errorf("channel count validation failed: %w", err)
	}
	if err := ValidateFrameDuration(config.FrameSize); err != nil {
		return fmt.Errorf("frame duration validation failed: %w", err)
	}
	return nil
}

// ValidateAudioConfigConstants validates audio configuration constants
func ValidateAudioConfigConstants(config *AudioConfigConstants) error {
	// Validate that audio quality constants are within valid ranges
	for _, quality := range []AudioQuality{AudioQualityLow, AudioQualityMedium, AudioQualityHigh, AudioQualityUltra} {
		if err := ValidateAudioQuality(quality); err != nil {
			return fmt.Errorf("invalid audio quality constant %v: %w", quality, err)
		}
	}
	// Validate configuration values if config is provided
	if config != nil {
		if config.MaxFrameSize <= 0 {
			return fmt.Errorf("invalid MaxFrameSize: %d", config.MaxFrameSize)
		}
		if config.SampleRate <= 0 {
			return fmt.Errorf("invalid SampleRate: %d", config.SampleRate)
		}
	}
	return nil
}

// Note: We're transitioning from individual cached values to using AudioConfigCache
// for better consistency and reduced maintenance overhead

// Global variable for backward compatibility
var cachedMaxFrameSize int

// InitValidationCache initializes cached validation values with actual config
func InitValidationCache() {
	// Initialize the global cache variable for backward compatibility
	config := GetConfig()
	cachedMaxFrameSize = config.MaxAudioFrameSize

	// Update the global audio config cache
	GetCachedConfig().Update()
}

// ValidateAudioFrame provides optimized validation for audio frame data
// This is the primary validation function used in all audio processing paths
//
// Performance optimizations:
// - Uses cached max frame size to eliminate config lookups
// - Single branch condition for optimal CPU pipeline efficiency
// - Minimal error allocation overhead
//
//go:inline
func ValidateAudioFrame(data []byte) error {
	// Fast path: check length against cached max size in single operation
	dataLen := len(data)
	if dataLen == 0 {
		return ErrFrameDataEmpty
	}

	// Use global cached value for fastest access - updated during initialization
	maxSize := cachedMaxFrameSize
	if maxSize == 0 {
		// Fallback: get from cache only if global cache not initialized
		cache := GetCachedConfig()
		maxSize = int(cache.maxAudioFrameSize.Load())
		if maxSize == 0 {
			// Last resort: update cache and get fresh value
			cache.Update()
			maxSize = int(cache.maxAudioFrameSize.Load())
		}
		// Cache the value globally for next calls
		cachedMaxFrameSize = maxSize
	}

	// Single comparison for validation
	if dataLen > maxSize {
		return ErrFrameDataTooLarge
	}
	return nil
}

// WrapWithMetadata wraps error with metadata for enhanced validation context
func WrapWithMetadata(err error, component, operation string, metadata map[string]interface{}) error {
	if err == nil {
		return nil
	}
	return fmt.Errorf("%s.%s: %w (metadata: %+v)", component, operation, err, metadata)
}
