package main

import "fmt"

// section: constraint
// Printable is a constraint that combines both type and method requirements
type Printable interface {
	~int | ~string | ~float64 // Type constraint: must be based on one of these types
	String() string            // Method constraint: must implement String() method
}

// section: constraint

// section: types
// Score is based on int and implements String()
type Score int

func (s Score) String() string {
	return fmt.Sprintf("Score: %d", s)
}

// Temperature is based on float64 and implements String()
type Temperature float64

func (t Temperature) String() string {
	return fmt.Sprintf("%.1f°C", t)
}

// Status is based on string and implements String()
type Status string

func (s Status) String() string {
	return fmt.Sprintf("Status: %s", string(s))
}

// section: types

// section: function
// Display is a generic function that accepts any Printable type
func Display[T Printable](value T) {
	// We can call String() because the Printable constraint requires it
	fmt.Println(value.String())
}

// Compare compares two Printable values
// Note: This works because all types in Printable constraint support comparison
func Compare[T Printable](a, b T) string {
	// We can use comparison operators because underlying types are comparable
	if a == b {
		return fmt.Sprintf("%s equals %s", a.String(), b.String())
	}
	return fmt.Sprintf("%s does not equal %s", a.String(), b.String())
}

// section: function

// section: usage
func main() {
	// Create instances of our custom types
	score1 := Score(95)
	score2 := Score(87)
	temp := Temperature(23.5)
	status := Status("active")

	// Display using the generic Display function
	fmt.Println("=== Display Examples ===")
	Display(score1)
	Display(temp)
	Display(status)

	// Compare values
	fmt.Println("\n=== Comparison Examples ===")
	fmt.Println(Compare(score1, score2))
	fmt.Println(Compare(score1, Score(95)))

	// We can also use the values in generic collections
	fmt.Println("\n=== Collection Example ===")
	scores := []Score{Score(85), Score(92), Score(78)}
	fmt.Println("All scores:")
	for _, s := range scores {
		Display(s)
	}
}

// section: usage
