package real_environment

import (
	"io/fs"
	"net/url"
	"time"

	"github.com/go-redis/redis/v8"

	"github.com/buildbuddy-io/buildbuddy/server/config"
	"github.com/buildbuddy-io/buildbuddy/server/interfaces"

	bspb "google.golang.org/genproto/googleapis/bytestream"

	pepb "github.com/buildbuddy-io/buildbuddy/proto/publish_build_event"
	repb "github.com/buildbuddy-io/buildbuddy/proto/remote_execution"
	scpb "github.com/buildbuddy-io/buildbuddy/proto/scheduler"
)

type executionClientConfig struct {
	client           repb.ExecutionClient
	maxDuration      time.Duration
	disableStreaming bool
}

func (cc *executionClientConfig) GetExecutionClient() repb.ExecutionClient {
	return cc.client
}
func (cc *executionClientConfig) GetMaxDuration() time.Duration {
	return cc.maxDuration
}
func (cc *executionClientConfig) DisableStreaming() bool {
	return cc.disableStreaming
}

type RealEnv struct {
	schedulerService                 interfaces.SchedulerService
	taskRouter                       interfaces.TaskRouter
	healthChecker                    interfaces.HealthChecker
	workflowService                  interfaces.WorkflowService
	runnerService                    interfaces.RunnerService
	gitProviders                     interfaces.GitProviders
	staticFilesystem                 fs.FS
	appFilesystem                    fs.FS
	blobstore                        interfaces.Blobstore
	invocationDB                     interfaces.InvocationDB
	authenticator                    interfaces.Authenticator
	repoDownloader                   interfaces.RepoDownloader
	executionService                 interfaces.ExecutionService
	cache                            interfaces.Cache
	userDB                           interfaces.UserDB
	authDB                           interfaces.AuthDB
	buildEventHandler                interfaces.BuildEventHandler
	invocationSearchService          interfaces.InvocationSearchService
	invocationStatService            interfaces.InvocationStatService
	usageService                     interfaces.UsageService
	usageTracker                     interfaces.UsageTracker
	splashPrinter                    interfaces.SplashPrinter
	actionCacheClient                repb.ActionCacheClient
	byteStreamClient                 bspb.ByteStreamClient
	schedulerClient                  scpb.SchedulerClient
	remoteExecutionClient            repb.ExecutionClient
	contentAddressableStorageClient  repb.ContentAddressableStorageClient
	metricsCollector                 interfaces.MetricsCollector
	keyValStore                      interfaces.KeyValStore
	APIService                       interfaces.ApiService
	fileCache                        interfaces.FileCache
	remoteExecutionService           interfaces.RemoteExecutionService
	configurator                     *config.Configurator
	executionClients                 map[string]*executionClientConfig
	cacheRedisClient                 *redis.Client
	defaultRedisClient               *redis.Client
	remoteExecutionRedisClient       *redis.Client
	dbHandle                         interfaces.DBHandle
	remoteExecutionRedisPubSubClient *redis.Client
	buildEventProxyClients           []pepb.PublishBuildEventClient
	webhooks                         []interfaces.Webhook
	xcodeLocator                     interfaces.XcodeLocator
	fileResolver                     fs.FS
	mux                              interfaces.HttpServeMux
}

func NewRealEnv(c *config.Configurator, h interfaces.HealthChecker) *RealEnv {
	return &RealEnv{
		configurator:  c,
		healthChecker: h,

		executionClients: make(map[string]*executionClientConfig, 0),
	}
}

// Required -- no SETTERs for these.
func (r *RealEnv) GetConfigurator() *config.Configurator {
	return r.configurator
}

func (r *RealEnv) GetHealthChecker() interfaces.HealthChecker {
	return r.healthChecker
}

// Optional -- may not be set depending on environment configuration.
func (r *RealEnv) SetDBHandle(h interfaces.DBHandle) {
	r.dbHandle = h
}
func (r *RealEnv) GetDBHandle() interfaces.DBHandle {
	return r.dbHandle
}

func (r *RealEnv) SetStaticFilesystem(staticFS fs.FS) {
	r.staticFilesystem = staticFS
}
func (r *RealEnv) GetStaticFilesystem() fs.FS {
	return r.staticFilesystem
}

func (r *RealEnv) SetAppFilesystem(staticFS fs.FS) {
	r.appFilesystem = staticFS
}
func (r *RealEnv) GetAppFilesystem() fs.FS {
	return r.appFilesystem
}

func (r *RealEnv) GetBlobstore() interfaces.Blobstore {
	return r.blobstore
}
func (r *RealEnv) SetBlobstore(bs interfaces.Blobstore) {
	r.blobstore = bs
}

