package controlplane

import (
	"errors"
	"go/ast"
	"testing"
)

func TestApplyAllMarkerGateIncludesPhysicalTableDiscoveryFailure(t *testing.T) {
	job := &JobResult{Steps: []StepResult{{
		Name:  "DiscoverAndRegisterPhysicalTables",
		Error: errors.New("unmanaged table registry unavailable"),
	}}}
	failed := job.FailedStepNames(convergenceCriticalRepairSteps)
	if len(failed) != 1 || failed[0] != "DiscoverAndRegisterPhysicalTables" {
		t.Fatalf("failed convergence steps = %v, want physical-table discovery", failed)
	}

	repair := parseControlplaneFuncDecl(t, "operations.go", "RunRepairWithOptionsAndRunnerOptions")
	if gate := findFuncCallByName(repair, "FailedStepNames"); gate == nil {
		t.Fatal("RunRepair does not enforce the convergence-critical marker gate")
	}

	applyAll := parseControlplaneFuncDecl(t, "operations.go", "applyAllWithOptions")
	assertApplyAllCallArg(
		t,
		applyAll,
		"RunRepairWithOptionsAndRunnerOptions",
		2,
		repairOptionsEnableFailClosedConvergence,
	)
	runRepair := findFuncCallByName(applyAll, "RunRepairWithOptionsAndRunnerOptions")
	completedMarker := findFuncCallByName(applyAll, "MarkControlPlaneApplyAllCompleted")
	if completedMarker == nil {
		t.Fatal("ApplyAll does not write its completed marker")
	}
	if runRepair.Pos() >= completedMarker.Pos() {
		t.Fatal("ApplyAll must enforce the repair marker gate before writing completed")
	}
}

func repairOptionsEnableFailClosedConvergence(expr ast.Expr) bool {
	literal, ok := expr.(*ast.CompositeLit)
	if !ok || !isIdentifier(literal.Type, "RepairOptions") {
		return false
	}
	for _, element := range literal.Elts {
		keyValue, ok := element.(*ast.KeyValueExpr)
		if !ok || !isIdentifier(keyValue.Key, "FailClosedOnConvergenceRepairs") {
			continue
		}
		return isIdentifier(keyValue.Value, "true")
	}
	return false
}
