package questiongen

import (
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"

	"boostai-backend/internal/sqlc"
)

type Service struct{}

type GenerateParams struct {
	Topic      sqlc.QuestionTopic
	Difficulty sqlc.QuestionDifficulty
	Count      int
	Seed       int64
}

type GeneratedQuestion struct {
	Title          string
	Prompt         string
	CorrectAnswer  string
	WorkedSolution []string
	Tags           []string
}

func NewService() *Service {
	return &Service{}
}

func (s *Service) Generate(params GenerateParams) ([]GeneratedQuestion, int64, error) {
	count := params.Count
	if count <= 0 {
		count = 1
	}

	seed := params.Seed
	if seed == 0 {
		seed = time.Now().UnixNano()
	}

	rng := rand.New(rand.NewSource(seed))
	items := make([]GeneratedQuestion, 0, count)
	for i := 0; i < count; i++ {
		question, err := s.generateOne(rng, params.Topic, params.Difficulty)
		if err != nil {
			return nil, seed, err
		}
		items = append(items, question)
	}

	return items, seed, nil
}

func (s *Service) generateOne(rng *rand.Rand, topic sqlc.QuestionTopic, difficulty sqlc.QuestionDifficulty) (GeneratedQuestion, error) {
	switch topic {
	case sqlc.QuestionTopicPlaceValue:
		return generatePlaceValueQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicArithmetic:
		return generateArithmeticQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicNegativeNumbers:
		return generateNegativeNumbersQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicBidmas:
		return generateBidmasQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicFractions:
		return generateFractionsQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicAlgebra:
		return generateAlgebraQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicGeometry:
		return generateGeometryQuestion(rng, difficulty), nil
	case sqlc.QuestionTopicData:
		return generateDataQuestion(rng, difficulty), nil
	default:
		return GeneratedQuestion{}, fmt.Errorf("unsupported topic: %s", topic)
	}
}

// Future word_problem work should not just bolt a `word_problem` tag onto an already-built
// abstract question. Each topic generator should eventually expose dedicated word-problem
// template families so the RNG chooses both the maths structure and a fitting real-world context
// together. That will keep prompts, answers, and worked steps consistent instead of doing a late
// text rewrite after the numbers are chosen.
func buildGeneratedTags(topic sqlc.QuestionTopic, difficulty sqlc.QuestionDifficulty, extra ...string) []string {
	tags := []string{string(topic), string(difficulty), "rng_generated"}
	tags = append(tags, extra...)

	unique := make(map[string]struct{}, len(tags))
	normalized := make([]string, 0, len(tags))
	for _, tag := range tags {
		value := strings.ToLower(strings.TrimSpace(tag))
		if value == "" {
			continue
		}
		if _, exists := unique[value]; exists {
			continue
		}
		unique[value] = struct{}{}
		normalized = append(normalized, value)
	}
	sort.Strings(normalized)
	return normalized
}

func generatePlaceValueQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	var digits, targetIndex int
	switch difficulty {
	case sqlc.QuestionDifficultyEasy:
		digits = 2
		targetIndex = randomInt(rng, 0, 1)
	case sqlc.QuestionDifficultyMedium:
		digits = 3
		targetIndex = randomInt(rng, 0, 2)
	default:
		digits = randomInt(rng, 4, 5)
		targetIndex = randomInt(rng, 1, digits-1)
	}

	numberDigits := make([]int, digits)
	numberDigits[0] = randomInt(rng, 1, 9)
	for i := 1; i < digits; i++ {
		numberDigits[i] = randomInt(rng, 0, 9)
	}
	number := digitsToInt(numberDigits)
	digit := numberDigits[targetIndex]
	placePower := digits - targetIndex - 1
	placeValue := digit
	for i := 0; i < placePower; i++ {
		placeValue *= 10
	}

	placeName := placeNameFromPower(placePower)
	prompt := fmt.Sprintf("What is the value of the digit %d in %d?", digit, number)
	return GeneratedQuestion{
		Title:         fmt.Sprintf("%s Place Value", strings.Title(string(difficulty))),
		Prompt:        prompt,
		CorrectAnswer: fmt.Sprintf("%d", placeValue),
		WorkedSolution: []string{
			fmt.Sprintf("In %d, the digit %d is in the %s place.", number, digit, placeName),
			fmt.Sprintf("So its value is %d.", placeValue),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicPlaceValue, difficulty, placeName),
	}
}

func generateArithmeticQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		a := randomInt(rng, 1, 9)
		b := randomInt(rng, 1, 9)
		if rng.Intn(2) == 0 {
			return GeneratedQuestion{
				Title:         "Easy Addition",
				Prompt:        fmt.Sprintf("Calculate %d + %d.", a, b),
				CorrectAnswer: fmt.Sprintf("%d", a+b),
				WorkedSolution: []string{
					fmt.Sprintf("Add the ones: %d + %d = %d.", a, b, a+b),
				},
				Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "addition", "single_digit"),
			}
		}

		if a < b {
			a, b = b, a
		}
		return GeneratedQuestion{
			Title:         "Easy Subtraction",
			Prompt:        fmt.Sprintf("Calculate %d - %d.", a, b),
			CorrectAnswer: fmt.Sprintf("%d", a-b),
			WorkedSolution: []string{
				fmt.Sprintf("Subtract %d from %d.", b, a),
				fmt.Sprintf("%d - %d = %d.", a, b, a-b),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "subtraction", "single_digit"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		if rng.Intn(2) == 0 {
			a := randomInt(rng, 10, 99)
			b := randomInt(rng, 10, 99)
			return GeneratedQuestion{
				Title:         "Medium Addition",
				Prompt:        fmt.Sprintf("Work out %d + %d.", a, b),
				CorrectAnswer: fmt.Sprintf("%d", a+b),
				WorkedSolution: []string{
					fmt.Sprintf("Add the numbers together: %d + %d = %d.", a, b, a+b),
				},
				Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "addition", "two_digit"),
			}
		}

		a := randomInt(rng, 2, 12)
		b := randomInt(rng, 2, 12)
		return GeneratedQuestion{
			Title:         "Medium Multiplication",
			Prompt:        fmt.Sprintf("Calculate %d × %d.", a, b),
			CorrectAnswer: fmt.Sprintf("%d", a*b),
			WorkedSolution: []string{
				fmt.Sprintf("Use multiplication facts: %d × %d = %d.", a, b, a*b),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "multiplication", "times_tables"),
		}
	}

	if rng.Intn(2) == 0 {
		divisor := randomInt(rng, 3, 12)
		quotient := randomInt(rng, 4, 12)
		dividend := divisor * quotient
		return GeneratedQuestion{
			Title:         "Hard Division",
			Prompt:        fmt.Sprintf("Calculate %d ÷ %d.", dividend, divisor),
			CorrectAnswer: fmt.Sprintf("%d", quotient),
			WorkedSolution: []string{
				fmt.Sprintf("Use the inverse of multiplication: %d × %d = %d.", divisor, quotient, dividend),
				fmt.Sprintf("So %d ÷ %d = %d.", dividend, divisor, quotient),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "division", "inverse_operations"),
		}
	}

	a := randomInt(rng, 20, 99)
	b := randomInt(rng, 11, 49)
	return GeneratedQuestion{
		Title:         "Hard Subtraction",
		Prompt:        fmt.Sprintf("Work out %d - %d.", a, b),
		CorrectAnswer: fmt.Sprintf("%d", a-b),
		WorkedSolution: []string{
			fmt.Sprintf("Subtract %d from %d carefully, using column subtraction if needed.", b, a),
			fmt.Sprintf("%d - %d = %d.", a, b, a-b),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicArithmetic, difficulty, "subtraction", "column_method"),
	}
}

func generateNegativeNumbersQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		a := randomInt(rng, -9, 9)
		b := randomInt(rng, -9, 9)
		result := a + b
		return GeneratedQuestion{
			Title:         "Easy Negative Numbers",
			Prompt:        fmt.Sprintf("Calculate %d + %d.", a, b),
			CorrectAnswer: fmt.Sprintf("%d", result),
			WorkedSolution: []string{
				fmt.Sprintf("Start at %d on the number line.", a),
				fmt.Sprintf("Move %d steps to get %d.", b, result),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicNegativeNumbers, difficulty, "addition"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		a := randomInt(rng, -20, 20)
		b := randomInt(rng, -20, 20)
		result := a - b
		return GeneratedQuestion{
			Title:         "Medium Negative Numbers",
			Prompt:        fmt.Sprintf("Calculate %d - (%d).", a, b),
			CorrectAnswer: fmt.Sprintf("%d", result),
			WorkedSolution: []string{
				fmt.Sprintf("Subtracting %d is the same as adding %d.", b, -b),
				fmt.Sprintf("So %d - (%d) = %d + %d = %d.", a, b, a, -b, result),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicNegativeNumbers, difficulty, "subtraction"),
		}
	}

	a := randomInt(rng, -30, 30)
	b := randomInt(rng, -30, 30)
	c := randomInt(rng, -15, 15)
	result := a - b + c
	prompt := fmt.Sprintf("Calculate %d - (%d) + %d.", a, b, c)
	return GeneratedQuestion{
		Title:         "Hard Negative Numbers",
		Prompt:        prompt,
		CorrectAnswer: fmt.Sprintf("%d", result),
		WorkedSolution: []string{
			fmt.Sprintf("First change subtraction of a negative: %d - (%d) = %d + %d.", a, b, a, -b),
			fmt.Sprintf("Then add %d to get %d.", c, result),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicNegativeNumbers, difficulty, "multi_step"),
	}
}

func generateBidmasQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		a := randomInt(rng, 1, 20)
		b := randomInt(rng, 2, 9)
		c := randomInt(rng, 2, 9)
		result := a + b*c
		return GeneratedQuestion{
			Title:         "Easy BIDMAS",
			Prompt:        fmt.Sprintf("Work out %d + %d × %d.", a, b, c),
			CorrectAnswer: fmt.Sprintf("%d", result),
			WorkedSolution: []string{
				fmt.Sprintf("Do multiplication first: %d × %d = %d.", b, c, b*c),
				fmt.Sprintf("Then add %d + %d = %d.", a, b*c, result),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicBidmas, difficulty, "order_of_operations"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		a := randomInt(rng, 2, 12)
		b := randomInt(rng, 3, 12)
		c := randomInt(rng, 2, 10)
		result := (a + b) * c
		return GeneratedQuestion{
			Title:         "Medium BIDMAS",
			Prompt:        fmt.Sprintf("Work out (%d + %d) × %d.", a, b, c),
			CorrectAnswer: fmt.Sprintf("%d", result),
			WorkedSolution: []string{
				fmt.Sprintf("Work inside brackets first: %d + %d = %d.", a, b, a+b),
				fmt.Sprintf("Then multiply %d × %d = %d.", a+b, c, result),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicBidmas, difficulty, "brackets"),
		}
	}

	a := randomInt(rng, 2, 12)
	b := randomInt(rng, 2, 6)
	c := randomInt(rng, 2, 12)
	d := randomInt(rng, 2, 5)
	left := a * b
	right := c * d
	result := left + right
	return GeneratedQuestion{
		Title:         "Hard BIDMAS",
		Prompt:        fmt.Sprintf("Work out %d × %d + %d × %d.", a, b, c, d),
		CorrectAnswer: fmt.Sprintf("%d", result),
		WorkedSolution: []string{
			fmt.Sprintf("Complete each multiplication first: %d × %d = %d and %d × %d = %d.", a, b, left, c, d, right),
			fmt.Sprintf("Then add %d + %d = %d.", left, right, result),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicBidmas, difficulty, "multiple_operations"),
	}
}

func generateFractionsQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		denominator := randomInt(rng, 2, 9)
		numerator := randomInt(rng, 1, denominator-1)
		maxMultiplier := 9 / denominator
		if maxMultiplier < 1 {
			maxMultiplier = 1
		}
		multiplier := randomInt(rng, 1, maxMultiplier)
		prompt := fmt.Sprintf("What is %d/%d of %d?", numerator, denominator, denominator*multiplier)
		answer := numerator * multiplier
		return GeneratedQuestion{
			Title:         "Easy Fractions",
			Prompt:        prompt,
			CorrectAnswer: fmt.Sprintf("%d", answer),
			WorkedSolution: []string{
				fmt.Sprintf("Find one part first: %d ÷ %d = %d.", denominator*multiplier, denominator, multiplier),
				fmt.Sprintf("Then take %d parts: %d × %d = %d.", numerator, multiplier, numerator, answer),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicFractions, difficulty, "single_digit", "fraction_of_amount"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		denominator := randomInt(rng, 3, 10)
		a := randomInt(rng, 1, denominator-1)
		b := randomInt(rng, 1, denominator-1)
		resultN, resultD := simplifyFraction(a+b, denominator)
		return GeneratedQuestion{
			Title:         "Medium Fractions",
			Prompt:        fmt.Sprintf("Work out %d/%d + %d/%d. Give your answer in simplest form.", a, denominator, b, denominator),
			CorrectAnswer: formatFraction(resultN, resultD),
			WorkedSolution: []string{
				fmt.Sprintf("The denominators are the same, so add the numerators: %d + %d = %d.", a, b, a+b),
				fmt.Sprintf("This gives %d/%d.", a+b, denominator),
				fmt.Sprintf("Simplify to %s.", formatFraction(resultN, resultD)),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicFractions, difficulty, "addition", "simplify"),
		}
	}

	aN := randomInt(rng, 1, 8)
	aD := randomInt(rng, 2, 9)
	bN := randomInt(rng, 1, 8)
	bD := randomInt(rng, 2, 9)
	resultN, resultD := simplifyFraction(aN*bN, aD*bD)
	return GeneratedQuestion{
		Title:         "Hard Fractions",
		Prompt:        fmt.Sprintf("Work out %d/%d × %d/%d. Give your answer in simplest form.", aN, aD, bN, bD),
		CorrectAnswer: formatFraction(resultN, resultD),
		WorkedSolution: []string{
			fmt.Sprintf("Multiply the numerators: %d × %d = %d.", aN, bN, aN*bN),
			fmt.Sprintf("Multiply the denominators: %d × %d = %d.", aD, bD, aD*bD),
			fmt.Sprintf("This gives %d/%d, which simplifies to %s.", aN*bN, aD*bD, formatFraction(resultN, resultD)),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicFractions, difficulty, "multiplication", "simplify"),
	}
}

func generateAlgebraQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		x := randomInt(rng, 1, 12)
		a := randomInt(rng, 1, 12)
		b := x + a
		return GeneratedQuestion{
			Title:         "Easy Algebra",
			Prompt:        fmt.Sprintf("Solve x + %d = %d.", a, b),
			CorrectAnswer: fmt.Sprintf("x = %d", x),
			WorkedSolution: []string{
				fmt.Sprintf("Subtract %d from both sides.", a),
				fmt.Sprintf("x = %d - %d = %d.", b, a, x),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicAlgebra, difficulty, "one_step"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		x := randomInt(rng, 2, 12)
		a := randomInt(rng, 2, 9)
		b := randomInt(rng, 1, 12)
		c := a*x + b
		return GeneratedQuestion{
			Title:         "Medium Algebra",
			Prompt:        fmt.Sprintf("Solve %dx + %d = %d.", a, b, c),
			CorrectAnswer: fmt.Sprintf("x = %d", x),
			WorkedSolution: []string{
				fmt.Sprintf("Subtract %d from both sides to get %dx = %d.", b, a, c-b),
				fmt.Sprintf("Divide both sides by %d, so x = %d.", a, x),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicAlgebra, difficulty, "two_step"),
		}
	}

	x := randomInt(rng, -6, 12)
	a := randomInt(rng, 2, 6)
	b := randomInt(rng, 1, 8)
	c := randomInt(rng, 2, 6)
	d := a*(x+b) - c
	return GeneratedQuestion{
		Title:         "Hard Algebra",
		Prompt:        fmt.Sprintf("Solve %d(x + %d) - %d = %d.", a, b, c, d),
		CorrectAnswer: fmt.Sprintf("x = %d", x),
		WorkedSolution: []string{
			fmt.Sprintf("Add %d to both sides: %d(x + %d) = %d.", c, a, b, d+c),
			fmt.Sprintf("Divide by %d: x + %d = %d.", a, b, x+b),
			fmt.Sprintf("Subtract %d, so x = %d.", b, x),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicAlgebra, difficulty, "brackets", "multi_step"),
	}
}

func generateGeometryQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		side := randomInt(rng, 2, 9)
		perimeter := side * 4
		return GeneratedQuestion{
			Title:         "Easy Geometry",
			Prompt:        fmt.Sprintf("A square has side length %d cm. What is its perimeter?", side),
			CorrectAnswer: fmt.Sprintf("%d cm", perimeter),
			WorkedSolution: []string{
				fmt.Sprintf("A square has 4 equal sides, so calculate 4 × %d.", side),
				fmt.Sprintf("The perimeter is %d cm.", perimeter),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicGeometry, difficulty, "perimeter", "square"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		length := randomInt(rng, 4, 15)
		width := randomInt(rng, 3, 12)
		area := length * width
		return GeneratedQuestion{
			Title:         "Medium Geometry",
			Prompt:        fmt.Sprintf("A rectangle has length %d cm and width %d cm. What is its area?", length, width),
			CorrectAnswer: fmt.Sprintf("%d cm²", area),
			WorkedSolution: []string{
				fmt.Sprintf("Area of a rectangle = length × width."),
				fmt.Sprintf("%d × %d = %d, so the area is %d cm².", length, width, area, area),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicGeometry, difficulty, "area", "rectangle"),
		}
	}

	a := randomInt(rng, 20, 100)
	b := randomInt(rng, 20, 100)
	missing := 180 - a - b
	return GeneratedQuestion{
		Title:         "Hard Geometry",
		Prompt:        fmt.Sprintf("Two angles in a triangle are %d° and %d°. Find the third angle.", a, b),
		CorrectAnswer: fmt.Sprintf("%d°", missing),
		WorkedSolution: []string{
			fmt.Sprintf("Angles in a triangle add to 180°."),
			fmt.Sprintf("First add the known angles: %d + %d = %d.", a, b, a+b),
			fmt.Sprintf("Then calculate 180 - %d = %d°.", a+b, missing),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicGeometry, difficulty, "angles", "triangle"),
	}
}

