package suite

import (
	"context"

	"code.justin.tv/eventbus/controlplane/internal/db"

	"code.justin.tv/eventbus/controlplane/e2e/internal/e2eutil"
	"code.justin.tv/eventbus/controlplane/e2e/internal/expected"
	"code.justin.tv/eventbus/controlplane/e2e/internal/report"
	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/pkg/errors"
)

var _ Runner = &AuthorizedFieldValidationTestSuite{}

type AuthorizedFieldValidationTestSuite struct {
	*DefaultTestSuite // Use the default suite as ground work
}

func NewAuthorizedFieldValidationTestSuite(suiteName string) (Runner, error) {
	defaultTest, err := NewDefaultTestSuite(suiteName)
	if err != nil {
		return nil, err
	}
	return &AuthorizedFieldValidationTestSuite{
		DefaultTestSuite: defaultTest,
	}, nil
}

func (s *AuthorizedFieldValidationTestSuite) Test(ctx context.Context) {
	ctx = e2eutil.AppendTestPath(ctx, s.TestName())
	s.DefaultTestSuite.Test(ctx)

	var err error

	s.Log(ctx, "Checking authorized fields with no configuration errors")
	err = s.checkAuthorizedFieldsOk(ctx)
	if err != nil {
		s.Error(ctx, "validation failed expectations", err)
		return
	}

	kmsGrants, err := e2eutil.KMSGrants(expected.AuthorizedFieldKeyARN)
	if err != nil {
		s.Error(ctx, "could not get KMS grants", err)
		return
	}

	err = s.checkAuthorizedFieldsPublisherGrantMissing(ctx, kmsGrants)
	if err != nil {
		s.Error(ctx, "validation failed for missing authorized field publisher grant", err)
		return
	}

	err = s.checkAuthorizedFieldsSubscriberGrantMissing(ctx, kmsGrants)
	if err != nil {
		s.Error(ctx, "validation failed for missing authorized field subscriber grant", err)
		return
	}

}

func (s *AuthorizedFieldValidationTestSuite) checkAuthorizedFieldsOk(ctx context.Context) error {
	reports, err := e2eutil.Validate(ctx)
	if err != nil {
		return errors.Wrap(err, "validation routine failed")
	}

	validationFailures := validation.NotOk(reports)
	if validationFailures != nil || len(validationFailures) != 0 {
		return errors.Wrap(err, "expected no errors for authorized field validation")
	}
	return nil
}

func (s *AuthorizedFieldValidationTestSuite) checkAuthorizedFieldsPublisherGrantMissing(ctx context.Context, existingGrants []*kms.GrantListEntry) error {
	// Create non-existent grants that will fail validation
	checkWithExpectedStatus := func(env string, expectedStatus validation.Status) error {
		fakePublisherGrant := &validation.AuthorizedFieldPublisherGrant{
			AuthorizedFieldPublisherGrant: &db.AuthorizedFieldPublisherGrant{
				ID:            123,
				IAMRoleID:     234,
				EventStreamID: 345,
				KMSGrantID:    expected.PublisherGrantName(234, 345),
			},
			EventType:   "NonExistentEvent",
			Environment: env,
			IAMRoleARN:  "arn:aws:iam::123456789012:role/eventbus-cool-role",
		}
		fakePublisherGrant.WithExistingGrants(existingGrants)

		reports, err := e2eutil.ValidateWithItems(ctx, []validation.Item{fakePublisherGrant})
		validationFailures := validation.NotOk(reports)
		if err != nil {
			return errors.Wrap(err, "unexpected validation error")
		} else if validationFailures == nil {
			return errors.New("expected validation error for missing authorized field publisher grant")
		} else if validationFailures[0].Status != expectedStatus {
			return errors.New("expected " + string(expectedStatus) + " for missing authorized field publisher grant in " + env + " , got " + string(validationFailures[0].Status))
		}
		return nil
	}

	var err error
	err = checkWithExpectedStatus("production", validation.StatusError)
	if err != nil {
		return err
	}

	err = checkWithExpectedStatus("staging", validation.StatusWarn)
	if err != nil {
		return err
	}

	err = checkWithExpectedStatus("development", validation.StatusWarn)
	if err != nil {
		return err
	}
	return nil
}

func (s *AuthorizedFieldValidationTestSuite) checkAuthorizedFieldsSubscriberGrantMissing(ctx context.Context, existingGrants []*kms.GrantListEntry) error {
	// Create a fake DB validation item for authorized field subscriber grant that doesn't exist in KMS
	checkWithExpectedStatus := func(env string, expectedStatus validation.Status) error {
		fakeSubscriberGrant := &validation.AuthorizedFieldSubscriberGrant{
			AuthorizedFieldSubscriberGrant: &db.AuthorizedFieldSubscriberGrant{
				ID:                345,
				IAMRoleID:         456,
				AuthorizedFieldID: 567,
				KMSGrantID:        expected.SubscriberGrantName(456, 567),
			},
			EventType:   "DoesNotExist",
			Environment: env,
			MessageName: "E2ETestMessage",
			FieldName:   "E2ETestField",
		}
		fakeSubscriberGrant.WithExistingGrants(existingGrants)

		reports, err := e2eutil.ValidateWithItems(ctx, []validation.Item{fakeSubscriberGrant})
		validationFailures := validation.NotOk(reports)
		if err != nil {
			return errors.Wrap(err, "unexpected validation error")
		} else if validationFailures == nil {
			return errors.New("expected validation error for missing authorized field subscriber grant")
		} else if validationFailures[0].Status != expectedStatus {
			return errors.New("expected " + string(expectedStatus) + " for missing authorized field subscriber grant in " + env + ", got " + string(validationFailures[0].Status))
		}
		return nil
	}

	var err error
	err = checkWithExpectedStatus("production", validation.StatusError)
	if err != nil {
		return err
	}

	err = checkWithExpectedStatus("staging", validation.StatusWarn)
	if err != nil {
		return err
	}

	err = checkWithExpectedStatus("development", validation.StatusWarn)
	if err != nil {
		return err
	}
	return nil
}

func (s *AuthorizedFieldValidationTestSuite) Clean(ctx context.Context) []report.Error {
	ctx = e2eutil.AppendTestPath(ctx, s.TestName())
	return s.DefaultTestSuite.Clean(ctx)
}

func (s *AuthorizedFieldValidationTestSuite) TestName() string {
	return "AuthorizedFieldValidationTestSuite"
}
