package controlplane

import (
	"context"
	"go/ast"
	"strings"
	"testing"
)

func TestResolveConnectorPushBackfillInstalledPackUsesComponentKindRegistrationSetBeforeAdoption(t *testing.T) {
	fixture := newRegisteredRuntimeConsumerFixture(t)
	row := fixture.row

	pack, err := resolveConnectorPushBackfillInstalledPackWithComponentKindRegistrationSet(
		context.Background(), nil,
		row.Name, row.Category, row.ManifestFormatVersion.String,
		row.SourceKind.String, row.SourceURI.String, row.SourceRevision.String,
		fixture.payloadHash, fixture.options.ComponentKindRegistrationSet,
	)
	if err != nil || pack == nil || pack.Name() != row.Name {
		t.Fatalf("registered connector mirror source resolution = (%v, %v)", pack, err)
	}

	pack, err = resolveConnectorPushBackfillInstalledPack(
		context.Background(), nil,
		row.Name, row.Category, row.ManifestFormatVersion.String,
		row.SourceKind.String, row.SourceURI.String, row.SourceRevision.String,
		fixture.payloadHash,
	)
	if pack != nil || err == nil || !strings.Contains(err.Error(), "runner_managed_test") {
		t.Fatalf("empty-registration connector mirror source resolution = (%v, %v), want unsupported registered kind", pack, err)
	}
}

func TestConnectorPushMirrorBackfillOwnsEveryRegistrationCarrier(t *testing.T) {
	topLevel := parseControlplaneFuncDecl(t, "connector_push_legacy_mirror_backfill.go", "BackfillConnectorPushLegacyMirrorAdoptionsWithComponentKindRegistrationSet")
	assertConnectorPushBackfillRegistrationCalls(t, topLevel, "loadConnectorPushLegacyMirrorBackfillContractsWithComponentKindRegistrationSet", 2, 1)

	load := parseControlplaneFuncDecl(t, "connector_push_legacy_mirror_backfill.go", "loadConnectorPushLegacyMirrorBackfillContractsWithComponentKindRegistrationSet")
	assertConnectorPushBackfillRegistrationCalls(t, load, "resolveConnectorPushBackfillInstalledPackWithComponentKindRegistrationSet", 9, 1)

	resolve := parseControlplaneFuncDecl(t, "connector_push_legacy_mirror_backfill.go", "resolveConnectorPushBackfillInstalledPackWithComponentKindRegistrationSet")
	assertConnectorPushBackfillRegistrationCalls(t, resolve, "ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevisionWithPackSourceOptionsAndComponentKindRegistrationSet", 7, 2)
}

func TestConnectorPushMirrorBackfillOperatorPathsOwnRunnerRegistrations(t *testing.T) {
	applyAll := parseControlplaneFuncDecl(t, "operations.go", "applyAllWithOptions")
	assertApplyAllCallArg(t, applyAll, "BackfillConnectorPushLegacyMirrorAdoptionsWithComponentKindRegistrationSet", 2, isRunnerComponentRegistrationSetCall)

	legacyConvergence := parseControlplaneFuncDecl(t, "operations.go", "ApplySourceConvergence")
	assertApplyAllCallArg(t, legacyConvergence, "ApplySourceConvergenceWithRunnerOptions", 3, isEmptyRunnerOptionsLiteral)

	convergence := parseControlplaneFuncDecl(t, "operations.go", "ApplySourceConvergenceWithRunnerOptions")
	assertApplyAllCallArg(t, convergence, "NewRunnerWithOptions", 1, func(expr ast.Expr) bool {
		return isIdentifier(expr, "runnerOptions")
	})
	assertApplyAllCallArg(t, convergence, "BackfillConnectorPushLegacyMirrorAdoptionsWithComponentKindRegistrationSet", 2, isRunnerComponentRegistrationSetCall)
	if call := findFuncCallByName(convergence, "NewRunner"); call != nil {
		t.Fatal("ApplySourceConvergenceWithRunnerOptions must not discard RunnerOptions through NewRunner")
	}
	if call := findFuncCallByName(convergence, "BackfillConnectorPushLegacyMirrorAdoptions"); call != nil {
		t.Fatal("ApplySourceConvergenceWithRunnerOptions must not discard registrations during connector mirror backfill")
	}
}

func assertConnectorPushBackfillRegistrationCalls(t *testing.T, fn *ast.FuncDecl, callName string, registrationArg int, wantCount int) {
	t.Helper()
	var calls []*ast.CallExpr
	ast.Inspect(fn.Body, func(node ast.Node) bool {
		call, ok := node.(*ast.CallExpr)
		if !ok {
			return true
		}
		name := ""
		switch called := call.Fun.(type) {
		case *ast.Ident:
			name = called.Name
		case *ast.SelectorExpr:
			if called.Sel != nil {
				name = called.Sel.Name
			}
		}
		if name == callName {
			calls = append(calls, call)
		}
		return true
	})
	if len(calls) != wantCount {
		t.Fatalf("%s calls %s %d times, want %d", fn.Name.Name, callName, len(calls), wantCount)
	}
	for _, call := range calls {
		if len(call.Args) <= registrationArg || !isIdentifier(call.Args[registrationArg], "registrations") {
			t.Fatalf("%s must pass registrations to %s argument %d", fn.Name.Name, callName, registrationArg)
		}
	}
}
