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/environment"
	"github.com/stretchr/testify/suite"
)

type MultitableE2ETest struct {
	suite.Suite

	dbConn *postgres.PostgresDB

	eventStreams []*db.EventStream
	accounts     []*db.Account
	iamRoles     []*db.IAMRole
	services     []*db.Service
}

func (suite *MultitableE2ETest) SetupTest() {
	ctx := context.Background()
	conn, err := newLocalDB()
	suite.NoError(err, "no db connection created in EventTypesE2ETest")
	suite.dbConn = conn

	// Prerequisite event types and streams
	environments := []string{environment.Development, environment.Staging, environment.Production}
	eventType1 := createEventType("CoolEvent", "Fires when cool stuff happens", "SCHEMA", "repopath")
	eventStreams1, err := conn.EventTypeAndStreamsCreate(ctx, eventType1, environments)
	suite.NoError(err)

	eventType2 := createEventType("AmazingUpdate", "Fires when an amazing update occurs", "SCHEMA", "repopath")
	eventStreams2, err := conn.EventTypeAndStreamsCreate(ctx, eventType2, environments)
	suite.NoError(err)

	suite.eventStreams = append(eventStreams1, eventStreams2...)

	// Prerequisite service (needed to create accounts)
	service1 := createService("S1", "cool-team", "cool service", "123")
	service1ID, err := conn.ServiceCreate(ctx, service1)
	service1.ID = service1ID
	suite.NoError(err)

	service2 := createService("S2", "wow-team", "wow service", "456")
	service2ID, err := conn.ServiceCreate(ctx, service2)
	service2.ID = service2ID
	suite.NoError(err)

	service3 := createService("S2", "wow-team", "super cool service", "567")
	service3ID, err := conn.ServiceCreate(ctx, service3)
	service3.ID = service3ID
	suite.NoError(err)

	suite.services = []*db.Service{service1, service2, service3}

	// Prerequisite accounts
	suite.accounts = []*db.Account{}
	account1 := createAccount("123", "acct1", service1ID)
	account2 := createAccount("456", "acct2", service1ID)
	account3 := createAccount("789", "acct3", service2ID)
	account4 := createAccount("111", "acct4", service3ID)
	account5 := createAccount("222", "acct5", service3ID)

	for _, acct := range []*db.Account{account1, account2, account3, account4, account5} {
		id, err := conn.AccountCreate(ctx, acct)
		suite.NoError(err)
		acct.ID = id
		suite.accounts = append(suite.accounts, acct)
	}

	// Prerequisite IAM roles
	suite.iamRoles = []*db.IAMRole{}
	iamRole1 := createIAMRole("111111111111", "iamRole1", service1ID)
	iamRole2 := createIAMRole("222222222222", "iamRole2", service2ID)

	for _, iamRole := range []*db.IAMRole{iamRole1, iamRole2} {
		id, err := conn.IAMRoleCreate(ctx, iamRole)
		suite.NoError(err)
		iamRole.ID = id
		suite.iamRoles = append(suite.iamRoles, iamRole)
	}

	// Prerequisite publications
	pub1 := createPublication(suite.eventStreams[0].ID, &suite.accounts[0].ID, nil)
	pub2 := createPublication(suite.eventStreams[0].ID, nil, &suite.iamRoles[1].ID)
	for _, pub := range []*db.Publication{pub1, pub2} {
		_, err := suite.dbConn.PublicationCreate(ctx, pub)
		suite.NoError(err)
	}
}

func (suite *MultitableE2ETest) TearDownTest() {
	writer := suite.dbConn.WriterConn()

	_, err := writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.PublicationsTableName))
	suite.NoError(err)
	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.EventStreamsTableName))
	suite.NoError(err)
	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.EventTypesTableName))
	suite.NoError(err)
	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.AccountsTableName))
	suite.NoError(err)
	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.IAMRolesTableName))
	suite.NoError(err)
	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.ServicesTableName))
	suite.NoError(err)

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

func (suite *MultitableE2ETest) TestMultitableE2E() {
	t := suite.T()
	ctx := context.Background()

	t.Run("PublisherServicesByEventStreamID", func(t *testing.T) {
		verifyPublishersByEventStreamID := func(eventStreamID int, expectedLen int) {
			services, err := suite.dbConn.PublisherServicesByEventStreamID(ctx, eventStreamID)
			suite.NoError(err)
			suite.Len(services, expectedLen)
		}
		verifyPublishersByEventStreamID(suite.eventStreams[0].ID, 2)
		verifyPublishersByEventStreamID(suite.eventStreams[1].ID, 0)
	})
}

func TestMultitableE2ETest(t *testing.T) {
	suite.Run(t, &MultitableE2ETest{})
}
