package steps

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/caselib/pkg/utils"
	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"
)

// dropTables	parameter example: test.t1,test.t2
type dropTables struct {
	res       *apiresource.ResourceHandle
	tableList *string
}

func (d dropTables) String() string {
	return fmt.Sprintf("Drop tables %s on resource %s", *d.tableList, d.res.Name)
}

func (d dropTables) Execute(ctx context.Context) error {
	mysqlRes := utils.Try(core.ResolveResourceHandle(d.res)).(resource.MySQLCompatible)
	db := utils.Try(mysqlRes.GetDB("")).(*sql.DB)
	for _, table := range strings.Split(*d.tableList, ",") {
		_, err := db.Exec(fmt.Sprintf("drop table if exists %s", table))
		if err != nil {
			log.Error("drop table error", zap.Error(err))
			return errors.Trace(err)
		}
	}
	return nil
}

var _ Step = &dropTables{}

func DropTablesIfExists(res *apiresource.ResourceHandle, tableList *string) Step {
	return dropTables{
		res,
		tableList,
	}
}

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

type dropTable struct {
	db    *string
	table *string
	res   *apiresource.ResourceHandle
}

func (d dropTable) String() string {
	return fmt.Sprintf("Drop table if exists `%s`.`%s` on resource %s", *d.db, *d.table, d.res.Name)
}

func (d dropTable) Execute(ctx context.Context) error {
	mysqlRes := utils.Try(core.ResolveResourceHandle(d.res)).(resource.MySQLCompatible)
	db := utils.Try(mysqlRes.GetDB("")).(*sql.DB)
	_, err := db.Exec(fmt.Sprintf("drop table if exists `%s`.`%s`", *d.db, *d.table))
	if err != nil {
		log.Error("drop table error", zap.Error(err))
		return errors.Trace(err)
	}
	return err
}

var _ Step = &dropTable{}

func DropTableIfExists(res *apiresource.ResourceHandle, db, table *string) Step {
	return &dropTable{
		db,
		table,
		res,
	}
}

func (b *Builder) DropTableIfExists(res *apiresource.ResourceHandle, db, table *string) *Builder {
	b.steps = append(b.steps, DropTableIfExists(res, db, table))
	return b
}
