package api

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"testing"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/mux"
)

const (
	signKey = "secret"
)

func TestInit(t *testing.T) {
	tapi := newTestAPI(t)
	defer tapi.close()

	resp := tapi.do("GET", "/", nil, nil)
	if resp.StatusCode != http.StatusNotFound {
		t.Error("Expected not found", resp.Status, resp.Body)
	}
}

type testAPI struct {
	t          *testing.T
	baseURL    string
	client     *http.Client
	server     *httptest.Server
	testPath   string
	token      string
	tokenAdmin string
}

func newTestAPI(t *testing.T) *testAPI {
	testPath, err := ioutil.TempDir(os.TempDir(), "cicer-test-")
	if err != nil {
		t.Fatal("Tempdir  error:", err)
	}
	dbPath := path.Join(testPath, "test.db")

	r := mux.NewRouter()
	err = Init(dbPath, signKey, r)
	if err != nil {
		t.Fatal("Init error:", err)
	}
	server := httptest.NewServer(r)

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"num":  testMember.Num,
		"role": "",
		"exp":  time.Now().Add(time.Hour * 24).Unix(),
	})
	tokenString, err := token.SignedString([]byte(signKey))
	if err != nil {
		t.Fatal("Can't generate token:", err)
	}
	tokenAdmin := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"num":  testMemberAdmin.Num,
		"role": "admin",
		"exp":  time.Now().Add(time.Hour * 24).Unix(),
	})
	tokenAdminString, err := tokenAdmin.SignedString([]byte(signKey))
	if err != nil {
		t.Fatal("Can't generate token:", err)
	}

	return &testAPI{t, server.URL, &http.Client{}, server, testPath, tokenString, tokenAdminString}
}

func (ta *testAPI) do(method string, url string, body interface{}, respBody interface{}) *http.Response {
	return ta.doToken(ta.token, method, url, body, respBody)
}

func (ta *testAPI) doAdmin(method string, url string, body interface{}, respBody interface{}) *http.Response {
	return ta.doToken(ta.tokenAdmin, method, url, body, respBody)
}

func (ta *testAPI) doToken(token string, method string, url string, body interface{}, respBody interface{}) *http.Response {
	var reader io.Reader
	if body != nil {
		var w io.WriteCloser
		reader, w = io.Pipe()
		go func() {
			json.NewEncoder(w).Encode(body)
			w.Close()
		}()
	}
	req, err := http.NewRequest(method, ta.baseURL+url, reader)
	if err != nil {
		ta.t.Fatal("Can't build request", method, url, err)
	}
	req.Header.Add("x-authentication", token)
	resp, err := ta.client.Do(req)
	if err != nil {
		ta.t.Fatal("HTTP query failed", method, url, err)
	}
	if respBody != nil {
		err = json.NewDecoder(resp.Body).Decode(respBody)
		if err != nil {
			ta.t.Fatal("Can't decode resp", method, url, err)
		}
	}
	return resp
}

func (ta *testAPI) close() {
	ta.server.Close()
	os.RemoveAll(ta.testPath)
}