package packs

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestSourceBackedBuildOrchestrationStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	orchestrationSrc := readPacksSourceForStructureTest(t, "source_backed_component_build_orchestration.go")
	buildersSrc := readPacksSourceForStructureTest(t, "source_backed_component_builders.go")
	objectSpecBuildStateSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_build_state.go")
	layoutSearchPrecollectSrc := readPacksSourceForStructureTest(t, "source_backed_layout_search_precollect.go")

	for _, reject := range []string{
		"func (p *SourceBackedPack) buildComponents() error",
		"func (p *SourceBackedPack) appendSourceBackedSweepComponents(",
	} {
		if strings.Contains(rootSrc, reject) {
			t.Fatalf("source_backed_pack.go still owns build-orchestration fragment %q", reject)
		}
		if !strings.Contains(orchestrationSrc, reject) {
			t.Fatalf("source_backed_component_build_orchestration.go is missing fragment %q", reject)
		}
	}
	for _, symbol := range []string{
		"type sourceBackedObjectSpecBuildState struct",
		"func (p *SourceBackedPack) collectSourceBackedObjectSpecBuildState(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns object-spec build-state fragment %q", symbol)
		}
		if strings.Contains(orchestrationSrc, symbol) {
			t.Fatalf("source_backed_component_build_orchestration.go still owns object-spec build-state fragment %q", symbol)
		}
		if !strings.Contains(objectSpecBuildStateSrc, symbol) {
			t.Fatalf("source_backed_object_spec_build_state.go is missing fragment %q", symbol)
		}
	}
	for _, symbol := range []string{
		"func (p *SourceBackedPack) precollectSourceBackedLayoutSearchMetadata(",
		"func (p *SourceBackedPack) precollectSourceBackedRelatedListRules(",
		"func (p *SourceBackedPack) precollectSourceBackedSearchAllDefaultFields(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns layout/search precollect fragment %q", symbol)
		}
		if strings.Contains(orchestrationSrc, symbol) {
			t.Fatalf("source_backed_component_build_orchestration.go still owns layout/search precollect fragment %q", symbol)
		}
		if !strings.Contains(layoutSearchPrecollectSrc, symbol) {
			t.Fatalf("source_backed_layout_search_precollect.go is missing fragment %q", symbol)
		}
	}
	if !strings.Contains(buildersSrc, "func attachSourceBackedComponentBuildHandlers(") {
		t.Fatal("source_backed_component_builders.go must attach ComponentKind build handlers")
	}
	if strings.Contains(orchestrationSrc, "attachSourceBackedComponentBuildHandlers") {
		t.Fatal("source_backed_component_build_orchestration.go must not own the ComponentKind handler registry")
	}
}

func TestSourceBackedListFieldsHelpersStayOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	listFieldsSrc := readPacksSourceForStructureTest(t, "source_backed_list_fields.go")

	for _, symbol := range []string{
		"func (p *SourceBackedPack) replayListFieldsComponentsForObjects(",
		"func (p *SourceBackedPack) appendObjectSpecListFieldsStartupShallowDeclarations(",
		"func lockedObjectSpecListFieldsObjects(",
		"func sourceBackedStandaloneListFieldsObjects(",
		"func sourceBackedListFieldsComponentTouchesSet(",
		"func objectSpecsForListFieldsVerify(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns source-backed list-fields symbol %q", symbol)
		}
		if !strings.Contains(listFieldsSrc, symbol) {
			t.Fatalf("source_backed_list_fields.go is missing source-backed list-fields symbol %q", symbol)
		}
	}
}

func TestSourceBackedManagedSystemFieldRepairStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	repairSrc := readPacksSourceForStructureTest(t, "source_backed_managed_system_field_repair.go")
	objectSpecComponentSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_component.go")

	for _, want := range []string{
		"func repairManagedSystemFieldColumnsFromLiveMetadata(",
		"func queryManagedSystemFieldsFromLiveMetadata(",
		"func isManagedSystemFieldSpec(",
		"func packFieldIntrinsicBounds(",
	} {
		if strings.Contains(rootSrc, want) {
			t.Fatalf("source_backed_pack.go still owns managed-system-field repair fragment %q", want)
		}
		if !strings.Contains(repairSrc, want) {
			t.Fatalf("source_backed_managed_system_field_repair.go is missing fragment %q", want)
		}
	}
	if strings.Contains(rootSrc, "repairManagedSystemFieldColumnsFromLiveMetadata(ctx, deps.DB, c.packName, c.specs)") {
		t.Fatal("source_backed_pack.go should not own the object-spec repairSubstrate handoff")
	}
	if !strings.Contains(objectSpecComponentSrc, "repairManagedSystemFieldColumnsFromLiveMetadata(ctx, deps.DB, c.packName, c.specs)") {
		t.Fatal("source_backed_object_spec_component.go should own the object-spec repairSubstrate handoff")
	}
}

func TestSourceBackedStaleFieldGuardStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	staleFieldSrc := readPacksSourceForStructureTest(t, "source_backed_stale_field_guard.go")
	objectSpecComponentSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_component.go")

	const symbol = "func detectStalePackOwnedFields("
	if strings.Contains(rootSrc, symbol) {
		t.Fatalf("source_backed_pack.go still owns stale-field guard %q", symbol)
	}
	if !strings.Contains(staleFieldSrc, symbol) {
		t.Fatalf("source_backed_stale_field_guard.go is missing stale-field guard %q", symbol)
	}
	if strings.Contains(rootSrc, "detectStalePackOwnedFields(deps.DB, c.packName, defs)") {
		t.Fatal("source_backed_pack.go should not own the stale-field guard handoff")
	}
	if !strings.Contains(objectSpecComponentSrc, "detectStalePackOwnedFields(deps.DB, c.packName, defs)") {
		t.Fatal("source_backed_object_spec_component.go should own the stale-field guard handoff")
	}
}

func TestSourceBackedInstalledResolverStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	resolverSrc := readPacksSourceForStructureTest(t, "source_backed_installed_resolver.go")

	for _, symbol := range []string{
		"type InstalledPackResolverInput struct",
		"type InstalledPackResolverOptions struct",
		"func ResolveInstalledSourceBackedPack(",
		"func ResolveInstalledSourceBackedPackForInstalledRow(",
		"func ResolveInstalledPack(",
		"func resolveInstalledSourceBackedPackWithStoredIdentityAndOptionsAndFormulaBundles(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns installed resolver fragment %q", symbol)
		}
		if !strings.Contains(resolverSrc, symbol) {
			t.Fatalf("source_backed_installed_resolver.go is missing installed resolver fragment %q", symbol)
		}
	}
}

func TestSourceBackedConnectorPushValidationStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	validationSrc := readPacksSourceForStructureTest(t, "source_backed_connector_push_validation.go")

	for _, symbol := range []string{
		"func connectorPushValidationOptionsFromObjectSpecs(",
		"func appendConnectorPushValidationFieldMetadata(",
		"func appendConnectorPushValidationObjectMetadata(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns connector push validation fragment %q", symbol)
		}
		if !strings.Contains(validationSrc, symbol) {
			t.Fatalf("source_backed_connector_push_validation.go is missing fragment %q", symbol)
		}
	}
}

func TestSourceBackedObjectSpecFormulaHelpersStayOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	formulaSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_formula.go")

	for _, symbol := range []string{
		"type packWriteMaterializedFormulaRef struct",
		"func writeMaterializedFormulaRefsFromObjectSpecComponent(",
		"func frozenFormulaRefsFromObjectSpecComponent(",
		"func writeMaterializedFormulaRefsFromObjectSpecs(",
		"func frozenFormulaRefsFromObjectSpecs(",
		"func formulaExpectedType(",
		"func formulaSchemasFromObjectSpecs(",
		"func frozenFormulaSchemasFromObjectSpecs(",
		"func verifyWriteMaterializedFormulaPayloadForObjectSpecComponent(",
		"func verifyFrozenFormulaPayloadForObjectSpecComponent(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns object-spec formula fragment %q", symbol)
		}
		if !strings.Contains(formulaSrc, symbol) {
			t.Fatalf("source_backed_object_spec_formula.go is missing object-spec formula fragment %q", symbol)
		}
	}
}

func TestSourceBackedFormulaValidationStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	formulaValidationSrc := readPacksSourceForStructureTest(t, "source_backed_formula_validation.go")

	for _, symbol := range []string{
		"func (p *SourceBackedPack) runtimeFormulaFunctionBundlesForValidation(",
		"func formulaFunctionBundlesExcludingSourceFunctions(",
		"func VerifySourceBackedPackWriteMaterializedFormulaPayload(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns formula validation fragment %q", symbol)
		}
		if !strings.Contains(formulaValidationSrc, symbol) {
			t.Fatalf("source_backed_formula_validation.go is missing formula validation fragment %q", symbol)
		}
	}
}

