package query

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

	. "github.com/onsi/gomega"
	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

func FullScanAllTablesRepeatedly(ctx context.Context, tc resource.MySQLCompatible, timeout time.Duration, dbName string, filterOutTables, filterInTables []string) error {
	dbUrl := Try(tc.ServiceURL(resource.DBAddr)).(*url.URL)
	db, err := sql.Open("mysql", resource.BuildDSN(dbUrl, dbName, "timeout=300m", "readTimeout=300m", "writeTimeout=300m"))
	Expect(err).Should(Succeed())
	defer db.Close()
	tables := oltppkg.GetAllTablesNames(ctx, tc, dbName)
	tableNum := len(tables)
	timeOut := time.After(timeout)
	log.Info("start full scan for all tables Repeatedly")
	for {
		select {
		case <-ctx.Done():
			log.Info("full scan ctx done.")
			return nil
		case <-timeOut:
			log.Info("full scan ctx timeout.")
			return nil
		default:
		}
		for i := 0; i < tableNum; i++ {
			tableName := tables[i]
			if filterInTables != nil {
				if !oltppkg.StrSlice(filterInTables).Has(tableName) {
					log.Info("not in filterInList, continue", zap.String("tableName", tableName),
						zap.Strings("filterInTables", filterInTables))
					continue
				}
			}
			if filterOutTables != nil {
				if oltppkg.StrSlice(filterOutTables).Has(tableName) {
					log.Info("In filterOutList, skip it, continue", zap.String("tableName", tableName),
						zap.Strings("filterOutTables", filterOutTables))
					continue
				}
			}

			log.Debug("Now scan table:", zap.String("dbName", dbName), zap.String("tableName", tableName))
			query := fmt.Sprintf("select count(*) from %s;", tableName)
			log.Debug(query)
			r, err := db.QueryContext(ctx, query)
			if err == nil {
				rows, err := oltppkg.DumpToByteRows(r)
				if !errors.Is(err, context.DeadlineExceeded) {
					result := rows.ConvertToString("")
					log.Debug(result)
				}
			} else {
				if !errors.Is(err, context.DeadlineExceeded) {
					return err
				}
			}
		}
	}
}
