package domain

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

// Allowed preference keys.
var allowedKeys = map[string]bool{
	"theme":                 true,
	"language":              true,
	"notifications_enabled": true,
}

// Valid theme values.
var validThemes = map[string]bool{
	"light": true,
	"dark":  true,
}

// languagePattern matches ISO 639-1 codes (two lowercase letters).
var languagePattern = regexp.MustCompile(`^[a-z]{2}$`)

// UserPreferences represents a user's stored preferences.
type UserPreferences struct {
	UserID      string
	Preferences map[string]any
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

// ValidatePreferences validates all keys and values in a preferences map.
func ValidatePreferences(prefs map[string]any) error {
	for key, value := range prefs {
		if err := ValidatePreferenceKey(key); err != nil {
			return err
		}
		if err := ValidatePreferenceValue(key, value); err != nil {
			return err
		}
	}
	return nil
}

// ValidatePreferenceKey checks that the key is in the allowed set.
func ValidatePreferenceKey(key string) error {
	if !allowedKeys[key] {
		return fmt.Errorf("%w: %s", ErrInvalidPreferenceKey, key)
	}
	return nil
}

// ValidatePreferenceValue checks that the value is valid for the given key.
func ValidatePreferenceValue(key string, value any) error {
	switch key {
	case "theme":
		s, ok := value.(string)
		if !ok {
			return fmt.Errorf("%w: theme must be a string", ErrInvalidPreferenceValue)
		}
		if !validThemes[s] {
			return fmt.Errorf("%w: theme must be \"light\" or \"dark\", got %q", ErrInvalidPreferenceValue, s)
		}
	case "language":
		s, ok := value.(string)
		if !ok {
			return fmt.Errorf("%w: language must be a string", ErrInvalidPreferenceValue)
		}
		if !languagePattern.MatchString(s) {
			return fmt.Errorf("%w: language must be a valid ISO 639-1 code (e.g. \"en\"), got %q", ErrInvalidPreferenceValue, s)
		}
	case "notifications_enabled":
		if _, ok := value.(bool); !ok {
			return fmt.Errorf("%w: notifications_enabled must be a boolean", ErrInvalidPreferenceValue)
		}
	}
	return nil
}
