package controller

import (
	"context"

	"github.com/aws/aws-sdk-go-v2/aws"
	awsconfig "github.com/aws/aws-sdk-go-v2/config"
	backendcmd "github.com/tidbcloud/chaos-svc/pkg/application/cmd/backend"
	experimentcmd "github.com/tidbcloud/chaos-svc/pkg/application/cmd/experiment"
	backend_query "github.com/tidbcloud/chaos-svc/pkg/application/query/backend"
	"github.com/tidbcloud/chaos-svc/pkg/application/query/cluster"
	experiment_query "github.com/tidbcloud/chaos-svc/pkg/application/query/experiment"
	"github.com/tidbcloud/chaos-svc/pkg/domain/service/backend"
	"github.com/tidbcloud/chaos-svc/pkg/domain/service/workflow"
	"github.com/tidbcloud/chaos-svc/pkg/infra/config"
	"github.com/tidbcloud/chaos-svc/pkg/infra/facade_imp"
	"github.com/tidbcloud/chaos-svc/pkg/infra/repo_imp"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/gormutil"
	"go.uber.org/dig"
	"gorm.io/gorm"
)

var (
	globalDI *dig.Container
)

func init() {
	globalDI = dig.New()
}

func mustProvide(constructor interface{}, options ...dig.ProvideOption) {
	if err := globalDI.Provide(constructor, options...); err != nil {
		panic(err)
	}
}

func DiCall(fn interface{}, options ...dig.InvokeOption) error {
	return globalDI.Invoke(fn, options...)
}

func InitDependencies() {
	mustProvide(func() *config.ChaosConfig { return config.Global() })
	mustProvide(newGormDB)
	mustProvide(newDBProvider)
	mustProvide(newTransProvider)
	mustProvide(newAWSConfig)

	// domain service
	mustProvide(backend.NewEnableBackendSvc)
	mustProvide(backend.NewDisableBackendSvc)
	mustProvide(backend.NewSyncBackendFromInfraSvc)
	mustProvide(workflow.NewCreateWorkflowSvc)
	mustProvide(workflow.NewSpecConverter)
	mustProvide(workflow.NewUpdateWorkflowSvc)
	mustProvide(workflow.NewDeleteWorkflowSvc)
	mustProvide(workflow.NewSyncBackendFromInfraSvc)

	// application service
	mustProvide(backendcmd.NewEnableBackendSvc)
	mustProvide(backendcmd.NewDisableBackendSvc)
	mustProvide(backendcmd.NewSyncBackendFromInfraSvc)
	mustProvide(experimentcmd.NewCreateExperimentSvc)
	mustProvide(experimentcmd.NewUpdateExperimentSvc)
	mustProvide(experimentcmd.NewDeleteExperimentSvc)
	mustProvide(experimentcmd.NewSyncWorkflowFromInfraSvc)

	mustProvide(backend_query.NewGetBackendSvc)
	mustProvide(experiment_query.NewListExperimentSvc)
	mustProvide(experiment_query.NewGetExperimentSvc)
	mustProvide(cluster.NewGetTiDBClusterSvc)

	// infra
	mustProvide(repo_imp.NewBackendRepo)
	mustProvide(repo_imp.NewWorkflowRepo)
	mustProvide(repo_imp.NewWorkflowLockRepo)

	mustProvide(facade_imp.NewInfra)
	// mustProvide(facade_imp.NewCoreSvc)
	mustProvide(facade_imp.NewTimeFacade)

	// worker
	mustProvide(NewGlobalWorker)
	mustProvide(NewSyncBackendWorker)
	mustProvide(NewSyncWorkflowWorker)
}

func newGormDB(conf *config.ChaosConfig) *gorm.DB {
	handle, err := gormutil.InitializeWithoutMigrate(conf.MysqlConfig)
	if err != nil {
		panic(err)
	}
	return handle
}

func newDBProvider(handle *gorm.DB) db.Provider {
	return db.NewProvider(handle)
}

func newTransProvider(handle *gorm.DB) db.TransProvider {
	return db.NewProvider(handle)
}

func newAWSConfig() aws.Config {
	config, err := awsconfig.LoadDefaultConfig(context.TODO())
	if err != nil {
		panic("AWS configuration error, " + err.Error())
	}
	return config
}
