package controlplane

import (
	"context"
	"database/sql"
	"errors"
	"go/ast"
	"strings"
	"testing"

	database "github.com/nexuscrm/db"
	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

func TestBookkeepingConvergenceDefersToManagedApprovalApply(t *testing.T) {
	_, options := runnerManagedVerifyFixture(t)
	registration := options.ComponentKindRegistrationSet.Registrations()[0]
	registration.Contract.Kind = cp.ComponentKindApprovalProcess
	registration.Capability.Kind = cp.ComponentKindApprovalProcess
	registration.Contract.WriteSet = []cp.ComponentWriteSurface{{Table: "ProcessDefinition", Fact: "approval process metadata"}}
	set, err := packs.NewComponentKindRegistrationSet([]cp.ComponentKindRegistration{registration})
	if err != nil {
		t.Fatalf("managed approval registration set: %v", err)
	}
	runner := &Runner{
		DB: database.NewConnectionFromDB(nil),
		SvcMgr: &runnerServiceGraph{
			ServiceGraph: newTestServiceGraph(testServiceGraphConfig{}),
			options:      RunnerOptions{ComponentKindRegistrationSet: set},
		},
	}
	pack := testControlplaneSourceBackedPack(t)

	originalVerify := verifyResolvedSourceBackedPackForReconcile
	originalMark := markVerifiedInstalledPackConvergedForReconcile
	t.Cleanup(func() {
		verifyResolvedSourceBackedPackForReconcile = originalVerify
		markVerifiedInstalledPackConvergedForReconcile = originalMark
	})
	verifyResolvedSourceBackedPackForReconcile = func(context.Context, installedPackRow, *sql.DB, *packs.SourceBackedPack) error {
		t.Fatal("managed approval coverage reached bookkeeping live verify")
		return nil
	}
	markVerifiedInstalledPackConvergedForReconcile = func(context.Context, *sql.DB, packs.Pack, string) error {
		t.Fatal("managed approval coverage reached bookkeeping convergence")
		return nil
	}

	converged, err := tryConvergeVerifiedInstalledPackSourceStateWithoutReapply(
		context.Background(), runner, installedPackRow{Name: pack.Name()}, pack,
		activeInstalledPackSourceStateDrift{kind: activeInstalledPackSourceStateDriftInconsistentHashes},
	)
	if err != nil {
		t.Fatalf("bookkeeping convergence guard: %v", err)
	}
	if converged {
		t.Fatal("managed approval coverage used bookkeeping-only convergence")
	}
}

func TestConvergenceSourceReadsUseRunnerRegistrationSet(t *testing.T) {
	fixture := newRegisteredRuntimeConsumerFixture(t)
	row := fixture.row
	metadata := packs.PackHashMetadataForPack(fixture.pack)
	row.Status = installedPackStatusActive
	row.PackVersion = sql.NullString{String: metadata.PackVersion, Valid: true}
	row.PlatformCompatibility = sql.NullString{String: metadata.PlatformCompatibility, Valid: true}
	row.DesiredPayloadHash = sql.NullString{String: fixture.payloadHash, Valid: true}
	row.PayloadHashAlgorithm = sql.NullString{String: metadata.PayloadHashAlgorithm, Valid: true}

	registeredRunner := &Runner{SvcMgr: &runnerServiceGraph{
		ServiceGraph: newTestServiceGraph(testServiceGraphConfig{}),
		options:      fixture.options,
	}}
	drifts, driftedPacks, err := detectActiveInstalledPackSourceStateDrift(context.Background(), registeredRunner, []installedPackRow{row})
	if err != nil {
		t.Fatalf("registered convergence drift read: %v", err)
	}
	if len(drifts) != 0 || len(driftedPacks) != 0 {
		t.Fatalf("registered convergence drift = (%#v, %#v), want no drift", drifts, driftedPacks)
	}

	pack, err := sourceBackedPackFromInstalledRowForReconcileWithFormulaBundlesAndComponentKindRegistrationSet(
		context.Background(), nil, row, &activeFormulaFunctionBundlesValidationContext{}, fixture.options.ComponentKindRegistrationSet,
	)
	if err != nil || pack == nil || pack.Name() != row.Name {
		t.Fatalf("registered final convergence read = (%v, %v)", pack, err)
	}

	emptyRunner := &Runner{}
	if _, _, err := detectActiveInstalledPackSourceStateDrift(context.Background(), emptyRunner, []installedPackRow{row}); err == nil || !strings.Contains(err.Error(), "runner_managed_test") {
		t.Fatalf("missing drift registration error = %v", err)
	}
	if _, err := sourceBackedPackFromInstalledRowForReconcileWithFormulaBundles(
		context.Background(), nil, row, &activeFormulaFunctionBundlesValidationContext{},
	); err == nil || !strings.Contains(err.Error(), "runner_managed_test") {
		t.Fatalf("missing final-read registration error = %v", err)
	}
}

func TestConvergenceNoBundleFallbackPreservesSourceResolutionError(t *testing.T) {
	fixture := newRegisteredRuntimeConsumerFixture(t)
	originalErr := errors.New("formula references function(s) not implemented by the runtime evaluator: convergence sentinel")
	noBundles := func() *activeFormulaFunctionBundlesValidationContext {
		return &activeFormulaFunctionBundlesValidationContext{loaded: true}
	}

	originalResolve := sourceBackedPackFromInstalledRowForReconcile
	t.Cleanup(func() { sourceBackedPackFromInstalledRowForReconcile = originalResolve })
	sourceBackedPackFromInstalledRowForReconcile = func(context.Context, *sql.DB, installedPackRow, ...packs.SourceBackedPackOption) (*packs.SourceBackedPack, error) {
		return nil, originalErr
	}
	pack, err := sourceBackedPackFromInstalledRowForReconcileWithFormulaBundlesAndComponentKindRegistrationSet(
		context.Background(), nil, fixture.row, noBundles(), packs.ComponentKindRegistrationSet{},
	)
	if pack != nil || !errors.Is(err, originalErr) {
		t.Fatalf("empty-registration no-bundle result = (%v, %v), want original source error", pack, err)
	}

	registrations := fixture.options.ComponentKindRegistrationSet.Registrations()
	registrations[0].BuildHandler = func(cp.ComponentBuildContext, cp.ComponentBuildRequest) error {
		return originalErr
	}
	registeredSet, err := packs.NewComponentKindRegistrationSet(registrations)
	if err != nil {
		t.Fatal(err)
	}
	_, registeredResolutionErr := sourceBackedPackFromInstalledRowWithComponentKindRegistrationSet(
		context.Background(), nil, fixture.row, registeredSet,
	)
	if registeredResolutionErr == nil {
		t.Fatal("registered source resolution unexpectedly succeeded")
	}
	pack, err = sourceBackedPackFromInstalledRowForReconcileWithFormulaBundlesAndComponentKindRegistrationSet(
		context.Background(), nil, fixture.row, noBundles(), registeredSet,
	)
	if pack != nil || err == nil || err.Error() != registeredResolutionErr.Error() {
		t.Fatalf("registered no-bundle result = (%v, %v), want original source error", pack, err)
	}
}

func TestConvergenceRunnerOwnsEveryInstalledSourceRead(t *testing.T) {
	adopt := parseControlplaneFuncDecl(t, "source_pack_ops.go", "adoptLegacyInstalledPackSourceIdentities")
	assertConvergenceCallArg(t, adopt, "installPackFromSourceUnlockedWithPreflightOverridesAndComponentKindRegistrationSet", 5, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})

	reconcile := parseControlplaneFuncDecl(t, "source_pack_ops.go", "reconcileActiveInstalledPackSourceState")
	activeAuthority := findFuncCallByName(reconcile, "preflightPackGenerationForReconcile")
	retryAuthority := findFuncCallByName(reconcile, "preflightRetryableInstalledPackSourceStateForReconcile")
	coverage := findFuncCallByName(reconcile, "validateActiveInstalledPackManagedApprovalOwnership")
	if activeAuthority == nil || retryAuthority == nil || coverage == nil {
		t.Fatalf(
			"source convergence admission is incomplete: active_authority=%v retry_authority=%v managed_coverage=%v",
			activeAuthority != nil,
			retryAuthority != nil,
			coverage != nil,
		)
	}
	for _, mutation := range []string{"applyResolvedSourceBackedPackUnlockedForReconcile", "repairActiveInstalledPackSourceHashDrift", "backfillChildCloneRulePackOwnershipForReconcile", "backfillExclusiveSiblingFieldPackOwnershipForReconcile"} {
		call := findFuncCallByName(reconcile, mutation)
		if call == nil ||
			activeAuthority.Pos() > call.Pos() ||
			retryAuthority.Pos() > call.Pos() {
			t.Fatalf("all source-backed lifecycle authority must be admitted before %s", mutation)
		}
		if coverage.Pos() > call.Pos() {
			t.Fatalf("managed approval coverage guard must precede %s", mutation)
		}
	}
	assertConvergenceCallArg(t, reconcile, "rebuildRepairPackWithAllowedOwnerlessObjectsForReconcileWithComponentKindRegistrationSet", 6, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, reconcile, "backfillChildCloneRulePackOwnershipForReconcile", 2, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, reconcile, "backfillExclusiveSiblingFieldPackOwnershipForReconcile", 2, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	fastPath := parseControlplaneFuncDecl(t, "source_pack_ops.go", "tryConvergeVerifiedInstalledPackSourceStateWithoutReapply")
	managedCoverage := findFuncCallByName(fastPath, "HasManagedOwnerPackTable")
	liveVerify := findFuncCallByName(fastPath, "verifyResolvedSourceBackedPackForReconcile")
	if managedCoverage == nil || liveVerify == nil || managedCoverage.Pos() > liveVerify.Pos() {
		t.Fatal("bookkeeping-only convergence must reject managed approval coverage before live component verify")
	}

	detect := parseControlplaneFuncDecl(t, "source_pack_ops.go", "detectActiveInstalledPackSourceStateDrift")
	assertConvergenceCallArg(t, detect, "detectActiveInstalledPackSourceStateDriftWithComponentKindRegistrationSet", 3, isRunnerComponentRegistrationSetCall)

	repair := parseControlplaneFuncDecl(t, "source_pack_ops.go", "repairActiveInstalledPackSourceHashDrift")
	assertConvergenceCallArg(t, repair, "detectActiveInstalledPackSourceStateDriftWithComponentKindRegistrationSet", 3, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, repair, "rebuildRepairPackWithAllowedOwnerlessObjectsForReconcileWithComponentKindRegistrationSet", 6, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})

	verify := parseControlplaneFuncDecl(t, "source_pack_ops.go", "verifyActiveInstalledPackSourceState")
	assertConvergenceCallArg(t, verify, "sourceBackedPackFromInstalledRowForReconcileWithFormulaBundlesAndComponentKindRegistrationSet", 4, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, verify, "rebuildRepairPackWithAllowedOwnerlessObjectsForReconcileWithComponentKindRegistrationSet", 6, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})

	resolve := parseControlplaneFuncDecl(t, "source_pack_ops.go", "sourceBackedPackFromInstalledRowForReconcileWithFormulaBundlesAndComponentKindRegistrationSet")
	assertConvergenceCallArg(t, resolve, "sourceBackedPackFromInstalledRowWithComponentKindRegistrationSet", 3, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, resolve, "sourceBackedPackFromInstalledRowWithFormulaBundlesAndComponentKindRegistrationSet", 5, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})

	rebuild := parseControlplaneFuncDecl(t, "source_pack_ops.go", "rebuildRepairPackWithAllowedOwnerlessObjectsAndComponentKindRegistrationSet")
	assertConvergenceCallArg(t, rebuild, "samePackAllowedOwnerlessObjectsWithOptionsAndComponentKindRegistrationSet", 5, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, rebuild, "sourceBackedPackFromInstalledRowWithComponentKindRegistrationSet", 3, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
	assertConvergenceCallArg(t, rebuild, "sourceBackedPackFromInstalledRowWithFormulaBundlesAndComponentKindRegistrationSet", 5, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})

	compat := parseControlplaneFuncDecl(t, "source_pack_ops.go", "rebuildRepairPackWithAllowedOwnerlessObjectsForReconcileWithComponentKindRegistrationSet")
	if findFuncCallByName(compat, "rebuildRepairPackWithAllowedOwnerlessObjectsForReconcile") == nil {
		t.Fatal("empty registration set must retain the legacy ownerless-rebuild seam")
	}
	assertConvergenceCallArg(t, compat, "rebuildRepairPackWithAllowedOwnerlessObjectsAndComponentKindRegistrationSet", 6, func(expr ast.Expr) bool {
		return isIdentifier(expr, "registrations")
	})
}

func assertConvergenceCallArg(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 runner-owned registration set to %s argument %d", fn.Name.Name, callName, argIndex)
	}
}
