package customstatus

import (
	"context"
	"os"
	"strconv"
	"testing"

	"github.com/docker/docker/api/types/container"
	"github.com/testcontainers/testcontainers-go"
	"github.com/testcontainers/testcontainers-go/modules/mysql"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	serverlesscluster "github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/app/openapi/serverless/cluster"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/customstatus"
	serverlessclustersvc "github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/serverless/cluster"
	workflowsvc "github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/workflow"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/accountcli"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/billingcli"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/eventbus/auditlogsender"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/globalserverlesscli"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/db"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/migration"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/workflowimpl"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/config"
)

// Tests in this package will test service functions that interact with the schema.
// Third-party service dependencies are mocked and not tested.
func TestMain(m *testing.M) {
	var (
		database = "local_test"
		password = "password"
		user     = "root"
	)

	ctx := context.Background()

	// Refer to https://golang.testcontainers.org/features/garbage_collector/#ryuk.
	if err := os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true"); err != nil {
		panic(err)
	}
	mysqlContainer, err := mysql.RunContainer(
		ctx,
		testcontainers.WithImage("gcr.io/pingcap-public/third-party/mysql:8.0"),
		mysql.WithDatabase(database),
		mysql.WithUsername(user),
		mysql.WithPassword(password),
		testcontainers.WithHostConfigModifier(func(hostConfig *container.HostConfig) {
			hostConfig.Tmpfs = map[string]string{"/var/lib/mysql": "rw"} // Use tmpfs to speed up tests.
		}),
	)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := mysqlContainer.Terminate(ctx); err != nil {
			panic(err)
		}
	}()

	host, _ := mysqlContainer.Host(ctx)
	portS, _ := mysqlContainer.MappedPort(ctx, "3306/tcp")
	port, _ := strconv.ParseUint(portS.Port(), 10, 16)

	cfg := db.Config{
		User:         user,
		Host:         host,
		Database:     database,
		InitDatabase: database,
		Password:     password,
		Port:         uint16(port),
	}
	gormdb := db.InitDB(&cfg, migration.Records)

	// init workflow and dbprovider
	dbprovider := db.NewProvider(gormdb)
	workflow, err := workflowimpl.InitWorkflow(cfg.ToMySQLConfigWithDefaults(), dbprovider, &config.Workflow{Mode: "competitive"})
	if err != nil {
		panic(err)
	}
	defer workflow.Close()

	m.Run()
}

func initService() (tidbmgmtservicev1.ClusterCustomStatusServiceServer, serverlesscluster.Server, error) {
	gs, err := globalserverlesscli.NewClient(&config.ServerlessGlobalService{
		Url:   "localhost:10001",
		Local: true,
	})

	if err != nil {
		panic("failed to serverless global. " + err.Error())
	}

	// use default db provider
	dbProvider := db.NewProvider(nil)

	clusterRepo := repository.NewClusterRepository(dbProvider)
	resourceTagRepo := repository.NewResourceTagRepository(dbProvider)
	clusterTagBindingRepo := repository.NewClusterTagBindingRepository(dbProvider)
	zeroRentRecordRepo := repository.NewZeroRentRecordRepository(dbProvider)
	customStatusRepo := repository.NewCustomStatusRepository(dbProvider)
	migrationClusterRepo := repository.NewMigrationClusterRepoRepo(dbProvider)
	lockRepo := repository.NewLockRepo(dbProvider)
	shadowPoolRepo := repository.NewShadowPoolRepository(dbProvider)

	accountCli, err := accountcli.NewClient(&config.AccountService{
		Url:   "localhost:10005",
		Local: true,
	})
	if err != nil {
		panic("failed to init account. " + err.Error())
	}
	billingCli := billingcli.NewBillingClient(&config.Config{
		Billing: &config.BillingService{
			Billing2Url: "localhost:10006",
			Local:       true,
		},
	})
	auditLogSender := auditlogsender.NewConsoleAuditLogSender()

	encryptor := remoteencryptor.NewEncryptor("")
	orgRuConfigRepo := repository.NewOrganizationRuConfigRepo(dbProvider)

	if err := workflowsvc.RegisterDags(
		workflowimpl.GetWorkflow(),
		dbProvider,
		customStatusRepo,
		migrationClusterRepo,
		clusterRepo,
		nil,
		nil,
		nil,
		gs,
		nil,
		accountCli,
		encryptor,
		nil,
		nil,
		nil,
		nil,
	); err != nil {
		panic(err)
	}

	clusterSvc, err := serverlessclustersvc.NewService(
		clusterRepo,
		resourceTagRepo,
		clusterTagBindingRepo,
		zeroRentRecordRepo,
		customStatusRepo,
		lockRepo,
		shadowPoolRepo,
		gs,
		accountCli,
		billingCli,
		&config.BizConfig{
			ServerlessFreeQuotaMaxCount: 10,
		},
		dbProvider,
		encryptor,
		workflowimpl.GetWorkflow(),
		orgRuConfigRepo,
		nil,
	)
	if err != nil {
		panic("failed to init serverless svc. " + err.Error())
	}

	customStatusSvc := customstatus.NewService(clusterRepo, customStatusRepo, nil, gs, nil, dbProvider)

	return NewServer(customStatusSvc), serverlesscluster.NewServer(clusterSvc, customStatusSvc, auditLogSender, nil, nil), err
}
