package utils

import (
	"fmt"
	"regexp"
	"time"
)

// ID length constants matching nanoid generation
const (
	ShortIDLen = 12 // dataset, location, cluster, pattern, species, filter, call_type
)

// Sample rate reasonable bounds for audio recording
const (
	MinSampleRate = 1000   // 1 kHz - below this is unlikely to be real audio
	MaxSampleRate = 500000 // 500 kHz - well above bat detectors (~250kHz)
)

// Max string lengths from schema
const (
	MaxNameLen        = 140 // location.name, cluster.name
	MaxDatasetNameLen = 255 // dataset.name
	MaxDescriptionLen = 255 // all description fields
	MaxPathLen        = 255 // cluster.path
	MaxFileNameLen    = 255 // file.file_name
	MaxTimezoneLen    = 40  // location.timezone_id
)

// ID format regex - alphanumeric characters (nanoid uses A-Za-z0-9_)
var shortIDRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{12}$`)

// ValidateShortID validates 12-character nanoid format
func ValidateShortID(id, fieldName string) error {
	if id == "" {
		return fmt.Errorf("%s cannot be empty", fieldName)
	}
	if len(id) != ShortIDLen {
		return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, ShortIDLen, len(id))
	}
	if !shortIDRegex.MatchString(id) {
		return fmt.Errorf("%s has invalid format (expected alphanumeric nanoid)", fieldName)
	}
	return nil
}

// ValidateOptionalShortID validates short ID if provided (non-empty)
func ValidateOptionalShortID(id *string, fieldName string) error {
	if id == nil || *id == "" {
		return nil
	}
	return ValidateShortID(*id, fieldName)
}

// ValidateStringLength validates string length constraint
func ValidateStringLength(value, fieldName string, maxLen int) error {
	if len(value) > maxLen {
		return fmt.Errorf("%s must be %d characters or less (got %d)", fieldName, maxLen, len(value))
	}
	return nil
}

// ValidateOptionalStringLength validates string length if provided
func ValidateOptionalStringLength(value *string, fieldName string, maxLen int) error {
	if value == nil || *value == "" {
		return nil
	}
	return ValidateStringLength(*value, fieldName, maxLen)
}

// ValidateRange validates numeric range constraint (inclusive)
func ValidateRange[T int | float64](value T, fieldName string, min, max T) error {
	if value < min || value > max {
		return fmt.Errorf("%s must be between %v and %v (got %v)", fieldName, min, max, value)
	}
	return nil
}

// ValidatePositive validates positive number (> 0)
func ValidatePositive[T int | float64](value T, fieldName string) error {
	if value <= 0 {
		return fmt.Errorf("%s must be positive (got %v)", fieldName, value)
	}
	return nil
}

// ValidateNonNegative validates non-negative number (>= 0)
func ValidateNonNegative[T int | float64](value T, fieldName string) error {
	if value < 0 {
		return fmt.Errorf("%s must be non-negative (got %v)", fieldName, value)
	}
	return nil
}

// ValidateSampleRate validates audio sample rate is in reasonable range
func ValidateSampleRate(rate int) error {
	return ValidateRange(rate, "sample_rate", MinSampleRate, MaxSampleRate)
}

// ValidateTimezone validates IANA timezone ID
func ValidateTimezone(tzID string) error {
	if _, err := time.LoadLocation(tzID); err != nil {
		return fmt.Errorf("invalid timezone_id '%s': %w", tzID, err)
	}
	return nil
}