package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	// section: main
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		log.Fatal(err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Load some sample data
	if err := load(ctx, db); err != nil {
		log.Fatal(err)
	}

	// Get all users
	users, err := GetUsers(ctx, db)
	if err != nil {
		log.Fatal(err)
	}
	for _, u := range users {
		fmt.Printf("%+v\n", u)
	}
	// section: main
}

// section: load
func load(ctx context.Context, db *sql.DB) error {
	_, err := db.ExecContext(ctx,
		`
		create table users(id int, name text);
		insert into users(id, name) values (1, 'Wilma'), (2, 'Fred'), (3, 'Betty'), (4, 'Barney');
		`,
	)
	return err
}

// section: load

// section: users
type User struct {
	ID   int
	Name string
}

func GetUsers(ctx context.Context, db *sql.DB) ([]User, error) {
	// Put a timeout on our query
	ctx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()

	// create an error channel to return any errors in our goroutine
	ec := make(chan error, 1)

	// create a channel to retrieve our result
	result := make(chan []User, 1)

	// launch the code in a go routine
	go func() {
		// pass in our context
		rows, err := db.QueryContext(ctx, "select * from users;")
		if err != nil {
			ec <- err
			return
		}

		users := []User{}
		for rows.Next() {
			var u User
			if err := rows.Scan(&u.ID, &u.Name); err != nil {
				ec <- err
				return
			}
			users = append(users, u)
		}
		result <- users
	}()

	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case err := <-ec:
		return nil, err
	case u := <-result:
		return u, nil
	}
}

// section: users
