package cluster

import (
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/tidbcloud/infra-api/pkg/config"
	"github.com/tidbcloud/infra-api/pkg/consts"
)

const (
	initSQL = `SET GLOBAL tidb_redact_log = 1;`

	analyzeSQL = `SET GLOBAL tidb_analyze_version = 1;`
)

const (
	setupBuiltinRoles = `
CREATE ROLE IF NOT EXISTS 'role_admin';
GRANT ALL ON *.* TO 'role_admin' WITH GRANT OPTION;
REVOKE SYSTEM_USER ON *.* FROM 'role_admin';
REVOKE SHUTDOWN, CONFIG ON *.* FROM 'role_admin';
GRANT ROLE_ADMIN ON *.* to 'role_admin';

# don't create role_developer for now
# CREATE ROLE IF NOT EXISTS 'role_developer';
# GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'role_developer';
# REVOKE SYSTEM_USER ON *.* FROM 'role_developer';
# REVOKE ALL ON mysql.* FROM 'role_developer';

# don't create role_observer for now
# CREATE ROLE IF NOT EXISTS 'role_observer';
# GRANT SELECT ON *.* TO 'role_observer';
# REVOKE SYSTEM_USER ON *.* FROM 'role_observer';
# REVOKE ALL ON mysql.* FROM 'role_observer';

# role_cdc_admin is used by CDC
CREATE ROLE IF NOT EXISTS 'role_cdc_admin';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, SHOW DATABASES, INDEX, CREATE VIEW ON *.* TO 'role_cdc_admin';
# For now, tidb don't support partial revoke.
# REVOKE ALL ON mysql.* FROM 'role_cdc_admin';
GRANT RESTRICTED_REPLICA_WRITER_ADMIN ON *.* TO 'role_cdc_admin';
`

	// NOTE: the root account will be locked after all initial tasks finished in core-svc
	setupUsers = `
CREATE USER IF NOT EXISTS cloud_admin;

GRANT DASHBOARD_CLIENT on *.* TO 'cloud_admin'@'%';
GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_USER_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT RESTRICTED_REPLICA_WRITER_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT BACKUP_ADMIN, RESTORE_ADMIN ON *.* TO 'cloud_admin'@'%';
GRANT ROLE_ADMIN ON *.* to 'role_admin';
GRANT SYSTEM_USER ON *.* TO 'cloud_admin'@'%' WITH GRANT OPTION;

GRANT RELOAD ON *.* TO 'cloud_admin'@'%';
GRANT PROCESS ON *.* TO 'cloud_admin'@'%';
GRANT SHUTDOWN, CONFIG ON *.* TO 'cloud_admin'@'%';

GRANT CREATE USER, CREATE ROLE ON *.* TO 'cloud_admin'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON mysql.* TO 'cloud_admin'@'%';
GRANT SELECT ON information_schema.* TO 'cloud_admin'@'%';
GRANT SELECT ON performance_schema.* TO 'cloud_admin'@'%';

# TODO(chenfei): for compatibility. We can remove the following privileges after all DP service(import, cdc, dm, etc.)
# have finished migrate all features to based on the delegate_user feature.
# Cross region requires these permissions, See https://docs.pingcap.com/tidb/stable/troubleshoot-ticdc/#when-the-sink-of-the-replication-downstream-is-tidb-or-mysql-what-permissions-do-users-of-the-downstream-database-need for more detail
GRANT SELECT ON *.* TO 'cloud_admin'@'%';
GRANT INDEX ON *.* TO 'cloud_admin'@'%';
GRANT INSERT ON *.* TO 'cloud_admin'@'%';
GRANT UPDATE ON *.* TO 'cloud_admin'@'%';
GRANT DELETE ON *.* TO 'cloud_admin'@'%';
GRANT CREATE ON *.* TO 'cloud_admin'@'%';
GRANT DROP ON *.* TO 'cloud_admin'@'%';
GRANT ALTER ON *.* TO 'cloud_admin'@'%';
GRANT CREATE VIEW ON *.* TO 'cloud_admin'@'%';
GRANT SHOW DATABASES on *.* TO 'cloud_admin'@'%';
# used by import
GRANT REFERENCES ON *.* TO 'cloud_admin'@'%';

REVOKE SHUTDOWN, CONFIG ON *.* FROM root;
`
)

// NewDefaultTidbInitializer creates a TidbInitializer for a tidb cluster named `db`
// For newly created cluster >= 5.3, enableSEM should set to true
// and cluster >= 5.1.0, enableAnalyze should set to true
func NewDefaultTidbInitializer(namespace string, enableSEM bool, enableAnalyze bool, experimentalEnabling string) *v1alpha1.TidbInitializer {
	pullIfNotPresent := corev1.PullIfNotPresent
	name := getInitializerName(consts.ClusterPrefix)
	initsql := BuildInitSql(enableSEM, enableAnalyze, experimentalEnabling)
	return &v1alpha1.TidbInitializer{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
		},
		Spec: v1alpha1.TidbInitializerSpec{
			Clusters:        v1alpha1.TidbClusterRef{Namespace: namespace, Name: consts.ClusterPrefix},
			InitSql:         &initsql,
			Image:           config.GetMysqlClientImage(),
			ImagePullPolicy: &pullIfNotPresent,
		},
	}
}

func NewTidbInitializerWithInitSQL(namespace, name string, initsql *string) *v1alpha1.TidbInitializer {
	pullIfNotPresent := corev1.PullIfNotPresent
	return &v1alpha1.TidbInitializer{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
		},
		Spec: v1alpha1.TidbInitializerSpec{
			Clusters:        v1alpha1.TidbClusterRef{Namespace: namespace, Name: consts.ClusterPrefix},
			InitSql:         initsql,
			Image:           config.GetMysqlClientImage(),
			ImagePullPolicy: &pullIfNotPresent,
		},
	}
}

func BuildInitSql(enableSEM, enableAnalyze bool, experimentalEnabling string) string {
	// If tidb_analyze_version is set to 2 in experimentalEnabling, version 1 set by analyzeSQL will be overwritten.
	initsql := initSQL + "\n" + experimentalEnabling

	if enableSEM {
		initsql = setupBuiltinRoles + "\n" + setupUsers + "\n" + initsql
	}

	if enableAnalyze {
		initsql = analyzeSQL + "\n" + initsql
	}
	return initsql
}
