package dbctrl

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"validate/util"
)

const GetAllDBAccountSQL = `SELECT user FROM mysql.user WHERE 
                                user NOT IN ('root','role_admin','role_cdc_admin','cloud_admin') 
                              AND Token_issuer <> 'pingcap.com';`
const ShowDBAccountCreationFormat = "SHOW CREATE USER '%s';"
const ShowUserGrantFormat = "SHOW GRANTS FOR '%s'@'%%';"

type DBAccountInfo struct {
	Name       string
	CreateStmt string
	GrantStmt  string
}

func ShowCreateDBAccount(dbAccount string, db *sql.Tx) (string, error) {
	var showCreateStmt = fmt.Sprintf(ShowDBAccountCreationFormat, dbAccount)

	var dbAccountCreationStmt string
	err := db.QueryRow(showCreateStmt).Scan(&dbAccountCreationStmt)
	if err != nil {
		return "", err
	}

	dbAccountCreationStmt = strings.ReplaceAll(dbAccountCreationStmt, "$", ";")
	return dbAccountCreationStmt, nil
}

func ShowGrantDBAccount(user string, db *sql.Tx) (string, error) {
	var showGrantStmt = fmt.Sprintf(ShowUserGrantFormat, user)

	var dbAccountGrantStmt string
	err := db.QueryRow(showGrantStmt).Scan(&dbAccountGrantStmt)
	if err != nil {
		return "", err
	}
	//     sed 's/\(GRANT .*\)/\1;/;s/^\(Grants for .*\)/-- \1 /;/--/{x;p;x;}'

	return dbAccountGrantStmt, err
}

func GetAllDBAccount(db *sql.Tx) ([]string, error) {
	var dbAccountNameList []string
	rows, err := db.Query(GetAllDBAccountSQL)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		var dbAccount string

		if err = rows.Scan(&dbAccount); err != nil {
			return nil, err
		}
		dbAccountNameList = append(dbAccountNameList, dbAccount)
	}

	return dbAccountNameList, nil
}

func (dbAccount *DBAccountInfo) ToString() string {
	dbAccountInfo := fmt.Sprintf("-- Create Statement: \n")
	dbAccountInfo += fmt.Sprintf("%s;\n", dbAccount.CreateStmt)
	dbAccountInfo += fmt.Sprintf("-- Grant Statement: \n")
	dbAccountInfo += fmt.Sprintf("%s;\n", dbAccount.GrantStmt)
	dbAccountInfo += fmt.Sprintf("--------------------")

	return dbAccountInfo
}

func (dba1 *DBAccountInfo) IsNameIdentical(dba2 *DBAccountInfo) bool {
	return 0 == strings.Compare(strings.TrimSpace(dba1.Name), strings.TrimSpace(dba2.Name))
}

func PrintDBAccountList(dbAccountList []*DBAccountInfo) (err error) {
	if dbAccountList == nil {
		return errors.New("empty DB account list")
	}

	for idx, dbAccount := range dbAccountList {
		dbAccountInfo := fmt.Sprintf("\n-- %d. %s\n", idx+1, dbAccount.Name)
		dbAccountInfo += dbAccount.ToString()
		util.Logger.Printf(dbAccountInfo)
	}

	return nil
}
