package main

import (
	"fmt"
)

// section: interface
type Identifiable[T comparable] interface {
	ID() T
}

type Repository[T comparable, U Identifiable[T]] interface {
	Save(item U) error
	FindByID(id T) (U, bool)
	Delete(id T) error
}

// section: interface

// section: user
type User struct {
	id   int
	name string
}

func (u User) ID() int {
	return u.id
}

func (u User) String() string {
	return fmt.Sprintf("User{id: %d, name: %s}", u.id, u.name)
}

// section: user

// section: implementation

type InMemoryRepository[T comparable, U Identifiable[T]] struct {
	items map[T]U
}

func NewInMemoryRepository[T comparable, U Identifiable[T]]() *InMemoryRepository[T, U] {
	return &InMemoryRepository[T, U]{
		items: make(map[T]U),
	}
}

func (r *InMemoryRepository[T, U]) Save(item U) error {
	r.items[item.ID()] = item
	return nil
}

func (r *InMemoryRepository[T, U]) FindByID(id T) (U, bool) {
	item, exists := r.items[id]
	return item, exists
}

func (r *InMemoryRepository[T, U]) Delete(id T) error {
	delete(r.items, id)
	return nil
}

// section: implementation

func main() {
	// Create a repository for Users with int IDs
	repo := NewInMemoryRepository[int, User]()

	// Save some users
	users := []User{
		{id: 1, name: "Alice"},
		{id: 2, name: "Bob"},
		{id: 3, name: "Charlie"},
	}

	for _, user := range users {
		repo.Save(user)
	}

	// Find users
	if user, found := repo.FindByID(2); found {
		fmt.Println("Found user:", user)
	}

	// Delete a user
	repo.Delete(1)

	// Try to find deleted user
	if _, found := repo.FindByID(1); !found {
		fmt.Println("User with ID 1 not found (deleted)")
	}
}
