package validation

import (
	"context"
	"fmt"
	"strings"

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

func itemID(item Item) string {
	attrs := item.Attributes()
	attrStrList := make([]string, len(attrs))
	for i, attr := range attrs {
		attrStrList[i] = fmt.Sprintf("%s=%s", attr.Key, attr.Value)
	}
	attrString := strings.Join(attrStrList, ",")
	return fmt.Sprintf("%s(%s)", item.Type(), attrString)
}

// ReportWithEnvironmentSeverity takes an item and its validation failure message,
// and based on the environment passed in will provide a WARN or an ERROR.
// Production environments yield an ERROR while non production yields WARN
// Useful for dynamically choosing a severity level in validation methods
func ReportWithEnvironmentSeverity(item Item, env, msg string) *Report {
	if env == environment.Production {
		return Error(item, msg)
	}
	return Warn(item, msg)
}

type eventStreamMap map[int]*db.EventStream

func getEventStreamMap(ctx context.Context, dbConn db.DB) (eventStreamMap, error) {
	m := make(map[int]*db.EventStream)

	eventStreams, err := dbConn.EventStreams(ctx)
	if err != nil {
		return nil, err
	}

	for _, eventStream := range eventStreams {
		m[eventStream.ID] = eventStream
	}

	return m, nil
}

func (m eventStreamMap) Get(id int) (*db.EventStream, bool) {
	v, found := m[id]
	return v, found
}

type subscriptionTargetMap map[int]*db.SubscriptionTarget

func getSubscriptionTargetMap(ctx context.Context, dbConn db.DB) (subscriptionTargetMap, error) {
	m := make(map[int]*db.SubscriptionTarget)

	targets, err := dbConn.SubscriptionTargets(ctx)
	if err != nil {
		return nil, err
	}

	for _, target := range targets {
		m[target.ID] = target
	}
	return m, nil
}

func (m subscriptionTargetMap) Get(id int) (*db.SubscriptionTarget, bool) {
	v, found := m[id]
	return v, found
}

type iamRoleMap map[int]*db.IAMRole

func getIAMRoleMap(ctx context.Context, dbConn db.DB) (iamRoleMap, error) {
	m := make(iamRoleMap)

	iamRoles, err := dbConn.IAMRoles(ctx)
	if err != nil {
		return nil, err
	}

	for _, role := range iamRoles {
		m[role.ID] = role
	}
	return m, nil
}

func (m iamRoleMap) Get(id int) (*db.IAMRole, bool) {
	v, found := m[id]
	return v, found
}

type authorizedFieldMap map[int]*db.AuthorizedField

func getAuthorizedFieldMap(ctx context.Context, dbConn db.DB) (authorizedFieldMap, error) {
	m := make(authorizedFieldMap)

	authorizedFields, err := dbConn.AuthorizedFields(ctx)
	if err != nil {
		return nil, err
	}

	for _, authField := range authorizedFields {
		m[authField.ID] = authField
	}
	return m, nil
}

func (m authorizedFieldMap) Get(id int) (*db.AuthorizedField, bool) {
	v, found := m[id]
	return v, found
}

type accountMap map[int]*db.Account

func getAccountMap(ctx context.Context, dbConn db.DB) (accountMap, error) {
	m := make(accountMap)

	accounts, err := dbConn.Accounts(ctx)
	if err != nil {
		return nil, err
	}

	for _, account := range accounts {
		m[account.ID] = account
	}
	return m, nil
}

func (m accountMap) Get(id int) (*db.Account, bool) {
	v, found := m[id]
	return v, found
}
