package deps

import (
	"context"

	"go.uber.org/dig"

	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/asset"
	larkcli "github.com/tidbcloud/aisupport/packages/sdk-go/infra/lark/client"
	"github.com/tidbcloud/aisupport/services/core-server/config"
	"github.com/tidbcloud/aisupport/services/core-server/internal/app"
	audithandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/audit"
	authhandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/auth"
	calendarhandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/calendar"
	iamhandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/iam"
	resourcetaghandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/resourcetag"
	userhandler "github.com/tidbcloud/aisupport/services/core-server/internal/app/handlers/user"
	"github.com/tidbcloud/aisupport/services/core-server/internal/clients/aws"
	"github.com/tidbcloud/aisupport/services/core-server/internal/clients/gcp"
	"github.com/tidbcloud/aisupport/services/core-server/internal/db"
	"github.com/tidbcloud/aisupport/services/core-server/internal/services"
	auditservice "github.com/tidbcloud/aisupport/services/core-server/internal/services/audit"
	calendarservice "github.com/tidbcloud/aisupport/services/core-server/internal/services/calendar"
	iamservice "github.com/tidbcloud/aisupport/services/core-server/internal/services/iam"
	resourcetagservice "github.com/tidbcloud/aisupport/services/core-server/internal/services/resourcetag"
	userservice "github.com/tidbcloud/aisupport/services/core-server/internal/services/user"
)

type Deps struct {
	Container *dig.Container
}

func NewDeps() *Deps {
	return &Deps{
		Container: dig.New(),
	}
}

func (d *Deps) Init(ctx context.Context, conf *config.RootConfig) error {
	coreFactories := []any{
		func() *config.RootConfig { return conf },
		func() context.Context { return ctx },
		db.New,
	}
	for _, provider := range coreFactories {
		if err := d.Container.Provide(provider); err != nil {
			return err
		}
	}

	cliFactories := []any{
		newLarkClient,
		newAWSClients,
		newAsset,
		newGCPAdminClient,
	}
	for _, factory := range cliFactories {
		if err := d.Container.Provide(factory); err != nil {
			return err
		}
	}

	svcFactories := []any{
		auditservice.NewService,
		calendarservice.NewService,
		resourcetagservice.NewService,
		userservice.NewService,
		iamservice.NewService,
	}
	for _, factory := range svcFactories {
		if err := d.Container.Provide(factory); err != nil {
			return err
		}
	}
	if err := d.Container.Provide(services.NewServiceManager); err != nil {
		return err
	}

	handlerFactories := []any{
		authhandler.NewHandler,
		audithandler.NewHandler,
		calendarhandler.NewHandler,
		iamhandler.NewHandler,
		resourcetaghandler.NewHandler,
		userhandler.NewHandler,
	}
	for _, factory := range handlerFactories {
		if err := d.Container.Provide(factory); err != nil {
			return err
		}
	}
	if err := d.Container.Provide(app.NewRPCServer); err != nil {
		return err
	}
	if err := d.Container.Provide(app.NewRPCGatewayServer); err != nil {
		return err
	}

	return nil
}

func newLarkClient(ctx context.Context, conf *config.RootConfig) (*larkcli.LarkClient, error) {
	return larkcli.New(ctx, conf.Clients.Lark)
}

func newAWSClients(ctx context.Context) (*aws.Clients, error) {
	return aws.NewClients(ctx)
}

func newAsset(conf *config.RootConfig, clients *aws.Clients) asset.Asset {
	return asset.NewS3Asset(clients.S3(), conf.Asset.BucketName)
}

func newGCPAdminClient(ctx context.Context, conf *config.RootConfig) (gcp.AdminCli, error) {
	return gcp.NewAdminClientFromCred(ctx, []byte(conf.Clients.GCP.Cred))
}