func (r *RealEnv) GetInvocationDB() interfaces.InvocationDB {
	return r.invocationDB
}
func (r *RealEnv) SetInvocationDB(idb interfaces.InvocationDB) {
	r.invocationDB = idb
}

func (r *RealEnv) GetWebhooks() []interfaces.Webhook {
	return r.webhooks
}
func (r *RealEnv) SetWebhooks(wh []interfaces.Webhook) {
	r.webhooks = wh
}

func (r *RealEnv) GetBuildEventHandler() interfaces.BuildEventHandler {
	return r.buildEventHandler
}
func (r *RealEnv) SetBuildEventHandler(b interfaces.BuildEventHandler) {
	r.buildEventHandler = b
}

func (r *RealEnv) GetInvocationSearchService() interfaces.InvocationSearchService {
	return r.invocationSearchService
}
func (r *RealEnv) SetInvocationSearchService(s interfaces.InvocationSearchService) {
	r.invocationSearchService = s
}

func (r *RealEnv) GetUsageService() interfaces.UsageService {
	return r.usageService
}
func (r *RealEnv) SetUsageService(s interfaces.UsageService) {
	r.usageService = s
}

func (r *RealEnv) GetUsageTracker() interfaces.UsageTracker {
	return r.usageTracker
}
func (r *RealEnv) SetUsageTracker(t interfaces.UsageTracker) {
	r.usageTracker = t
}

func (r *RealEnv) GetBuildEventProxyClients() []pepb.PublishBuildEventClient {
	return r.buildEventProxyClients
}
func (r *RealEnv) SetBuildEventProxyClients(clients []pepb.PublishBuildEventClient) {
	r.buildEventProxyClients = clients
}

func (r *RealEnv) GetCache() interfaces.Cache {
	return r.cache
}
func (r *RealEnv) SetCache(c interfaces.Cache) {
	r.cache = c
}

func (r *RealEnv) GetAuthenticator() interfaces.Authenticator {
	return r.authenticator
}
func (r *RealEnv) SetAuthenticator(a interfaces.Authenticator) {
	r.authenticator = a
}

func (r *RealEnv) GetUserDB() interfaces.UserDB {
	return r.userDB
}
func (r *RealEnv) SetUserDB(udb interfaces.UserDB) {
	r.userDB = udb
}

func (r *RealEnv) GetAuthDB() interfaces.AuthDB {
	return r.authDB
}
func (r *RealEnv) SetAuthDB(adb interfaces.AuthDB) {
	r.authDB = adb
}

func (r *RealEnv) GetInvocationStatService() interfaces.InvocationStatService {
	return r.invocationStatService
}
func (r *RealEnv) SetInvocationStatService(iss interfaces.InvocationStatService) {
	r.invocationStatService = iss
}

func (r *RealEnv) SetSplashPrinter(p interfaces.SplashPrinter) {
	r.splashPrinter = p
}
func (r *RealEnv) GetSplashPrinter() interfaces.SplashPrinter {
	return r.splashPrinter
}

func (r *RealEnv) SetActionCacheClient(a repb.ActionCacheClient) {
	r.actionCacheClient = a
}
func (r *RealEnv) GetActionCacheClient() repb.ActionCacheClient {
	return r.actionCacheClient
}

func (r *RealEnv) SetByteStreamClient(b bspb.ByteStreamClient) {
	r.byteStreamClient = b
}
func (r *RealEnv) GetByteStreamClient() bspb.ByteStreamClient {
	return r.byteStreamClient
}

func (r *RealEnv) SetSchedulerClient(s scpb.SchedulerClient) {
	r.schedulerClient = s
}
func (r *RealEnv) GetSchedulerClient() scpb.SchedulerClient {
	return r.schedulerClient
}

func (r *RealEnv) SetRemoteExecutionClient(e repb.ExecutionClient) {
	r.remoteExecutionClient = e
}
func (r *RealEnv) GetRemoteExecutionClient() repb.ExecutionClient {
	return r.remoteExecutionClient
}

func (r *RealEnv) SetContentAddressableStorageClient(c repb.ContentAddressableStorageClient) {
	r.contentAddressableStorageClient = c
}
func (r *RealEnv) GetContentAddressableStorageClient() repb.ContentAddressableStorageClient {
	return r.contentAddressableStorageClient
}

