package oci

import (
	"fmt"

	"go.flipt.io/flipt/internal/containers"
	"go.flipt.io/flipt/internal/oci/ecr"
	"oras.land/oras-go/v2"
	"oras.land/oras-go/v2/registry/remote/auth"
)

type AuthenticationType string

const (
	AuthenticationTypeStatic AuthenticationType = "static"
	AuthenticationTypeAWSECR AuthenticationType = "aws-ecr"
)

func (s AuthenticationType) IsValid() bool {
	switch s {
	case AuthenticationTypeStatic, AuthenticationTypeAWSECR:
		return true
	}

	return false
}

// StoreOptions are used to configure call to NewStore
// This shouldn't be handled directory, instead use one of the function options
// e.g. WithBundleDir or WithCredentials
type StoreOptions struct {
	bundleDir       string
	manifestVersion oras.PackManifestVersion
	auth            credentialFunc
	authCache       auth.Cache
}

// WithCredentials configures username and password credentials used for authenticating
// with remote registries
func WithCredentials(kind AuthenticationType, user, pass string) (containers.Option[StoreOptions], error) {
	switch kind {
	case AuthenticationTypeAWSECR:
		return WithAWSECRCredentials(""), nil
	case AuthenticationTypeStatic:
		return WithStaticCredentials(user, pass), nil
	default:
		return nil, fmt.Errorf("unsupported auth type %s", kind)
	}
}

// WithStaticCredentials configures username and password credentials used for authenticating
// with remote registries
func WithStaticCredentials(user, pass string) containers.Option[StoreOptions] {
	return func(so *StoreOptions) {
		so.auth = func(registry string) auth.CredentialFunc {
			return auth.StaticCredential(registry, auth.Credential{
				Username: user,
				Password: pass,
			})
		}
		so.authCache = auth.DefaultCache
	}
}

// WithAWSECRCredentials configures username and password credentials used for authenticating
// with remote registries
func WithAWSECRCredentials(endpoint string) containers.Option[StoreOptions] {
	return func(so *StoreOptions) {
		store := ecr.NewCredentialsStore(endpoint)
		so.auth = func(registry string) auth.CredentialFunc {
			return ecr.Credential(store)
		}
	}
}

// WithManifestVersion configures what OCI Manifest version to build the bundle.
func WithManifestVersion(version oras.PackManifestVersion) containers.Option[StoreOptions] {
	return func(s *StoreOptions) {
		s.manifestVersion = version
	}
}
