package client

import (
	"sync"

	"code.justin.tv/beefcake/server/internal/config"
	"code.justin.tv/beefcake/server/internal/legacyperm"
	"code.justin.tv/beefcake/server/internal/role"
	"code.justin.tv/beefcake/server/internal/user"
	"code.justin.tv/sse/malachai/pkg/events"
	"code.justin.tv/sse/malachai/pkg/s2s/callee"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/sirupsen/logrus"
)

// EnvironmentLoader returns a client loader for an environment
func EnvironmentLoader(environment string) (*Loader, error) {
	cfg, err := config.LoadWithS3(environment)
	if err != nil {
		return nil, err
	}
	return &Loader{config: cfg}, nil
}

// Loader loads clients
type Loader struct {
	config *config.Config

	clientsLock sync.Mutex

	clients struct {
		logger *logrus.Logger
	}

	credentials     *credentials.Credentials
	s2sCalleeClient *callee.Client
	session         *session.Session
}

// WithCustomSessionRole returns the same loader with a role arn configured
func (l *Loader) WithCustomSessionRole(roleArn string) *Loader {
	l.clientsLock.Lock()
	defer l.clientsLock.Unlock()

	l.credentials = stscreds.NewCredentials(session.Must(session.NewSession(&aws.Config{
		Region: aws.String("us-west-2"),
	})), roleArn)

	l.session = session.Must(session.NewSession(&aws.Config{
		Region:      aws.String("us-west-2"),
		Credentials: l.credentials,
	}))
	return l
}

// Session returns an aws session
func (l Loader) Session() *session.Session {
	l.clientsLock.Lock()
	defer l.clientsLock.Unlock()

	if l.session == nil {
		l.session = session.Must(session.NewSession(&aws.Config{
			Region: aws.String("us-west-2"),
		}))
	}

	return l.session
}

// Config ...
func (l Loader) Config() *config.Config {
	return l.config
}

// Logger ...
func (l Loader) Logger() logrus.FieldLogger {
	l.clientsLock.Lock()
	defer l.clientsLock.Unlock()
	return l.logger()
}

func (l Loader) logger() logrus.FieldLogger {
	if l.clients.logger == nil {
		l.clients.logger = logrus.New()
	}

	return l.clients.logger
}

// LegacyPermissions ...
func (l Loader) LegacyPermissions() *legacyperm.LegacyPermissions {
	return &legacyperm.LegacyPermissions{
		Config:   l.Config(),
		DynamoDB: dynamodb.New(l.Session()),
	}
}

// Roles ...
func (l Loader) Roles() *role.Roles {
	return &role.Roles{
		Config:   l.Config(),
		DynamoDB: dynamodb.New(l.Session()),
	}
}

// Users ...
func (l Loader) Users() *user.Users {
	return &user.Users{
		Config:   l.Config(),
		DynamoDB: dynamodb.New(l.Session()),
	}
}

// S2SCalleeClient ...
func (l Loader) S2SCalleeClient() (*callee.Client, error) {
	l.clientsLock.Lock()
	defer l.clientsLock.Unlock()

	if l.s2sCalleeClient != nil {
		return l.s2sCalleeClient, nil
	}

	eventsWriterClient, err := events.NewEventLogger(events.Config{}, l.logger())
	if err != nil {
		return nil, err
	}

	s2sCalleeClient := &callee.Client{
		Config: &callee.Config{
			AWSConfigBase:   &aws.Config{Credentials: l.credentials},
			SupportWildCard: true,
		},
		EventsWriterClient: eventsWriterClient,
		Logger:             l.logger(),
		ServiceName:        l.Config().S2SServiceName.Get(),
	}

	if err = s2sCalleeClient.Start(); err != nil {
		return nil, err
	}

	l.s2sCalleeClient = s2sCalleeClient

	return l.s2sCalleeClient, nil
}
