package e2e

import (
	"context"
	"fmt"
	"testing"
	"time"

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

type SubscriptionsE2ETest struct {
	suite.Suite

	dbConn             *postgres.PostgresDB
	testServiceID      int
	testAccountID      int
	testTargetID1      int
	testTargetID2      int
	testEventTypeID    int
	testEventStreamID1 int
	testEventStreamID2 int
}

// TODO: https://jira.twitch.com/browse/ASYNC-338 investigate grouping tests for readability
func (e *SubscriptionsE2ETest) SetupTest() {
	conn, err := newLocalDB()
	e.NoError(err)
	e.dbConn = conn

	testServiceID, err := e.dbConn.ServiceCreate(context.Background(), createService("123", "TestService", "test", "this is for a test"))
	e.NoError(err)
	e.testServiceID = testServiceID

	testAccountID, err := e.dbConn.AccountCreate(context.Background(), createAccount("343434343434", "test-account", e.testServiceID))
	e.NoError(err)
	e.testAccountID = testAccountID

	testTargetID1, err := e.dbConn.SubscriptionTargetCreate(context.Background(), createSubscriptionTarget("test-target-1", "test-assume-role-arn-1", "Pending", "", "sqs-arn-1", "sqs-url-1", e.testServiceID))
	e.NoError(err)
	e.testTargetID1 = testTargetID1

	testTargetID2, err := e.dbConn.SubscriptionTargetCreate(context.Background(), createSubscriptionTarget("test-target-2", "test-assume-role-arn-2", "Pending", "", "sqs-arn-2", "sqs-url-2", e.testServiceID))
	e.NoError(err)
	e.testTargetID2 = testTargetID2

	testEventTypeID, err := e.dbConn.EventTypeCreate(context.Background(), createEventType("TestEvent", "testing event type", "n/a", "path/to/event.proto"))
	e.NoError(err)
	e.testEventTypeID = testEventTypeID

	testEventStreamID1, err := e.dbConn.EventStreamCreate(context.Background(), createEventStream("staging", e.testEventTypeID))
	e.NoError(err)
	e.testEventStreamID1 = testEventStreamID1

	testEventStreamID2, err := e.dbConn.EventStreamCreate(context.Background(), createEventStream("production", e.testEventTypeID))
	e.NoError(err)
	e.testEventStreamID2 = testEventStreamID2
}

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

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.EventStreamsTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.EventTypesTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.SubscriptionTargetsTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.AccountsTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.IAMRolesTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.ServicesTableName))
	e.NoError(err)

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

func (e *SubscriptionsE2ETest) TestSubscriptionsE2E() {
	ctx := context.Background()

	sub1 := createSubscription("Pending", "", e.testTargetID1, e.testEventStreamID1)
	id1, err := e.dbConn.SubscriptionCreate(ctx, sub1)
	e.NoError(err)
	sub1.ID = id1

	sub2 := createSubscription("", "", e.testTargetID2, e.testEventStreamID2)
	id2, err := e.dbConn.SubscriptionCreate(ctx, sub2)
	e.NoError(err)
	sub2.ID = id2

	getSub1, err := e.dbConn.SubscriptionByID(ctx, id1)
	e.NoError(err)
	e.Equalf(sub1, getSub1, "expected subscription received from db by id to be equal")

	getSub2Arr, err := e.dbConn.SubscriptionsBySubscriptionTargetID(ctx, e.testTargetID2)
	e.NoError(err)
	e.Equalf(1, len(getSub2Arr), "expected %d subscriptions by target id, got %d", 1, len(getSub2Arr))
	e.Equalf(sub2, getSub2Arr[0], "expected subscription received from db by target id to be equal")

	getSub1Arr, err := e.dbConn.SubscriptionsByEventStreamID(ctx, e.testEventStreamID1)
	e.NoError(err)
	e.Equalf(1, len(getSub1Arr), "expected %d subscriptions by event stream id, got %d", 1, len(getSub1Arr))
	e.Equalf(sub1, getSub1Arr[0], "expected subscription received from db by event stream id to be equal")

	subs, err := e.dbConn.Subscriptions(ctx)
	e.NoError(err)
	e.Equalf(2, len(subs), "expected total number of subscriptions in db to be %d, got %d", 2, len(subs))

	errDelete := e.dbConn.SubscriptionDelete(ctx, &postgres.PostgresAWSLease{ExpiresAt: time.Now().Add(10 * time.Second)}, id2)
	e.NoError(errDelete)
	_, err = e.dbConn.SubscriptionByID(ctx, id2)
	e.Errorf(err, "expected an error after getting a deleted sub")

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

	notFound2, err := e.dbConn.SubscriptionByEventStreamIDAndSubscriptionTargetID(ctx, 888, 777)
	e.Equal(err, db.ErrResourceNotFound)
	e.Nil(notFound2)
}

func TestSubscriptionsE2E(T *testing.T) {
	suite.Run(T, &SubscriptionsE2ETest{})
}

func createSubscription(status, sError string, targetID, eventStreamID int) *db.Subscription {
	return &db.Subscription{
		Status:               status,
		Error:                sError,
		SubscriptionTargetID: targetID,
		EventStreamID:        eventStreamID,
	}
}
