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 SubscriptionTargetsE2ETest struct {
	suite.Suite

	dbConn        *postgres.PostgresDB
	testServiceID int
}

func (e *SubscriptionTargetsE2ETest) SetupTest() {
	conn, err := newLocalDB()
	e.NoError(err, "no db connection created in SubscriptionTargetsE2ETest")
	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
}

func (e *SubscriptionTargetsE2ETest) TearDownTest() {
	writer := e.dbConn.WriterConn()

	_, 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.AuditLogsTableName))
	e.NoError(err)

	_, err = writer.Exec(fmt.Sprintf("DELETE FROM %s;", postgres.ServicesTableName))
	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 *SubscriptionTargetsE2ETest) TestSubscriptionTargetsE2E() {
	ctx := context.Background()

	target1 := createSubscriptionTarget("test-target-1", "assume-role-arn-1", "Pending", "", "sqs-arn-1", "sqs-url-1", e.testServiceID)

	id1, err := e.dbConn.SubscriptionTargetCreate(ctx, target1)
	e.Require().NoError(err)
	target1.ID = id1

	target2 := createSubscriptionTarget("test-target-2", "assume-role-arn-2", "Pending Delete", "Error", "sqs-arn-2", "sqs-url-2", e.testServiceID)
	id2, err := e.dbConn.SubscriptionTargetCreate(ctx, target2)
	e.NoError(err)
	target2.ID = id2

	getTarget1, err := e.dbConn.SubscriptionTargetByID(ctx, id1)
	e.NoError(err)
	e.Equalf(target1, getTarget1, "expected subscription target received from db by id to be equal")

	getTarget2, err := e.dbConn.SubscriptionTargetByQueueURL(ctx, target2.SQSDetails.SQSQueueURL)
	e.NoError(err)
	e.Equalf(target2, getTarget2, "expected subscription target received from db by name to be equal")

	updated := createSubscriptionTargetEditable(target2)
	e.Require().NotNil(updated)
	update2ID, err := e.dbConn.SubscriptionTargetUpdate(ctx, id2, updated)
	e.NoError(err)
	e.Equalf(id2, update2ID, "expected id of update subscription to be %d, got %d", target2, update2ID)

	getUpdatedTarget, err := e.dbConn.SubscriptionTargetByID(ctx, id2)
	e.NoError(err)
	e.Equalf(updated.Name, getUpdatedTarget.Name, "expected updated name %s but got %s", updated.Name, getUpdatedTarget.Name)

	targetsByServiceID, err := e.dbConn.SubscriptionTargetsByServiceID(ctx, e.testServiceID)
	e.NoError(err)
	e.Equalf(2, len(targetsByServiceID), "expected %d subscription targets by service id in the db but got %d", 2, len(targetsByServiceID))

	targets, err := e.dbConn.SubscriptionTargets(ctx)
	e.NoError(err)
	e.Equalf(2, len(targets), "expected %d subscription targets in the db but got %d", 2, len(targets))

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

	notFound2, err := e.dbConn.SubscriptionTargetByQueueURL(ctx, "DoesNotExist")
	e.Equal(err, db.ErrResourceNotFound)
	e.Nil(notFound2)

	_, err = e.dbConn.SubscriptionTargetUpdate(ctx, 888, &db.SubscriptionTargetEditable{})
	e.Equal(err, db.ErrResourceNotFound)

	fakeLease := &postgres.PostgresAWSLease{
		ExpiresAt: time.Now().Add(1 * time.Minute),
	}
	_, err = e.dbConn.SubscriptionTargetUpdateInfra(ctx, fakeLease, 888, &db.SubscriptionTargetInfraUpdate{})
	e.Equal(err, db.ErrResourceNotFound)
}

func TestSubscriptionTargetsE2E(T *testing.T) {
	suite.Run(T, &SubscriptionTargetsE2ETest{})
}

func createSubscriptionTarget(name, assumeRoleARN, status, stError, sqsARN, sqsURL string, serviceID int) *db.SubscriptionTarget {
	return &db.SubscriptionTarget{
		Name:          name,
		AssumeRoleARN: assumeRoleARN,
		SQSDetails:    db.SQSDetails{SQSQueueARN: sqsARN, SQSQueueURL: sqsURL},
		Status:        status,
		Error:         stError,
		ServiceID:     serviceID,
	}
}

func createSubscriptionTargetEditable(s *db.SubscriptionTarget) *db.SubscriptionTargetEditable {
	return &db.SubscriptionTargetEditable{
		Name: s.Name + " updated",
	}
}
