package controlplane

import (
	"go/ast"
	"testing"
)

func TestApplyAllRunnerOptionsOwnSourceBackedConsumers(t *testing.T) {
	for _, test := range []struct {
		legacy  string
		variant string
	}{
		{legacy: "ApplyAllWithOptions", variant: "ApplyAllWithOptionsAndRunnerOptions"},
		{legacy: "ApplyAllIfNeededWithOptions", variant: "ApplyAllIfNeededWithOptionsAndRunnerOptions"},
	} {
		t.Run(test.legacy, func(t *testing.T) {
			legacy := parseControlplaneFuncDecl(t, "operations.go", test.legacy)
			assertApplyAllCallArg(t, legacy, test.variant, 3, isEmptyRunnerOptionsLiteral)

			variant := parseControlplaneFuncDecl(t, "operations.go", test.variant)
			assertApplyAllCallArg(t, variant, "applyAllWithOptions", 3, func(expr ast.Expr) bool {
				return isIdentifier(expr, "runnerOptions")
			})
		})
	}

	applyAll := parseControlplaneFuncDecl(t, "operations.go", "applyAllWithOptions")
	assertApplyAllCallArg(t, applyAll, "NewRunnerWithOptions", 1, func(expr ast.Expr) bool {
		return isIdentifier(expr, "runnerOptions")
	})
	assertApplyAllCallArg(t, applyAll, "refreshPersistedRuntimeDeclarationsForActivePacksWithComponentKindRegistrationSet", 2, isRunnerComponentRegistrationSetCall)
	assertApplyAllCallArg(t, applyAll, "VerifyWithRunnerOptions", 2, func(expr ast.Expr) bool {
		return isIdentifier(expr, "runnerOptions")
	})
	if call := findFuncCallByName(applyAll, "NewRunner"); call != nil {
		t.Fatal("applyAllWithOptions must not discard RunnerOptions through NewRunner")
	}
	if call := findFuncCallByName(applyAll, "refreshPersistedRuntimeDeclarationsForActivePacks"); call != nil {
		t.Fatal("applyAllWithOptions must not discard runner registrations during persisted declaration refresh")
	}
	if call := findFuncCallByName(applyAll, "Verify"); call != nil {
		t.Fatal("applyAllWithOptions must not discard runner registrations during final verification")
	}
}

func assertApplyAllCallArg(t *testing.T, fn *ast.FuncDecl, callName string, argIndex int, matches func(ast.Expr) bool) {
	t.Helper()
	call := findFuncCallByName(fn, callName)
	if call == nil {
		t.Fatalf("%s must call %s", fn.Name.Name, callName)
	}
	if len(call.Args) <= argIndex || !matches(call.Args[argIndex]) {
		t.Fatalf("%s must pass the owned RunnerOptions value to %s argument %d", fn.Name.Name, callName, argIndex)
	}
}

func isEmptyRunnerOptionsLiteral(expr ast.Expr) bool {
	literal, ok := expr.(*ast.CompositeLit)
	return ok && isIdentifier(literal.Type, "RunnerOptions") && len(literal.Elts) == 0
}