func (r *RealEnv) SetAPIService(s interfaces.ApiService) {
	r.APIService = s
}
func (r *RealEnv) GetAPIService() interfaces.ApiService {
	return r.APIService
}
func (r *RealEnv) SetFileCache(s interfaces.FileCache) {
	r.fileCache = s
}
func (r *RealEnv) GetFileCache() interfaces.FileCache {
	return r.fileCache
}
func (r *RealEnv) SetRemoteExecutionService(e interfaces.RemoteExecutionService) {
	r.remoteExecutionService = e
}
func (r *RealEnv) GetRemoteExecutionService() interfaces.RemoteExecutionService {
	return r.remoteExecutionService
}
func (r *RealEnv) SetSchedulerService(s interfaces.SchedulerService) {
	r.schedulerService = s
}
func (r *RealEnv) GetSchedulerService() interfaces.SchedulerService {
	return r.schedulerService
}
func (r *RealEnv) SetTaskRouter(tr interfaces.TaskRouter) {
	r.taskRouter = tr
}
func (r *RealEnv) GetTaskRouter() interfaces.TaskRouter {
	return r.taskRouter
}
func (r *RealEnv) SetMetricsCollector(c interfaces.MetricsCollector) {
	r.metricsCollector = c
}
func (r *RealEnv) GetMetricsCollector() interfaces.MetricsCollector {
	return r.metricsCollector
}
func (r *RealEnv) SetKeyValStore(c interfaces.KeyValStore) {
	r.keyValStore = c
}
func (r *RealEnv) GetKeyValStore() interfaces.KeyValStore {
	return r.keyValStore
}
func (r *RealEnv) SetExecutionService(e interfaces.ExecutionService) {
	r.executionService = e
}
func (r *RealEnv) GetExecutionService() interfaces.ExecutionService {
	return r.executionService
}
func (r *RealEnv) GetRepoDownloader() interfaces.RepoDownloader {
	return r.repoDownloader
}
func (r *RealEnv) SetRepoDownloader(d interfaces.RepoDownloader) {
	r.repoDownloader = d
}
func (r *RealEnv) GetWorkflowService() interfaces.WorkflowService {
	return r.workflowService
}
func (r *RealEnv) SetWorkflowService(wf interfaces.WorkflowService) {
	r.workflowService = wf
}
func (r *RealEnv) GetRunnerService() interfaces.RunnerService {
	return r.runnerService
}
func (r *RealEnv) SetRunnerService(wf interfaces.RunnerService) {
	r.runnerService = wf
}
func (r *RealEnv) GetGitProviders() interfaces.GitProviders {
	return r.gitProviders
}
func (r *RealEnv) SetGitProviders(gp interfaces.GitProviders) {
	r.gitProviders = gp
}
func (r *RealEnv) GetXcodeLocator() interfaces.XcodeLocator {
	return r.xcodeLocator
}
func (r *RealEnv) SetXcodeLocator(xl interfaces.XcodeLocator) {
	r.xcodeLocator = xl
}

func (r *RealEnv) SetCacheRedisClient(redisClient *redis.Client) {
	r.cacheRedisClient = redisClient
}

func (r *RealEnv) GetCacheRedisClient() *redis.Client {
	return r.cacheRedisClient
}

func (r *RealEnv) SetDefaultRedisClient(redisClient *redis.Client) {
	r.defaultRedisClient = redisClient
}

func (r *RealEnv) GetDefaultRedisClient() *redis.Client {
	return r.defaultRedisClient
}

func (r *RealEnv) SetRemoteExecutionRedisClient(redisClient *redis.Client) {
	r.remoteExecutionRedisClient = redisClient
}

func (r *RealEnv) GetRemoteExecutionRedisClient() *redis.Client {
	return r.remoteExecutionRedisClient
}

func (r *RealEnv) SetRemoteExecutionRedisPubSubClient(client *redis.Client) {
	r.remoteExecutionRedisPubSubClient = client
}

func (r *RealEnv) GetRemoteExecutionRedisPubSubClient() *redis.Client {
	return r.remoteExecutionRedisPubSubClient
}

func (r *RealEnv) GetFileResolver() fs.FS {
	return r.fileResolver
}

func (r *RealEnv) SetFileResolver(fr fs.FS) {
	r.fileResolver = fr
}

func (r *RealEnv) GetSelfAuthURL() *url.URL {
	u, err := url.Parse(r.GetConfigurator().GetAppBuildBuddyURL())
	if err != nil {
		u = &url.URL{
			Scheme: "http",
			Host:   "localhost",
		}
	}
	return u
}

func (r *RealEnv) GetMux() interfaces.HttpServeMux {
	return r.mux
}

func (r *RealEnv) SetMux(mux interfaces.HttpServeMux) {
	r.mux = mux
}
