package message

import (
	"context"
	"database/sql"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

type messageSearchReadinessQueryer struct {
	database string
	row      messageSearchReadinessRow
	queries  []string
	args     [][]any
}

func (queryer *messageSearchReadinessQueryer) Database() string { return queryer.database }

func (queryer *messageSearchReadinessQueryer) QueryRow(_ context.Context, query string, args ...any) coredb.Row {
	queryer.queries = append(queryer.queries, query)
	queryer.args = append(queryer.args, append([]any(nil), args...))
	return queryer.row
}

type messageSearchReadinessRow struct {
	values []any
	err    error
}

func (row messageSearchReadinessRow) Scan(dest ...any) error {
	if row.err != nil {
		return row.err
	}
	for index, value := range row.values {
		switch target := dest[index].(type) {
		case *bool:
			*target = value.(bool)
		case *string:
			*target = value.(string)
		default:
			return errors.New("unsupported scan target")
		}
	}
	return nil
}

func TestBuildTiDBFTSMatchLiteral(t *testing.T) {
	t.Parallel()

	require.Equal(t, "'deploy production'", BuildTiDBFTSMatchLiteral("  deploy   production  "))
	require.Equal(t, "'don''t \\\\ break'", BuildTiDBFTSMatchLiteral("don't \\ break"))
}

func TestBuildTiDBFTSMatchConditionUsesLiteralFirstArgument(t *testing.T) {
	t.Parallel()

	require.Equal(t, "fts_match_word('deploy production', docs.content)", BuildTiDBFTSMatchCondition("  deploy   production  ", "docs.content"))
	require.Equal(t, "fts_match_word('don''t \\\\ break', docs.content)", BuildTiDBFTSMatchCondition("don't \\ break", "docs.content"))
}

func TestMessageSearchSupportRequiresTiDBAndReadReady(t *testing.T) {
	t.Parallel()

	require.False(t, supportsMessageSearch(context.Background(), nil))
	require.False(t, supportsMessageSearch(context.Background(), &messageSearchReadinessQueryer{database: "postgres", row: messageSearchReadinessRow{values: []any{true}}}))

	queryer := &messageSearchReadinessQueryer{database: "tidb", row: messageSearchReadinessRow{values: []any{true}}}
	require.True(t, supportsMessageSearch(nil, queryer))
	require.Equal(t, []any{TiDBMessageSearchProjectionName}, queryer.args[0])

	queryer.row = messageSearchReadinessRow{values: []any{false}}
	require.False(t, supportsMessageSearch(context.Background(), queryer))
}

func TestRequireTiDBMessageSearchFinalizedMapsStateErrors(t *testing.T) {
	t.Parallel()

	queryer := &messageSearchReadinessQueryer{database: "tidb", row: messageSearchReadinessRow{err: sql.ErrNoRows}}
	require.ErrorContains(t, RequireTiDBMessageSearchFinalized(context.Background(), queryer), "projection state is missing")

	queryer.row = messageSearchReadinessRow{values: []any{"backfilling", TiDBMessageSearchFinalizeComplete, TiDBMessageSearchFinalizeComplete, true}}
	require.ErrorContains(t, RequireTiDBMessageSearchFinalized(context.Background(), queryer), "until the TiDB projection is ready")

	queryer.row = messageSearchReadinessRow{values: []any{TiDBMessageSearchFinalizeComplete, TiDBMessageSearchFinalizeComplete, TiDBMessageSearchFinalizeComplete, true}}
	require.NoError(t, RequireTiDBMessageSearchFinalized(context.Background(), queryer))
}