func generateDataQuestion(rng *rand.Rand, difficulty sqlc.QuestionDifficulty) GeneratedQuestion {
	if difficulty == sqlc.QuestionDifficultyEasy {
		values := sortedRandomValues(rng, 5, 1, 9)
		median := values[len(values)/2]
		return GeneratedQuestion{
			Title:         "Easy Data",
			Prompt:        fmt.Sprintf("Find the median of these numbers: %s.", joinInts(values)),
			CorrectAnswer: fmt.Sprintf("%d", median),
			WorkedSolution: []string{
				fmt.Sprintf("Put the numbers in order: %s.", joinInts(values)),
				fmt.Sprintf("The middle value is %d, so the median is %d.", median, median),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicData, difficulty, "median"),
		}
	}

	if difficulty == sqlc.QuestionDifficultyMedium {
		values := sortedRandomValues(rng, 5, 2, 20)
		sum := 0
		for _, value := range values {
			sum += value
		}
		mean := sum / len(values)
		adjustment := sum % len(values)
		if adjustment != 0 {
			values[len(values)-1] += len(values) - adjustment
			sort.Ints(values)
			sum = 0
			for _, value := range values {
				sum += value
			}
			mean = sum / len(values)
		}
		return GeneratedQuestion{
			Title:         "Medium Data",
			Prompt:        fmt.Sprintf("Find the mean of these numbers: %s.", joinInts(values)),
			CorrectAnswer: fmt.Sprintf("%d", mean),
			WorkedSolution: []string{
				fmt.Sprintf("Add the numbers: the total is %d.", sum),
				fmt.Sprintf("Divide by %d: %d ÷ %d = %d.", len(values), sum, len(values), mean),
			},
			Tags: buildGeneratedTags(sqlc.QuestionTopicData, difficulty, "mean"),
		}
	}

	values := sortedRandomValues(rng, 6, 5, 30)
	modeIndex := randomInt(rng, 0, len(values)-1)
	modeValue := values[modeIndex]
	values = append(values, modeValue)
	sort.Ints(values)
	return GeneratedQuestion{
		Title:         "Hard Data",
		Prompt:        fmt.Sprintf("Find the mode of these numbers: %s.", joinInts(values)),
		CorrectAnswer: fmt.Sprintf("%d", modeValue),
		WorkedSolution: []string{
			fmt.Sprintf("The mode is the value that appears most often."),
			fmt.Sprintf("%d appears more than any other value, so the mode is %d.", modeValue, modeValue),
		},
		Tags: buildGeneratedTags(sqlc.QuestionTopicData, difficulty, "mode"),
	}
}

func randomInt(rng *rand.Rand, min, max int) int {
	if max <= min {
		return min
	}
	return min + rng.Intn(max-min+1)
}

func digitsToInt(digits []int) int {
	value := 0
	for _, digit := range digits {
		value = value*10 + digit
	}
	return value
}

func placeNameFromPower(power int) string {
	switch power {
	case 0:
		return "ones"
	case 1:
		return "tens"
	case 2:
		return "hundreds"
	case 3:
		return "thousands"
	case 4:
		return "ten-thousands"
	default:
		return "place"
	}
}

func gcd(a, b int) int {
	for b != 0 {
		a, b = b, a%b
	}
	if a < 0 {
		return -a
	}
	return a
}

func simplifyFraction(numerator, denominator int) (int, int) {
	if denominator == 0 {
		return numerator, denominator
	}
	divisor := gcd(numerator, denominator)
	return numerator / divisor, denominator / divisor
}

func formatFraction(numerator, denominator int) string {
	if denominator == 1 {
		return fmt.Sprintf("%d", numerator)
	}
	return fmt.Sprintf("%d/%d", numerator, denominator)
}

func sortedRandomValues(rng *rand.Rand, count, min, max int) []int {
	values := make([]int, count)
	for i := 0; i < count; i++ {
		values[i] = randomInt(rng, min, max)
	}
	sort.Ints(values)
	return values
}

func joinInts(values []int) string {
	parts := make([]string, 0, len(values))
	for _, value := range values {
		parts = append(parts, fmt.Sprintf("%d", value))
	}
	return strings.Join(parts, ", ")
}
