package api

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

type BookResult struct {
	Book Book
	Err  error
}

// Books will return a list of books based on the provided ISBN numbers
func Books(ctx context.Context, isbns []string) <-chan BookResult {
	br := make(chan BookResult, len(isbns))

	for _, isbn := range isbns {
		// fan out go routines so they all request concurrently
		go func(isbn string) {
			// set a timeout for our book retrievals
			ctx, cancel := context.WithTimeout(ctx, time.Second*10)
			defer cancel()

			work := make(chan BookResult, 1)

			// don't block while retrieving the book
			go func() {
				book, err := getBook(isbn)
				work <- BookResult{Book: book, Err: err}
			}()

			select {
			case result := <-work:
				br <- result
			case <-ctx.Done():
				br <- BookResult{Err: fmt.Errorf("timed out retrieving %q: %w", isbn, ctx.Err())}
			}
		}(isbn)
	}
	return br
}

// getBook retrieves the book requested via the isbn
func getBook(isbn string) (Book, error) {
	u, err := url.Parse("https://openlibrary.org/isbn/" + isbn + ".json")
	if err != nil {
		return Book{}, err
	}
	resp, err := http.Get(u.String())
	if err != nil {
		return Book{}, err
	}
	defer resp.Body.Close()
	var book Book
	if err := json.NewDecoder(resp.Body).Decode(&book); err != nil {
		return Book{}, err
	}
	return book, nil
}