func TestSourceBackedObjectSpecOverlayHelpersStayOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	overlaySrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_overlay.go")

	for _, symbol := range []string{
		"func overlaySourceBackedChildCloneRules(",
		"func sourceBackedObjectSpecRuntimePayload(",
		"func validateSourceBackedObjectSpecAccessPolicySidecarSurface(",
		"func objectSpecAPINames(",
		"func uniqueSortedNonEmpty(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns object-spec overlay symbol %q", symbol)
		}
		if !strings.Contains(overlaySrc, symbol) {
			t.Fatalf("source_backed_object_spec_overlay.go is missing object-spec overlay symbol %q", symbol)
		}
	}
}

func TestSourceBackedObjectSpecComponentStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	componentSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_component.go")

	for _, symbol := range []string{
		"type sourceBackedObjectSpecComponent struct",
		"type sourceBackedObjectSpecTarget struct",
		"func (c *sourceBackedObjectSpecComponent) Apply(",
		"func (c *sourceBackedObjectSpecComponent) repairSubstrate(",
		"func sourceBackedObjectSpecsTouchSet(",
		"func sourceBackedApplySchemaFallback(",
		"func specdriftPolicyForManageability(",
		"func verifyTableTypesForObjectSpecs(",
		"func (c *sourceBackedObjectSpecComponent) VerifyComponent(",
		"func sourceBackedVerifySchemaFallback(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns object-spec component symbol %q", symbol)
		}
		if !strings.Contains(componentSrc, symbol) {
			t.Fatalf("source_backed_object_spec_component.go is missing object-spec component symbol %q", symbol)
		}
	}
}

func TestSourceBackedValidationRuleComponentStaysOutOfMetadataComponentsFile(t *testing.T) {
	metadataSrc := readPacksSourceForStructureTest(t, "source_backed_metadata_components.go")
	componentSrc := readPacksSourceForStructureTest(t, "source_backed_validation_rule_component.go")

	for _, symbol := range []string{
		"func newSourceBackedValidationRuleComponent(",
		"type sourceBackedValidationRuleRow struct",
		"func sourceBackedValidationRuleProjection(",
		"func liveSourceBackedValidationRuleProjection(",
	} {
		if strings.Contains(metadataSrc, symbol) {
			t.Fatalf("source_backed_metadata_components.go still owns validation-rule component symbol %q", symbol)
		}
		if !strings.Contains(componentSrc, symbol) {
			t.Fatalf("source_backed_validation_rule_component.go is missing validation-rule component symbol %q", symbol)
		}
	}
}

func TestSourceBackedObjectSpecPreflightStaysOutOfPackRootFile(t *testing.T) {
	rootSrc := readPacksSourceForStructureTest(t, "source_backed_pack.go")
	preflightSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_preflight.go")
	componentSrc := readPacksSourceForStructureTest(t, "source_backed_object_spec_component.go")

	for _, symbol := range []string{
		"func guardSourceBackedObjectSpecs(",
		"func adoptLegacyObjectSpecFieldOwnership(",
		"func verifyOwnerlessObjectSpecPartialCompatibility(",
	} {
		if strings.Contains(rootSrc, symbol) {
			t.Fatalf("source_backed_pack.go still owns object-spec preflight symbol %q", symbol)
		}
		if !strings.Contains(preflightSrc, symbol) {
			t.Fatalf("source_backed_object_spec_preflight.go is missing object-spec preflight symbol %q", symbol)
		}
	}
	if strings.Contains(rootSrc, "guardSourceBackedObjectSpecs(ctx, deps.DB, c.packName, defs, c.allowOwnerlessObjectAPI") {
		t.Fatal("source_backed_pack.go should not own the object-spec preflight handoff")
	}
	if !strings.Contains(componentSrc, "guardSourceBackedObjectSpecsWithComponentKindRegistrationSet(ctx, deps.DB, c.packName, defs, c.allowOwnerlessObjectAPI, c.registrations, newObjectSpecRequiredFieldIndex(c.specs))") {
		t.Fatal("source_backed_object_spec_component.go should own the object-spec preflight handoff")
	}
	if !strings.Contains(componentSrc, "verifyPackObjectPhysicalSchemaWithContextAndAuthoredFieldsAndComponentKindRegistrationSet(ctx, deps.DB, c.packName, defs, c.manifestComponent.Manageability, newObjectSpecAuthoredFieldSet(c.specs), c.registrations, newObjectSpecRequiredFieldIndex(c.specs))") {
		t.Fatal("source_backed_object_spec_component.go should carry registrations into physical-schema verification")
	}
}

func readPacksSourceForStructureTest(t *testing.T, filename string) string {
	t.Helper()

	raw, err := os.ReadFile(filepath.Join(packsDir(t), filename))
	if err != nil {
		t.Fatalf("read %s: %v", filename, err)
	}
	return string(raw)
}
