package controlplane

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

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/db"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

func TestInstallPackFromSourceUsesRunnerRegistrations(t *testing.T) {
	rawSource, options := runnerManagedVerifyFixture(t)
	rawDB, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = rawDB.Close() })
	mock.ExpectQuery(regexp.QuoteMeta("SELECT GET_LOCK('nexuscrm_controlplane_apply', 600)")).
		WillReturnRows(sqlmock.NewRows([]string{"GET_LOCK"}).AddRow(1))
	expectApplyAllMarkerWrite(mock, "invalidated", "stale")
	mock.ExpectQuery(regexp.QuoteMeta("SELECT RELEASE_LOCK('nexuscrm_controlplane_apply')")).
		WillReturnRows(sqlmock.NewRows([]string{"RELEASE_LOCK"}).AddRow(1))

	originalEnsure := ensureInstallPackSchemaReadinessForInstall
	originalDependencies := validateLivePackDependenciesForInstall
	originalOwnerless := samePackAllowedOwnerlessObjectsForReapply
	originalApply := applyResolvedSourceBackedPackForInstall
	t.Cleanup(func() {
		ensureInstallPackSchemaReadinessForInstall = originalEnsure
		validateLivePackDependenciesForInstall = originalDependencies
		samePackAllowedOwnerlessObjectsForReapply = originalOwnerless
		applyResolvedSourceBackedPackForInstall = originalApply
	})
	ensureInstallPackSchemaReadinessForInstall = func(context.Context, *Runner) error { return nil }
	validateLivePackDependenciesForInstall = func(context.Context, *sql.DB, *packs.SourceBackedPack) error { return nil }
	registrationsReachedCandidate := false
	samePackAllowedOwnerlessObjectsForReapply = func(_ context.Context, _ *sql.DB, _ *packs.SourceBackedPack, _ string, _ InstallPackOptions, registrations packs.ComponentKindRegistrationSet) ([]string, error) {
		registrationsReachedCandidate = len(registrations.Registrations()) == 1
		return nil, nil
	}
	applyCalls := 0
	applyResolvedSourceBackedPackForInstall = func(_ context.Context, _ *Runner, pack *packs.SourceBackedPack, _ *installPackPreflightOverrides, _ InstallPackOptions) error {
		if pack == nil || pack.Name() != "runner-managed-test" {
			t.Fatalf("apply pack = %#v", pack)
		}
		applyCalls++
		return nil
	}

	runner := &Runner{
		DB: database.NewConnectionFromDB(rawDB),
		SvcMgr: &runnerServiceGraph{
			ServiceGraph: newTestServiceGraph(testServiceGraphConfig{}),
			options:      options,
		},
	}
	if err := InstallPackFromSource(context.Background(), runner, rawSource); err != nil {
		t.Fatalf("registered fresh install: %v", err)
	}
	if !registrationsReachedCandidate {
		t.Fatal("runner registrations did not reach locked candidate construction")
	}
	if applyCalls != 1 {
		t.Fatalf("apply calls = %d, want 1", applyCalls)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func TestInstallPackFromSourceMissingRegistrationFailsBeforeLockAndApply(t *testing.T) {
	rawSource, _ := runnerManagedVerifyFixture(t)
	rawDB, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = rawDB.Close() })

	originalEnsure := ensureInstallPackSchemaReadinessForInstall
	originalApply := applyResolvedSourceBackedPackForInstall
	t.Cleanup(func() {
		ensureInstallPackSchemaReadinessForInstall = originalEnsure
		applyResolvedSourceBackedPackForInstall = originalApply
	})
	lockedCalls := 0
	ensureInstallPackSchemaReadinessForInstall = func(context.Context, *Runner) error {
		lockedCalls++
		return nil
	}
	applyCalls := 0
	applyResolvedSourceBackedPackForInstall = func(context.Context, *Runner, *packs.SourceBackedPack, *installPackPreflightOverrides, InstallPackOptions) error {
		applyCalls++
		return nil
	}

	runner := &Runner{DB: database.NewConnectionFromDB(rawDB)}
	err = InstallPackFromSource(context.Background(), runner, rawSource)
	if err == nil || !strings.Contains(err.Error(), "runner_managed_test") {
		t.Fatalf("missing registration error = %v", err)
	}
	if lockedCalls != 0 || applyCalls != 0 {
		t.Fatalf("locked/apply calls = %d/%d, want 0/0", lockedCalls, applyCalls)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func TestFreshInstallRuntimeMetadataCarriersUseRunnerRegistrationSet(t *testing.T) {
	fn := parseControlplaneFuncDecl(t, "source_pack_ops.go", "applyResolvedSourceBackedPackUnlockedWithPreflightOverrides")
	for _, carrier := range []string{
		"RegisterRuntimeMetadataForInstallWithOverridesAndComponentKindRegistrationSet",
		"RegisterRuntimeMetadataForInstallingPacksWithOverridesAndComponentKindRegistrationSet",
	} {
		call := findFuncCallByName(fn, carrier)
		if call == nil {
			t.Fatalf("fresh install must call %s", carrier)
		}
		if len(call.Args) != 5 || !isRunnerComponentRegistrationSetCall(call.Args[4]) {
			t.Fatalf("%s must receive r.componentKindRegistrationSet()", carrier)
		}
	}
}

func TestInstallPackManagedApprovalGuardPrecedesAttributionOverride(t *testing.T) {
	fn := parseControlplaneFuncDecl(t, "source_pack_ops.go", "validateSourceBackedPackInstallPreflights")
	guard := findFuncCallByName(fn, "validatePackManagedApprovalOwnership")
	if guard == nil {
		t.Fatal("install path is missing the managed approval ownership guard")
	}
	var skipOverride ast.Node
	ast.Inspect(fn.Body, func(node ast.Node) bool {
		selector, ok := node.(*ast.SelectorExpr)
		if ok && selector.Sel.Name == "skipAttributionPreflight" {
			skipOverride = selector
			return false
		}
		return true
	})
	if skipOverride == nil {
		t.Fatal("install path is missing the attribution override branch")
	}
	if guard.Pos() > skipOverride.Pos() {
		t.Fatal("managed approval ownership guard must run before the optional attribution override")
	}
}

func TestInstallPackPreflightHelperOwnsCompleteGuardOrderBeforeMutation(t *testing.T) {
	preflight := parseControlplaneFuncDecl(t, "source_pack_ops.go", "validateSourceBackedPackInstallPreflights")
	lastPosition := preflight.Pos()
	for _, name := range []string{
		"validatePackManagedApprovalOwnership",
		"validateInstallPackAttributionPreflightWithComponentKindRegistrationSet",
		"validateInstallPackChildRelationshipNamespacePreflight",
		"assertConnectorCapabilitiesHaveHandlers",
		"validateDefaultChildRowsInstallPreflightWithSnapshots",
		"validateChildCloneRulesInstallPreflightWithSnapshot",
		"ValidateSourceBackedPermissionOwnerPreflight",
	} {
		call := findFuncCallByName(preflight, name)
		if call == nil {
			t.Fatalf("shared install preflight is missing %s", name)
		}
		if call.Pos() <= lastPosition {
			t.Fatalf("shared install preflight call %s is out of legacy order", name)
		}
		lastPosition = call.Pos()
	}
	install := parseControlplaneFuncDecl(t, "source_pack_ops.go", "applyResolvedSourceBackedPackUnlockedWithPreflightOverrides")
	shared := findFuncCallByName(install, "validateSourceBackedPackInstallPreflights")
	registration := findFuncCallByName(install, "RegisterRuntimeMetadataForInstallWithOverridesAndComponentKindRegistrationSet")
	if shared == nil || registration == nil || shared.Pos() >= registration.Pos() {
		t.Fatal("shared install preflight must complete before runtime registration and installing-row mutation")
	}
}

func TestPrepareSourceRevisionAdvanceAttemptUsesNeutralErrorBoundary(t *testing.T) {
	prepare := parseControlplaneFuncDecl(
		t, "apply_all_source_revision_advance_begin.go",
		"prepareApplyAllSourceRevisionAdvanceAttempt",
	)
	if findFuncCallByName(prepare, "applyAllSourceRevisionAdvanceBeginError") != nil {
		t.Fatal("prepare path mislabeled failure as a Begin transition")
	}
	if findFuncCallByName(prepare, "applyAllSourceRevisionAdvanceAttemptError") == nil {
		t.Fatal("prepare path is missing its neutral attempt error boundary")
	}
}
