package steps

import (
	"context"
	"fmt"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/common/model/resource"
	sdkResource "github.com/pingcap/test-infra/sdk/resource"
)

type enableSem struct {
	target   *resource.ResourceHandle
	expected *bool
	timeout  time.Duration
}

func (e *enableSem) String() string {
	return fmt.Sprintf("enabled SEM for target TiDB: %s, expected: %s, timeout: %s", e.target.Name, *e.expected, e.timeout)
}

func (e *enableSem) Execute(ctx context.Context) error {
	// check current @@tidb_enable_enhanced_security
	switch *e.expected {
	case true:
		log.Info("GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'root'@'%', followed by enable-sem")
		SQL := "GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'root'@'%';"
		execSQL := executeSqlStatement{
			handle:     e.target,
			statements: &SQL,
		}
		err := execSQL.Execute(ctx)
		if err != nil {
			return err
		}
		log.Info("GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'root'@'%' ;, followed by enable-sem")
		SQL = "GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'root'@'%'; "
		execSQL = executeSqlStatement{
			handle:     e.target,
			statements: &SQL,
		}
		err = execSQL.Execute(ctx)
		if err != nil {
			return err
		}

		updateTiDBClusterConfig := updateTiDBClusterConfig{
			target:   e.target,
			nodeType: sdkResource.TiDB,
			config:   "[security]\nenable-sem = true",
			timeout:  e.timeout,
		}
		err = updateTiDBClusterConfig.Execute(ctx)
		if err != nil {
			return err
		}

	case false:
		updateTiDBClusterConfig := updateTiDBClusterConfig{
			target:   e.target,
			nodeType: sdkResource.TiDB,
			config:   "[security]\nenable-sem = false",
			timeout:  e.timeout,
		}
		err := updateTiDBClusterConfig.Execute(ctx)
		if err != nil {
			return err
		}
	default:
		log.Error("Unknown parameters. expected must be true or false.")
		return errors.New("Unknown parameters. expected must be true or false.")
	}

	return nil
}

func NewEnableSem(target *resource.ResourceHandle, expected *bool, timeout time.Duration) Step {
	return &enableSem{
		target:   target,
		expected: expected,
		timeout:  timeout,
	}
}

func (b *Builder) EnableSem(mysqlRes *resource.ResourceHandle, expected *bool, timeout time.Duration) *Builder {
	b.steps = append(b.steps, NewEnableSem(mysqlRes, expected, timeout))
	return b
}
