// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.

package gremlin

import (
	"context"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/facebook/ent/dialect/gremlin/encoding/graphson"

	jsoniter "github.com/json-iterator/go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestHTTPTransportRoundTripper(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, r.Header.Get("Content-Type"), "application/json")
		got, err := ioutil.ReadAll(r.Body)
		require.NoError(t, err)
		assert.JSONEq(t, `{"gremlin": "g.V(1)", "language": "gremlin-groovy"}`, string(got))

		_, err = io.WriteString(w, `{
			"requestId": "f679127f-8701-425c-af55-049a44720db6",
			"result": {
				"data": {
					"@type": "g:List",
					"@value": [
						{
							"@type": "g:Vertex",
							"@value": {
								"id": {
									"@type": "g:Int64",
									"@value": 1
								},
								"label": "person"
							}
						}
					]
				},
				"meta": {
					"@type": "g:Map",
					"@value": []
				}
			},
			"status": {
				"attributes": {
					"@type": "g:Map",
					"@value": []
				},
				"code": 200,
				"message": ""
			}
		}`)
		require.NoError(t, err)
	}))
	defer srv.Close()

	transport, err := NewHTTPTransport(srv.URL, nil)
	require.NoError(t, err)

	rsp, err := transport.RoundTrip(context.Background(), NewEvalRequest("g.V(1)"))
	require.NoError(t, err)

	assert.Equal(t, "f679127f-8701-425c-af55-049a44720db6", rsp.RequestID)
	assert.Equal(t, 200, rsp.Status.Code)
	assert.Empty(t, rsp.Status.Message)

	v := jsoniter.Get(rsp.Result.Data, graphson.ValueKey, 0, graphson.ValueKey)
	require.NoError(t, v.LastError())
	assert.Equal(t, 1, v.Get("id", graphson.ValueKey).ToInt())
	assert.Equal(t, "person", v.Get("label").ToString())
}

func TestNewHTTPTransportBadURL(t *testing.T) {
	transport, err := NewHTTPTransport(":", nil)
	assert.Nil(t, transport)
	assert.Error(t, err)
}

func TestHTTPTransportBadRequest(t *testing.T) {
	transport, err := NewHTTPTransport("example.com", nil)
	require.NoError(t, err)

	req := NewEvalRequest("g.V()")
	req.Operation = ""
	rsp, err := transport.RoundTrip(context.Background(), req)
	assert.EqualError(t, err, `gremlin/http: unsupported operation: ""`)
	assert.Nil(t, rsp)

	req = NewEvalRequest("g.V()")
	delete(req.Arguments, ArgsGremlin)
	rsp, err = transport.RoundTrip(context.Background(), req)
	assert.EqualError(t, err, "gremlin/http: missing query expression")
	assert.Nil(t, rsp)
}

func TestHTTPTransportBadResponseStatus(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
	}))
	defer srv.Close()

	transport, err := NewHTTPTransport(srv.URL, nil)
	require.NoError(t, err)

	_, err = transport.RoundTrip(context.Background(), NewEvalRequest("g.E()."))
	require.Error(t, err)
	assert.Contains(t, err.Error(), http.StatusText(http.StatusInternalServerError))
}

func TestHTTPTransportBadResponseBody(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, err := io.WriteString(w, "{{{")
		require.NoError(t, err)
	}))
	defer srv.Close()

	transport, err := NewHTTPTransport(srv.URL, nil)
	require.NoError(t, err)

	_, err = transport.RoundTrip(context.Background(), NewEvalRequest("g.E()."))
	require.Error(t, err)
	assert.Contains(t, err.Error(), "decoding response")
}
