package installation

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

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

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

	queryer := &recordingLockQueryer{rows: []coredb.Row{
		scriptedRow{values: []any{"app-1"}},
	}}
	if err := LockAppInstallationMutationScope(context.Background(), queryer, " app-1 "); err != nil {
		t.Fatalf("LockAppInstallationMutationScope returned error: %v", err)
	}
	if got, want := len(queryer.queries), 1; got != want {
		t.Fatalf("query count = %d, want %d", got, want)
	}
	if query := queryer.queries[0]; !strings.Contains(query, "FROM service_definitions") || !strings.Contains(query, "FOR UPDATE") {
		t.Fatalf("query does not lock the App definition row:\n%s", query)
	}
	if got, want := queryer.args[0], []any{"app-1", "app"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("query args = %#v, want %#v", got, want)
	}
}

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

	queryer := &recordingLockQueryer{rows: []coredb.Row{
		scriptedRow{err: sql.ErrNoRows},
		scriptedRow{err: sql.ErrNoRows},
	}}
	locked, err := LockExistingAppInstallationMutationScope(context.Background(), queryer, "missing")
	if err != nil || locked {
		t.Fatalf("LockExistingAppInstallationMutationScope locked=%v err=%v, want false, nil", locked, err)
	}
	if err := LockAppInstallationMutationScope(context.Background(), queryer, "missing"); !errors.Is(err, ErrNotFound) {
		t.Fatalf("LockAppInstallationMutationScope err = %v, want ErrNotFound", err)
	}
	if got, want := len(queryer.queries), 2; got != want {
		t.Fatalf("query count = %d, want %d", got, want)
	}
}

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

	queryer := &recordingLockQueryer{rows: []coredb.Row{
		scriptedRow{values: []any{"inst-1"}},
	}}
	if err := LockCurrentWorkspaceAppInstallation(
		context.Background(), queryer, " workspace-1 ", " inst-1 ", " app-1 ",
	); err != nil {
		t.Fatalf("LockCurrentWorkspaceAppInstallation returned error: %v", err)
	}
	if got, want := len(queryer.queries), 1; got != want {
		t.Fatalf("query count = %d, want %d", got, want)
	}
	query := queryer.queries[0]
	for _, fragment := range []string{
		"FROM service_installations installation",
		"JOIN extension_catalog_attributions attribution",
		"installation.status = 'active'",
		"FOR UPDATE",
	} {
		if !strings.Contains(query, fragment) {
			t.Fatalf("query missing %q:\n%s", fragment, query)
		}
	}
	if got, want := queryer.args[0], []any{"inst-1", "workspace-1", "app-1", "app"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("query args = %#v, want %#v", got, want)
	}
}

type recordingLockQueryer struct {
	queries []string
	args    [][]any
	rows    []coredb.Row
}

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