package models

import (
	"context"
	"time"

	adminmodel "github.com/tidbcloud/account/internel/svc/admin"
	"github.com/tidbcloud/account/internel/svc/admin/grafana"
	tenant2 "github.com/tidbcloud/account/internel/svc/org/tenantctx"
	token2 "github.com/tidbcloud/account/internel/svc/token"

	"github.com/patrickmn/go-cache"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/account/central/test"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/account/commons/database"
	"github.com/tidbcloud/account/thirdparty/account"
	"github.com/tidbcloud/account/thirdparty/salesforce"
	panicIf "github.com/tidbcloud/pkgv2/panicif"
	"github.com/tidbcloud/pkgv2/secret/encrypt"
	"github.com/tidbcloud/pkgv2/zendesk"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gopkg.in/auth0.v4/management"
)

var (
	adminModel     *adminmodel.Model
	testTokenModel *token2.TestTokenSvc
)

func GetAdminModel() *adminmodel.Model {
	return adminModel
}

func GetTestTokenModel() *token2.TestTokenSvc {
	return testTokenModel
}

func InitMySQL(isUnitTest bool) {
	ctx := context.Background()
	if isUnitTest {
		tilog.Info(ctx, "init db in unittest mode")
		test.Setup()
	} else {
		mysqlConfig := config.Global().MysqlConfig
		tilog.Info(ctx, "Initialize MySQL connection", zap.String("user", mysqlConfig.User))
		_, err := database.Initialize(ctx, mysqlConfig)
		panicIf.Error(err)
	}
}

func InitAdminModel() error {
	var err error

	serverConfig := config.Global().ServerConfig

	aesCrypt, err := encrypt.NewAESCrypt([]byte(serverConfig.SaltSecret))
	tilog.PlainInfo("InitAdminModel get aesCrypt", zap.Any("aesCrypt", aesCrypt))
	if err != nil {
		return errors.Trace(err)
	}
	session := token2.NewSession(aesCrypt)

	zendeskConfig := config.Global().ZendeskConfig
	var zendeskClient *zendesk.Client
	if zendeskConfig.EnableZendesk {
		zendeskClient, err = zendesk.NewClient(zendeskConfig.ZendeskDomain, zendeskConfig.ZendeskEmail, zendeskConfig.ZendeskSecret)
		if err != nil {
			return errors.Annotate(err, "initialize the zendesk client failed")
		}
	}

	auth0Config := config.Global().Auth0Config
	m, err := management.New(auth0Config.Auth0Domain, auth0Config.Auth0ManagementClientID, auth0Config.Auth0ManagementClientSecret)
	if err != nil {
		return errors.Trace(err)
	}

	salesforceConfig := config.Global().SalesforceConfig
	var salesforceClient *salesforce.Client
	if salesforceConfig.EnableSalesforce {
		accessTokenCache := cache.New(20*time.Minute, 10*time.Minute)
		salesforceCfg := &salesforce.Config{
			SalesforceDomain:       config.Global().SalesforceConfig.SalesforceDomain,
			SalesforceClientID:     config.Global().SalesforceConfig.SalesforceClientID,
			SalesforceClientSecret: config.Global().SalesforceConfig.SalesforceClientSecret,
			SalesforceUsername:     config.Global().SalesforceConfig.SalesforceUsername,
			SalesforcePassword:     config.Global().SalesforceConfig.SalesforcePassword,
		}
		salesforceClient, err = salesforce.NewClient(accessTokenCache, salesforceCfg)
		if err != nil {
			return err
		}
	}
	grafanaConfig := config.Global().GrafanaConfig
	grafanaClient := grafana.NewClient(grafanaConfig.Server, grafanaConfig.APIKey, grafana.DefaultHTTPClient)
	tenantModel := tenant2.NewModel(tilog.WithContextL(context.Background()), session, m.User, zendeskClient, salesforceClient, nil, account.GetHandler())
	admin := adminmodel.NewModel(tilog.WithContextL(context.Background()), grafanaClient, salesforceClient, tenantModel, account.GetHandler())

	adminModel = &admin
	t := token2.NewTestTokenSvc()
	testTokenModel = &t
	return nil
}

func InitFakeAdminModel(log *zap.Logger,
	grafanaClient adminmodel.GrafanaCollector, salesforceClient tenant2.SalesforceHandler,
	session *token2.Session, authHandler tenant2.Auth0UserManagementHandler, zendeskHandler tenant2.ZendeskHandler,
	accountAPI account.AccountAPIHandler,
) error {
	tenantModel := tenant2.NewModel(log, session, authHandler, zendeskHandler, salesforceClient, nil, nil)

	m := adminmodel.NewModel(log, grafanaClient, salesforceClient, tenantModel, accountAPI)

	adminModel = &m
	return nil
}
