package steps

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

type TableIndexes struct {
	Table   string   `json:"table"`
	Indexes []string `json:"indexes"`
}

// .env config example: TI_PARAM_STR_admin_check: [ {"table": "test.t3", "indexes": ["idx_lower_b","idx_a"] } ]
type adminCheckStep struct {
	res            *apiresource.ResourceHandle
	tableIndexList *string
}

func (a adminCheckStep) String() string {
	return fmt.Sprintf("Admin check on `%s` resource `%s`", *(a.tableIndexList), a.res.Name)
}

func (a adminCheckStep) Execute(ctx context.Context) error {
	resolved, err := core.ResolveResourceHandle(a.res)
	if err != nil {
		return err
	}
	mysql := resolved.(resource.MySQLCompatible)
	db, err := mysql.GetDB("")

	var tableIndexes []TableIndexes
	err = json.Unmarshal([]byte(*a.tableIndexList), &tableIndexes)
	if err != nil {
		fmt.Println("json unmarshal failed!")
		return errors.Trace(err)
	}

	for _, tableIndexesList := range tableIndexes {
		table := tableIndexesList.Table

		// admin check for tables
		cmd := fmt.Sprintf("admin check table %s", table)
		log.Info("executing query", zap.String("cmd", cmd))
		_, err = db.Exec(cmd)
		if err != nil {
			log.Error("admin check error", zap.Error(err))
			return errors.Trace(err)
		}

		indexesList := tableIndexesList.Indexes
		// admin check for indexes
		for _, index := range indexesList {
			cmd := fmt.Sprintf("admin check index %s %s", table, index)
			log.Info("executing query", zap.String("cmd", cmd))
			_, err = db.Exec(cmd)
			if err != nil {
				log.Error("admin check error", zap.Error(err))
				return errors.Trace(err)
			}
		}

	}

	return nil
}

func AdminCheck(res *apiresource.ResourceHandle, opts *string) Step {
	return adminCheckStep{
		res:            res,
		tableIndexList: opts,
	}
}

func (b *Builder) AdminCheck(res *apiresource.ResourceHandle, opts *string) *Builder {
	b.steps = append(b.steps, AdminCheck(res, opts))
	return b
}
