package qhad

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"net/url"
	"strconv"
	"time"

	"github.com/pingcap/endless/pkg/client/tidb"
	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/endless/pkg/workloadnode/cmdstr"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/endless/testcase/oltp/scenario/params"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

func InitializeAddIndex001Params(ctx context.Context, tidbClient *tidb.Client) (*params.AddIndex, error) {
	params := params.AddIndex{
		RunTime:    1 * time.Hour,
		WarmUpTime: 7 * time.Minute,
	}
	return &params, nil
}

func AddandDropIndexForAllTablesRepeatedly(
	ctx context.Context,
	tc resource.TiDBCluster,
	opts cmdstr.QHAdOptions,
) error {
	oltppkg.CancelAllDDLTasks(context.Background(), tc)
	defer oltppkg.CancelAllDDLTasks(context.Background(), tc)
	// TODO: recover config after test, such as drop index for all tables which added index successfully.
	dbName := opts.DBName
	tables := oltppkg.GetAllTablesNames(ctx, tc, dbName)
	tableNum := len(tables)
	addIndexColumnName := "create_date"
	addIndexName := fmt.Sprintf("%s_%s", "index_test", strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
	log.Info("add index", zap.String("addIndexColumnName", addIndexColumnName))
	var indexName string
	db := Try(ConnectDbWithTimeout(tc, dbName, "timeout=300m", "readTimeout=300m", "writeTimeout=300m")).(*sql.DB)
	defer db.Close()
	for {
		select {
		case <-ctx.Done():
			log.Info("add index ctx done.")
			return nil
		default:
		}
		for i := 0; i < tableNum; i++ {
			tableName := tables[i]
			query := fmt.Sprintf("select column_name from information_schema.statistics where table_schema='%s' and table_name='%s' and column_name='%s' and index_name='%s';", dbName, tableName, addIndexColumnName, addIndexName)
			dropIndex := fmt.Sprintf("alter table %s drop index %s;", tableName, addIndexName)
			addIndex := fmt.Sprintf("alter table %s add index %s (%s);", tableName, addIndexName, addIndexColumnName)
			if DbQueryRowWithLog(db, query).Scan(&indexName) == nil {
				Try(DbExecWithIgnoreCtxDeadline(ctx, db, dropIndex))
			}
			Try(DbExecWithIgnoreCtxDeadline(ctx, db, addIndex))
			Try(DbExecWithIgnoreCtxDeadline(ctx, db, dropIndex))
		}
	}
}

func ConnectDbWithTimeout(tc resource.TiDBCluster, dbName string, params ...string) (*sql.DB, error) {
	dbUrl := Try(tc.ServiceURL(resource.DBAddr)).(*url.URL)
	db, err := sql.Open("mysql", resource.BuildDSN(dbUrl, dbName, params...))
	if err != nil {
		return nil, err
	}
	return db, nil
}

func DbQueryContextWithLog(ctx context.Context, db *sql.DB, query string) (*sql.Rows, error) {
	log.Info(query)
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return nil, err
	}
	return rows, nil
}

func DbQueryRowWithLog(db *sql.DB, query string) *sql.Row {
	log.Info(query)
	row := db.QueryRow(query)
	return row
}

func DbExecWithIgnoreCtxDeadline(ctx context.Context, db *sql.DB, execCmd string) error {
	err := oltppkg.DbExecContextWithLog(ctx, db, execCmd)
	if err != nil && !errors.Is(err, context.DeadlineExceeded) {
		return err
	} else {
		return nil
	}
}
