package deps

import (
	"context"
	"errors"

	"github.com/go-logr/logr"
	"go.uber.org/dig"
	"gorm.io/gorm"

	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/database"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/database/transaction"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/logger"
	"github.com/tidbcloud/aisupport/services/agent-gateway/config"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/app"
	agenthandler "github.com/tidbcloud/aisupport/services/agent-gateway/internal/app/handlers/agent"
	gatewayhandler "github.com/tidbcloud/aisupport/services/agent-gateway/internal/app/handlers/gateway"
	sessionhandler "github.com/tidbcloud/aisupport/services/agent-gateway/internal/app/handlers/session"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/integrations/agentsidecar"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/integrations/core"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/services"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/services/agent"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/services/gateway"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/services/session"
	"github.com/tidbcloud/aisupport/services/agent-gateway/internal/services/task"
)

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 {
	factories := []any{
		func() context.Context { return ctx },
		func() *config.RootConfig { return conf },
		func() logr.Logger { return logger.Logger },
		newDB,
		core.NewClient,
		agentsidecar.NewRegistry,
		session.NewService,
		agent.NewService,
		task.NewTaskService,
		gateway.NewService,
		gatewayhandler.NewHandler,
		agenthandler.NewHandler,
		sessionhandler.NewHandler,
		app.NewMetricsGatewayHandler,
		app.NewRPCServer,
		app.NewRPCGatewayServer,
		services.NewManager,
	}
	for _, factory := range factories {
		if err := d.Container.Provide(factory); err != nil {
			return err
		}
	}

	return nil
}

func newDB(conf *config.RootConfig) (*gorm.DB, transaction.Provider, error) {
	db, err := database.Connect(conf.Database, "AgentGatewayDB")
	if err != nil {
		return nil, nil, err
	}
	return db, transaction.NewProvider(db), nil
}

func (d *Deps) Close() error {
	return d.Container.Invoke(func(db *gorm.DB, coreClient *core.Client, sidecars *agentsidecar.Registry) error {
		sqlDB, err := db.DB()
		if err != nil {
			return err
		}
		return errors.Join(sidecars.Close(), coreClient.Close(), sqlDB.Close())
	})
}
