package api

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"code.osinet.fr/fgm/kurz/domain"
)

func jsonBody(t *testing.T, res *http.Response) Short {
	body, err := ioutil.ReadAll(res.Body)
	var actual Short
	err = json.Unmarshal(body, &actual)
	if err != nil {
		t.Logf("Response body is not valid JSON: %s", body)
		t.FailNow()
	}
	return actual
}

// subTest performs the main test work: settings up repositories, querying the
// web API, and ensuring response status and format.
func subTest(t *testing.T, seed bool, targetURL string, expectedStatus int, errorMessage string) *http.Response {
	tr := domain.MakeMockTargetRepo(!seed)
	if seed {
		tr.Data[domain.TargetURL{URL: domain.URL(sampleTarget)}] = domain.ShortURL{URL: domain.URL(sampleShort)}
	}
	domain.RegisterRepositories(domain.MockShortRepo{}, tr)

	ts := httptest.NewServer(http.HandlerFunc(handlePostTarget))
	defer ts.Close()

	c := ts.Client()
	c.CheckRedirect = doNotFollowRedirects

	target, err := json.Marshal(Target{Target: targetURL})
	if err != nil {
		panic(err)
	}

	res, err := c.Post(ts.URL, postContentType, bytes.NewReader(target))
	if err != nil {
		t.Log(err)
		t.FailNow()
	}

	if res.StatusCode != expectedStatus {
		t.Log(errorMessage)
		t.FailNow()
	}
	return res
}

// TestHandleGetShortHappyNew ensures that submitting a new valid target succeeds
// with 201 and returns a new short.
func TestHandleGetShortHappyNew(t *testing.T) {
	res := subTest(t, false, sampleTarget,
		http.StatusCreated, "Creation of new short for valid target should succeed")

	// Mock API creates short URLs equal to the target.
	actual := jsonBody(t, res)
	if actual.Short != sampleTarget {
		t.Logf("Response short URL is %s, expected %s", actual.Short, sampleTarget)
		t.FailNow()
	}
}

// TestHandleGetShortHappyOld ensures that submitting an existing target fails
// with 409 and returns an existing short.
func TestHandleGetShortHappyOld(t *testing.T) {
	res := subTest(t, true, sampleTarget,
		http.StatusConflict, "Re-creation of existing short should conflict")

	// It was created that way in the previous Post() call.
	actual := jsonBody(t, res)
	if actual.Short != sampleShort {
		t.Logf("Response short URL is %s, expected %s", actual.Short, sampleShort)
		t.FailNow()
	}
}

// TestHandlePostTargetSadEmpty ensures that submitting an invalid target fails with 400.
func TestHandlePostTargetSadEmpty(t *testing.T) {
	subTest(t, true, "",
		http.StatusBadRequest, "Creation of short for empty target should be a bad request")
}

// TestHandlePostTargetSadUncreated ensures that submitting a new valid target
// fails with 50x when the repository cannot create new short URLs.
func TestHandlePostTargetSadUncreated(t *testing.T) {
	subTest(t, true, sampleTarget+"bis",
		http.StatusInternalServerError, "Creation of new short for valid target should fail since repository cannot create")
}