package e2e

import (
	"context"
	"fmt"
	"testing"

	"code.justin.tv/eventbus/controlplane/internal/db"
	"code.justin.tv/eventbus/controlplane/internal/db/postgres"
	"code.justin.tv/eventbus/controlplane/internal/twirperr"
	"github.com/stretchr/testify/suite"
)

type EventTypesE2ETest struct {
	suite.Suite

	dbConn *postgres.PostgresDB
}

func (e *EventTypesE2ETest) SetupTest() {
	conn, err := newLocalDB()
	e.NoError(err, "no db connection created in EventTypesE2ETest")
	e.dbConn = conn
}

func (e *EventTypesE2ETest) TearDownTest() {
	_, err := e.dbConn.WriterConn().Exec(fmt.Sprintf("DELETE FROM %s;", postgres.EventTypesTableName))
	e.NoError(err)

	err = e.dbConn.Close()
	e.NoError(err)
}

// TODO: https://jira.twitch.com/browse/ASYNC-338 investigate grouping tests for readability
func (e *EventTypesE2ETest) TestEventTypesE2E() {
	ctx := context.Background()

	event1 := createEventType("SkipToWork", "skippity!", "n/a", "path/to/event/skiptowork.proto")
	id1, err := e.dbConn.EventTypeCreate(ctx, event1)
	e.Require().NoError(err)
	event1.ID = id1

	event2 := createEventType("SayHiToNate", "politeness is key", "n/a", "path/to/event/sayhitonate.proto")
	id2, err := e.dbConn.EventTypeCreate(ctx, event2)
	e.Require().NoError(err)
	event2.ID = id2

	getEventID1, err := e.dbConn.EventTypeByID(ctx, id1)
	e.Require().NoError(err)
	e.Equalf(event1, getEventID1, "expected event type received from db by id to be equal")

	getEventName2, err := e.dbConn.EventTypeByName(ctx, event2.Name)
	e.Require().NoError(err)
	e.Equalf(event2, getEventName2, "expected event type received from db by name to be equal")

	getEventNotExists, err := e.dbConn.EventTypeByName(ctx, "DoesNotExist")
	e.Error(err)
	e.Nil(getEventNotExists, "expected event to not exist and be returned as nil")

	updated := createEventTypeEditable(event1)
	resID, err := e.dbConn.EventTypeUpdate(ctx, id1, updated)
	e.Require().NoError(err)
	e.Equalf(resID, id1, "expected updated event id %d was not equal to original id %d", resID, id1)

	getUpdatedEvent, err := e.dbConn.EventTypeByID(ctx, resID)
	e.Require().NoError(err)
	e.Equalf(updated.Name, getUpdatedEvent.Name, "expected updated name %s but got %s", updated.Name, getUpdatedEvent.Name)
	e.Equalf(updated.Description, getUpdatedEvent.Description, "expected updated description %s but got %s", updated.Description, getUpdatedEvent.Description)
	e.Equalf(updated.Schema, getUpdatedEvent.Schema, "expected updated schema %s but got %s", updated.Schema, getUpdatedEvent.Schema)
	e.Equalf(updated.RepoFilePath, getUpdatedEvent.RepoFilePath, "expected updated repofilepath %s but got %s", updated.RepoFilePath, getUpdatedEvent.RepoFilePath)
	e.Equal(updated.Deprecated, getUpdatedEvent.Deprecated, "expected deprecated")
	e.Equal(updated.LDAPGroup, getUpdatedEvent.LDAPGroup, "expected updated LDAP group")

	eventTypes, err := e.dbConn.EventTypes(ctx)
	e.Require().NoError(err)
	e.Equalf(2, len(eventTypes), "expected %d event types in the db but got %d", 2, len(eventTypes))

	notFound1, err := e.dbConn.EventTypeByID(ctx, 999)
	e.Equal(err, db.ErrResourceNotFound)
	e.Nil(notFound1)

	notFound2, err := e.dbConn.EventTypeByName(ctx, "DoesNotExist")
	e.Error(err)
	e.True(err.(twirperr.DBError).EventTypeNotFound())
	e.Nil(notFound2)

	deprecatedEvents, err := e.dbConn.EventTypesDeprecated(ctx)
	e.Require().NoError(err)
	e.Require().Equal(len(deprecatedEvents), 1)
	e.Equal(deprecatedEvents[0].Deprecated, true, "expected deprecated")

	_, err = e.dbConn.EventTypeUpdate(ctx, 666, &db.EventTypeEditable{})
	e.Equal(err, db.ErrResourceNotFound)
}

func createEventType(name, desc, schema, repo string) *db.EventType {
	return &db.EventType{
		Name:         name,
		Description:  desc,
		Schema:       schema,
		RepoFilePath: repo,
		Deprecated:   false,
		LDAPGroup:    "team-coolpeople",
	}
}

func createEventTypeEditable(e *db.EventType) *db.EventTypeEditable {
	return &db.EventTypeEditable{
		Name:         e.Name + "Updated",
		Description:  e.Description + " updated",
		Schema:       e.Schema + " updated",
		RepoFilePath: e.RepoFilePath + "/updated",
		Deprecated:   true,
		LDAPGroup:    e.LDAPGroup + "updated",
	}
}

func TestEventTypesE2E(T *testing.T) {
	suite.Run(T, &EventTypesE2ETest{})
}
