package integration

import (
	"fmt"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/contiamo/dev/tests/integration/suite"
	uuid "github.com/satori/go.uuid"
)

type Payload suite.Payload

func TestExternalDataSourceCreation(t *testing.T) {
	s := suite.New(t)

	var (
		dsName = "ds" + strings.Replace(uuid.NewV4().String(), "-", "_", -1)
		dsID   string
	)

	s.Run("create new external datasource", func(s *suite.Suite) {
		path := fmt.Sprintf("/hub/api/v1/%s/datasources", s.ProjectID)
		payload := Payload{
			"name":       dsName,
			"type":       "external",
			"technology": "postgresql",
			"connectionInfo": Payload{
				"host":     "metadb",
				"port":     "5433",
				"database": "hub",
				"user":     "user",
				"password": "localdev",
			},
		}
		resp := s.Post(path, payload)
		defer resp.Body.Close()
		s.Require().Equal(http.StatusCreated, resp.StatusCode)
		createResponse := s.ParsePayload(resp.Body)
		id, ok := createResponse["id"].(string)
		s.Require().True(ok)
		dsID = id
	})

	s.Run("check hub effects", func(s *suite.Suite) {
		time.Sleep(11 * time.Second)
		s.Run("check if we can get it by id", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/datasources/%s", s.ProjectID, dsID)
			resp := s.Get(path)
			defer resp.Body.Close()
			s.Require().Equal(http.StatusOK, resp.StatusCode)
			data := s.ParsePayload(resp.Body)
			hits := s.Jq(data, `.data.name`)
			s.Require().Len(hits, 1)
			s.Require().Equal(dsName, hits[0])

			s.Run("check that a sync task was scheduled by the time we GET it now", func(s *suite.Suite) {
				taskIdHits := s.Jq(data, `.data.syncTask.taskId`)
				s.Require().NotEmpty(taskIdHits[0])
			})
		})

		s.Run("check if we can get it by name", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/datasources/%s", s.ProjectID, dsName)
			resp := s.Get(path)
			defer resp.Body.Close()
			s.Require().Equal(http.StatusOK, resp.StatusCode)
			data := s.ParsePayload(resp.Body)
			hits := s.Jq(data, `.data.id`)
			s.Require().Len(hits, 1)
			s.Require().Equal(dsID, hits[0])
		})

		s.Run("check if we find it in the datasource list", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/datasources", s.ProjectID)
			resp := s.Get(path)
			defer resp.Body.Close()
			s.Require().Equal(http.StatusOK, resp.StatusCode)
			data := s.ParsePayload(resp.Body)
			hits := s.Jq(data, `.data[] | select(.name == "%s")`, dsName)
			s.Require().Len(hits, 1)
		})

	})

	s.Run("side effects", func(s *suite.Suite) {
		s.Run("check if we can query the datasource", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/query", s.ProjectID)
			resp := s.Post(path, Payload{
				"sql": fmt.Sprintf(`SELECT name FROM "%s"."datasources"`, dsName),
			})
			defer resp.Body.Close()
			data := s.ParsePayload(resp.Body)
			hits := s.Jq(data, `.rows[] | select(.[0] == "%s")`, dsName)
			s.Require().Len(hits, 1)
		})

		s.Run("check if we can profile the query", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/profile/sql", s.ProjectID)
			resp := s.Post(path, Payload{
				"projectId": s.ProjectID,
				"sql":       fmt.Sprintf(`SELECT name FROM "%s"."datasources"`, dsName),
			})
			defer resp.Body.Close()
			data := s.ParsePayload(resp.Body)
			columns, ok := data["columns"].(map[string]interface{})
			s.Require().True(ok, "unknown response %+v", data)
			s.Require().Len(columns, 1, "unknown response %+v", data)

			nameProfile, ok := columns["name"].(map[string]interface{})
			s.Require().True(ok, "unknown response %+v", columns)
			s.Require().Equal(nameProfile["dataType"], "string", "unknown response %+v", nameProfile)
		})
	})

	s.Run("reject to delete the table", func(s *suite.Suite) {
		path := fmt.Sprintf("/hub/api/v1/%s/datasources/%s/tables/datasources", s.ProjectID, dsID)
		resp := s.Delete(path)
		defer resp.Body.Close()
		s.Require().Equal(http.StatusUnprocessableEntity, resp.StatusCode)

		s.Run("check if we can still query the datasource", func(s *suite.Suite) {
			path := fmt.Sprintf("/hub/api/v1/%s/query", s.ProjectID)
			resp := s.Post(path, Payload{
				"sql": fmt.Sprintf(`SELECT name FROM "%s"."datasources"`, dsName),
			})
			defer resp.Body.Close()
			data := s.ParsePayload(resp.Body)
			hits := s.Jq(data, `.rows[] | select(.[0] == "%s")`, dsName)
			s.Require().Len(hits, 1)
		})
	})

}
