package pkgserver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"testing"
)

// newIndex returns the address of a newly populated packageIndex.
func newIndex(t *testing.T) *packageIndex {
	t.Helper()

	var index packageIndex
	if err := index.populate(nil); err != nil {
		t.Fatalf("populate: error = %v", err)
	}
	return &index
}

// checkStatus checks response status code.
func checkStatus(t *testing.T, resp *http.Response, want int) {
	t.Helper()

	if resp.StatusCode != want {
		t.Errorf(
			"StatusCode: %s, want %s",
			http.StatusText(resp.StatusCode),
			http.StatusText(want),
		)
	}
}

// checkHeader checks the value of a header entry.
func checkHeader(t *testing.T, h http.Header, key, want string) {
	t.Helper()

	if got := h.Get(key); got != want {
		t.Errorf("%s: %q, want %q", key, got, want)
	}
}

// checkAPIHeader checks common entries set for API endpoints.
func checkAPIHeader(t *testing.T, h http.Header) {
	t.Helper()

	checkHeader(t, h, "Content-Type", "application/json; charset=utf-8")
	checkHeader(t, h, "Cache-Control", "no-cache, no-store, must-revalidate")
	checkHeader(t, h, "Pragma", "no-cache")
	checkHeader(t, h, "Expires", "0")
}

// checkPayloadFunc checks the JSON response of an API endpoint by passing it to f.
func checkPayloadFunc[T any](
	t *testing.T,
	resp *http.Response,
	f func(got *T) bool,
) {
	t.Helper()

	var got T
	r := io.Reader(resp.Body)
	if testing.Verbose() {
		var buf bytes.Buffer
		r = io.TeeReader(r, &buf)
		defer func() { t.Helper(); t.Log(buf.String()) }()
	}
	if err := json.NewDecoder(r).Decode(&got); err != nil {
		t.Fatalf("Decode: error = %v", err)
	}

	if !f(&got) {
		t.Errorf("Body: %#v", got)
	}
}

// checkPayload checks the JSON response of an API endpoint.
func checkPayload[T any](t *testing.T, resp *http.Response, want T) {
	t.Helper()

	checkPayloadFunc(t, resp, func(got *T) bool {
		return reflect.DeepEqual(got, &want)
	})
}

func checkError(t *testing.T, resp *http.Response, error string, code int) {
	t.Helper()

	checkStatus(t, resp, code)
	if got, _ := io.ReadAll(resp.Body); string(got) != fmt.Sprintln(error) {
		t.Errorf("Body: %q, want %q", string(got), error)
	}
}
