package packs

import (
	"go/ast"
	"go/parser"
	"go/token"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"testing"
	"unicode"

	featureauth "github.com/nexuscrm/auth"
	"github.com/nexuscrm/shared/pkg/constants"
)

const nexusCRMRepoRootEnvVar = "NEXUSCRM_REPO_ROOT"

func TestPR3NoSharedSalesforceImports(t *testing.T) {
	requireNexusCRMRepoRootForGuardrail(t)
	var violations []string
	for _, path := range productionGoFiles(t) {
		fset := token.NewFileSet()
		file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
		if err != nil {
			t.Fatalf("parse imports %s: %v", path, err)
		}
		for _, imp := range file.Imports {
			importPath, err := strconv.Unquote(imp.Path.Value)
			if err != nil {
				t.Fatalf("unquote import %s in %s: %v", imp.Path.Value, path, err)
			}
			if importPath == "github.com/nexuscrm/shared/pkg/salesforce" {
				violations = append(violations, relRepoPath(t, path))
			}
		}
	}
	if len(violations) > 0 {
		t.Fatalf("production files import deleted shared/pkg/salesforce package:\n  - %s", strings.Join(violations, "\n  - "))
	}
}

func TestPR3NoDeletedCompatibilityAPIs(t *testing.T) {
	requireNexusCRMRepoRootForGuardrail(t)
	deletedAPIs := map[string]struct{}{
		"LookupStandardRelationTarget":   {},
		"StandardRelationshipForeignKey": {},
		"StandardObjectForForeignKey":    {},
		"StandardObjectForKeyPrefix":     {},
		"StandardKeyPrefixForObject":     {},
		"StandardKeyPrefixToObjectMap":   {},
		"StandardObjectToKeyPrefixMap":   {},
	}

	var violations []string
	for _, path := range productionGoFiles(t) {
		fset := token.NewFileSet()
		file, err := parser.ParseFile(fset, path, nil, 0)
		if err != nil {
			t.Fatalf("parse %s: %v", path, err)
		}
		ast.Inspect(file, func(n ast.Node) bool {
			ident, ok := n.(*ast.Ident)
			if !ok {
				return true
			}
			if _, deleted := deletedAPIs[ident.Name]; !deleted {
				return true
			}
			pos := fset.Position(ident.Pos())
			violations = append(violations, relRepoPath(t, pos.Filename)+":"+strconv.Itoa(pos.Line)+" uses "+ident.Name)
			return true
		})
	}
	if len(violations) > 0 {
		t.Fatalf("production files reference deleted compatibility APIs:\n  - %s", strings.Join(violations, "\n  - "))
	}
}

func TestGeneratedSystemArtifactsExcludeAppPackObjects(t *testing.T) {
	repoRoot := requireNexusCRMRepoRootForGuardrail(t)
	appObjectNames := appPackObjectNamesForL0GeneratedGuardrail(t)

	for _, def := range systemTableDefinitions() {
		if _, appObject := appObjectNames[def.TableName]; appObject {
			t.Fatalf("system_tables.json still declares app-pack object table %s", def.TableName)
		}
	}

	allSystemTables := make(map[string]struct{}, len(constants.AllSystemTableNames))
	for _, name := range constants.AllSystemTableNames {
		allSystemTables[name] = struct{}{}
	}
	for appObject := range appObjectNames {
		if _, ok := allSystemTables[appObject]; ok {
			t.Fatalf("AllSystemTableNames includes app-pack object table %s", appObject)
		}
	}

	constantsFile := filepath.Join(repoRoot, "shared", "pkg", "constants", "z_generated_tables.go")
	fset := token.NewFileSet()
	parsedConstants, err := parser.ParseFile(fset, constantsFile, nil, 0)
	if err != nil {
		t.Fatalf("parse generated table constants: %v", err)
	}
	for _, decl := range parsedConstants.Decls {
		gen, ok := decl.(*ast.GenDecl)
		if !ok || gen.Tok != token.CONST {
			continue
		}
		for _, spec := range gen.Specs {
			valueSpec, ok := spec.(*ast.ValueSpec)
			if !ok {
				continue
			}
			for _, name := range valueSpec.Names {
				for appObject := range appObjectNames {
					if name.Name == "Table"+appObject {
						t.Fatalf("generated table constants still include %s", name.Name)
					}
				}
			}
		}
	}

	modelsFile := filepath.Join(repoRoot, "shared", "pkg", "models", "z_generated.go")
	parsedModels, err := parser.ParseFile(fset, modelsFile, nil, 0)
	if err != nil {
		t.Fatalf("parse generated models: %v", err)
	}
	for _, decl := range parsedModels.Decls {
		gen, ok := decl.(*ast.GenDecl)
		if !ok || gen.Tok != token.TYPE {
			continue
		}
		for _, spec := range gen.Specs {
			typeSpec, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}
			if _, isStruct := typeSpec.Type.(*ast.StructType); !isStruct {
				continue
			}
			if _, appObject := appObjectNames[typeSpec.Name.Name]; appObject {
				t.Fatalf("generated models still include app-pack object struct %s", typeSpec.Name.Name)
			}
		}
	}

	frontendGeneratedSchemaFile := filepath.Join(repoRoot, "frontend", "src", "generated", "schema.ts")
	frontendGeneratedSchema, err := os.ReadFile(frontendGeneratedSchemaFile)
	if err != nil {
		t.Fatalf("read frontend generated schema: %v", err)
	}
	frontendSchemaText := string(frontendGeneratedSchema)
	for appObject := range appObjectNames {
		if strings.Contains(frontendSchemaText, "FIELDS_"+strings.ToUpper(appObject)) {
			t.Fatalf("frontend generated/schema.ts still includes app-pack object fields for %s", appObject)
		}
		if strings.Contains(frontendSchemaText, "interface "+appObject+" ") {
			t.Fatalf("frontend generated/schema.ts still includes app-pack object interface %s", appObject)
		}
	}
	assertGeneratedCommentsExcludeAppPackObjects(t, frontendGeneratedSchemaFile, frontendSchemaText, appObjectNames)
	modelsRaw, err := os.ReadFile(modelsFile)
	if err != nil {
		t.Fatalf("read generated models: %v", err)
	}
	assertGeneratedCommentsExcludeAppPackObjects(t, modelsFile, string(modelsRaw), appObjectNames)
}

func TestAppPackObjectGuardrailDerivesFutureBundledPackPayloads(t *testing.T) {
	root := t.TempDir()
	objectsDir := filepath.Join(root, "jira_payload", "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir future app payload: %v", err)
	}
	payloadPath := filepath.Join(objectsDir, "standard_objects_spec.json")
	if err := os.WriteFile(payloadPath, []byte(`[
  {"apiName":"Issue","category":"jira"},
  {"apiName":"Project","category":"jira"},
  {"apiName":"Sprint","category":"jira"}
]`), 0o644); err != nil {
		t.Fatalf("write future app payload: %v", err)
	}

	names := appPackObjectNamesFromPayloadRoot(t, root)
	for _, name := range []string{"Issue", "Project", "Sprint"} {
		if _, ok := names[name]; !ok {
			t.Fatalf("future app object %s was not derived from payload", name)
		}
	}
}

func TestPR3FormerCompatFactsResolveFromDeclarations(t *testing.T) {
	platformDecls, err := loadPlatformCompatibilityDeclarations()
	if err != nil {
		t.Fatalf("load platform compatibility declarations: %v", err)
	}
	crmPayload, err := os.ReadFile(filepath.Join(officialPackRootFromThisFile(t), "salesforce-crm-standard", "objects", "standard_objects_spec.json"))
	if err != nil {
		t.Fatalf("load CRM pack object spec: %v", err)
	}
	crmDecls, err := compatibilityDeclarationsFromObjectSpecPayloads([][]byte{crmPayload}, "official_repo:salesforce-crm-standard/objects/standard_objects_spec.json")
	if err != nil {
		t.Fatalf("derive CRM compatibility declarations: %v", err)
	}
	decls, err := mergeCompatibilityDeclarationSets(platformDecls, crmDecls)
	if err != nil {
		t.Fatalf("merge compatibility declarations: %v", err)
	}

	for _, tc := range []struct {
		prefix string
		object string
	}{
		{prefix: "001", object: "Account"},
		{prefix: "00a", object: "CaseComment"},
		{prefix: "00k", object: "OpportunityLineItem"},
		{prefix: "00K", object: "OpportunityContactRole"},
		{prefix: "00v", object: "CampaignMember"},
		{prefix: "003", object: "Contact"},
		{prefix: "005", object: "User"},
		{prefix: "006", object: "Opportunity"},
		{prefix: "00Q", object: "Lead"},
		{prefix: "00T", object: "Task"},
		{prefix: "01s", object: "Pricebook2"},
		{prefix: "01t", object: "Product2"},
		{prefix: "01u", object: "PricebookEntry"},
		{prefix: "01Y", object: "CampaignMemberStatus"},
		{prefix: "0QL", object: "QuoteLineItem"},
		{prefix: "300", object: "FlowDefinition"},
		{prefix: "301", object: "Flow"},
		{prefix: "500", object: "Case"},
		{prefix: "701", object: "Campaign"},
		{prefix: "800", object: "Contract"},
		{prefix: "0Q0", object: "Quote"},
		{prefix: "801", object: "Order"},
		{prefix: "802", object: "OrderItem"},
	} {
		got, ok := objectForKeyPrefixFromDeclarations(decls, tc.prefix)
		if !ok || got != tc.object {
			t.Fatalf("ObjectForKeyPrefix(%s) = (%q, %v), want (%q, true)", tc.prefix, got, ok, tc.object)
		}
	}

	for _, tc := range []struct {
		object          string
		relationship    string
		foreignKey      string
		expectedTargets []string
	}{
		{object: "User", relationship: "Profile", foreignKey: "ProfileId", expectedTargets: []string{"Profile"}},
		{object: "User", relationship: "Manager", foreignKey: "ManagerId", expectedTargets: []string{"User"}},
		{object: "Task", relationship: "Who", foreignKey: "WhoId", expectedTargets: []string{"Contact", "Lead"}},
		{object: "Task", relationship: "What", foreignKey: "WhatId", expectedTargets: []string{"Account", "Asset", "Campaign", "Case", "Contract", "Opportunity", "Order", "Product2", "Quote"}},
		{object: "Task", relationship: "RecordType", foreignKey: "RecordTypeId", expectedTargets: []string{"RecordType"}},
		{object: "Task", relationship: "Owner", foreignKey: "OwnerId", expectedTargets: []string{"User"}},
		{object: "Task", relationship: "CreatedBy", foreignKey: "CreatedById", expectedTargets: []string{"User"}},
		{object: "Task", relationship: "LastModifiedBy", foreignKey: "LastModifiedById", expectedTargets: []string{"User"}},
		{object: "Contact", relationship: "Account", foreignKey: "AccountId", expectedTargets: []string{"Account"}},
		{object: "Opportunity", relationship: "Campaign", foreignKey: "CampaignId", expectedTargets: []string{"Campaign"}},
		{object: "Order", relationship: "Contract", foreignKey: "ContractId", expectedTargets: []string{"Contract"}},
		{object: "Order", relationship: "Quote", foreignKey: "QuoteId", expectedTargets: []string{"Quote"}},
		{object: "PricebookEntry", relationship: "Pricebook2", foreignKey: "Pricebook2Id", expectedTargets: []string{"Pricebook2"}},
		{object: "PricebookEntry", relationship: "Product2", foreignKey: "Product2Id", expectedTargets: []string{"Product2"}},
		{object: "OrderItem", relationship: "Product2", foreignKey: "Product2Id", expectedTargets: []string{"Product2"}},
	} {
		gotFK, ok := foreignKeyForRelationshipFromDeclarations(decls, tc.object, tc.relationship)
		if !ok || gotFK != tc.foreignKey {
			t.Fatalf("ForeignKeyForRelationship(%s, %s) = (%q, %v), want (%q, true)", tc.object, tc.relationship, gotFK, ok, tc.foreignKey)
		}
		targets, ok := targetObjectsForForeignKeyFromDeclarations(decls, tc.object, tc.foreignKey)
		if !ok || !equalStringSets(targets, tc.expectedTargets) {
			t.Fatalf("TargetObjectsForForeignKey(%s, %s) = (%v, %v), want (%v, true)", tc.object, tc.foreignKey, targets, ok, tc.expectedTargets)
		}
	}

}

func TestCRMStandardPackIncludesExactKeyPrefixesForFormerMissingObjects(t *testing.T) {
	raw, err := os.ReadFile(filepath.Join(officialPackRootFromThisFile(t), "salesforce-crm-standard", "objects", "standard_objects_spec.json"))
	if err != nil {
		t.Fatalf("read CRM pack object spec: %v", err)
	}
	specs, err := parseObjectSpecsJSON(raw, "official_repo:salesforce-crm-standard/objects/standard_objects_spec.json")
	if err != nil {
		t.Fatalf("parse CRM pack object spec: %v", err)
	}
	byAPIName := make(map[string]string, len(specs))
	for _, spec := range specs {
		byAPIName[spec.APIName] = spec.KeyPrefix
	}
	for _, tc := range []struct {
		object string
		prefix string
	}{
		{object: "OpportunityContactRole", prefix: "00K"},
		{object: "CaseComment", prefix: "00a"},
		{object: "CampaignMemberStatus", prefix: "01Y"},
		{object: "CampaignMember", prefix: "00v"},
		{object: "Pricebook2", prefix: "01s"},
		{object: "OpportunityLineItem", prefix: "00k"},
		{object: "QuoteLineItem", prefix: "0QL"},
		{object: "OrderItem", prefix: "802"},
		{object: "PricebookEntry", prefix: "01u"},
	} {
		if got := byAPIName[tc.object]; got != tc.prefix {
			t.Fatalf("%s keyPrefix = %q, want %q", tc.object, got, tc.prefix)
		}
	}
}

func TestCRMStandardPackIncludesFirstWaveBackfilledStandardFields(t *testing.T) {
	raw, err := os.ReadFile(filepath.Join(officialPackRootFromThisFile(t), "salesforce-crm-standard", "objects", "standard_objects_spec.json"))
	if err != nil {
		t.Fatalf("read CRM pack object spec: %v", err)
	}
	specs, err := parseObjectSpecsJSON(raw, "official_repo:salesforce-crm-standard/objects/standard_objects_spec.json")
	if err != nil {
		t.Fatalf("parse CRM pack object spec: %v", err)
	}

	objects := make(map[string]ObjectSpec, len(specs))
	for _, spec := range specs {
		objects[spec.APIName] = spec
	}

	for _, tc := range []struct {
		object                string
		field                 string
		typ                   string
		createable            *bool
		updateable            *bool
		isNameField           *bool
		returnType            string
		formulaMode           string
		formula               string
		projectionTarget      string
		projectionSelect      string
		projectionMatches     map[string]string
		projectionExcludeGone *bool
		referenceTo           []string
		relationshipName      string
		childRelationshipName string
	}{
		{object: "Account", field: "AccountNumber", typ: "string"},
		{object: "Account", field: "Sic", typ: "string"},
		{object: "Account", field: "TickerSymbol", typ: "string"},
		{object: "Account", field: "Site", typ: "string"},
		{object: "Account", field: "DunsNumber", typ: "string"},
		{object: "Account", field: "Tradestyle", typ: "string"},
		{object: "Account", field: "NaicsDesc", typ: "string"},
		{object: "Account", field: "YearStarted", typ: "string"},
		{object: "Account", field: "SicDesc", typ: "string"},
		{object: "Case", field: "SuppliedName", typ: "string"},
		{object: "Case", field: "SuppliedEmail", typ: "email"},
		{object: "Case", field: "SuppliedPhone", typ: "string"},
		{object: "Case", field: "SuppliedCompany", typ: "string"},
		{object: "Contact", field: "Fax", typ: "phone"},
		{object: "Contact", field: "HomePhone", typ: "phone"},
		{object: "Contact", field: "OtherPhone", typ: "phone"},
		{object: "Lead", field: "Fax", typ: "phone"},
		{object: "Lead", field: "CompanyDunsNumber", typ: "string"},
		{object: "CampaignMember", field: "Title", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.Title, Contact.Title)"},
		{object: "CampaignMember", field: "Street", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.Street, Contact.MailingStreet)"},
		{object: "CampaignMember", field: "City", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.City, Contact.MailingCity)"},
		{object: "CampaignMember", field: "State", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.State, Contact.MailingState)"},
		{object: "CampaignMember", field: "PostalCode", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.PostalCode, Contact.MailingPostalCode)"},
		{object: "CampaignMember", field: "Country", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "string", formula: "IF(ISBLANK(ContactId), Lead.Country, Contact.MailingCountry)"},
		{object: "CampaignMember", field: "Fax", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "phone", formula: "IF(ISBLANK(ContactId), Lead.Fax, Contact.Fax)"},
		{object: "CampaignMember", field: "MobilePhone", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "phone", formula: "IF(ISBLANK(ContactId), Lead.MobilePhone, Contact.MobilePhone)"},
		{object: "CampaignMember", field: "Description", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "textarea", formula: "IF(ISBLANK(ContactId), Lead.Description, Contact.Description)"},
		{object: "CampaignMember", field: "DoNotCall", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "boolean", formula: "IF(ISBLANK(ContactId), Lead.DoNotCall, Contact.DoNotCall)"},
		{object: "CampaignMember", field: "HasOptedOutOfEmail", typ: "formula", isNameField: boolPtr(false), formulaMode: "active", returnType: "boolean", formula: "IF(ISBLANK(ContactId), Lead.HasOptedOutOfEmail, Contact.HasOptedOutOfEmail)"},
		{object: "PricebookEntry", field: "Name", typ: "string", createable: boolPtr(false), updateable: boolPtr(false), isNameField: boolPtr(true), formulaMode: "active", returnType: "string", projectionTarget: "Product2", projectionSelect: "Name", projectionMatches: map[string]string{"Id": "Product2Id"}, projectionExcludeGone: boolPtr(true)},
		{object: "Product2", field: "ExternalId", typ: "string"},
		{object: "Product2", field: "DisplayUrl", typ: "url"},
		{object: "Product2", field: "StockKeepingUnit", typ: "string"},
	} {
		spec, ok := objects[tc.object]
		if !ok {
			t.Fatalf("object %s missing from CRM pack", tc.object)
		}
		field, ok := fieldSpecByAPIName(spec.Fields, tc.field)
		if !ok {
			t.Fatalf("%s.%s missing from CRM pack", tc.object, tc.field)
		}
		if field.Type != tc.typ {
			t.Fatalf("%s.%s type = %q, want %q", tc.object, tc.field, field.Type, tc.typ)
		}
		if tc.createable != nil {
			if field.Createable == nil || *field.Createable != *tc.createable {
				t.Fatalf("%s.%s createable = %v, want %v", tc.object, tc.field, field.Createable, *tc.createable)
			}
		}
		if tc.updateable != nil {
			if field.Updateable == nil || *field.Updateable != *tc.updateable {
				t.Fatalf("%s.%s updateable = %v, want %v", tc.object, tc.field, field.Updateable, *tc.updateable)
			}
		}
		if tc.isNameField != nil && field.IsNameField != *tc.isNameField {
			t.Fatalf("%s.%s isNameField = %v, want %v", tc.object, tc.field, field.IsNameField, *tc.isNameField)
		}
		if tc.returnType != "" && field.ReturnType != tc.returnType {
			t.Fatalf("%s.%s returnType = %q, want %q", tc.object, tc.field, field.ReturnType, tc.returnType)
		}
		if tc.formulaMode != "" && field.FormulaMode != tc.formulaMode {
			t.Fatalf("%s.%s formulaMode = %q, want %q", tc.object, tc.field, field.FormulaMode, tc.formulaMode)
		}
		if tc.formula != "" && field.Formula != tc.formula {
			t.Fatalf("%s.%s formula = %q, want %q", tc.object, tc.field, field.Formula, tc.formula)
		}
		if tc.projectionTarget != "" {
			if field.ProjectionConfig == nil {
				t.Fatalf("%s.%s projection = nil, want target %q", tc.object, tc.field, tc.projectionTarget)
			}
			if field.ProjectionConfig.TargetObject != tc.projectionTarget {
				t.Fatalf("%s.%s projection targetObject = %q, want %q", tc.object, tc.field, field.ProjectionConfig.TargetObject, tc.projectionTarget)
			}
			if field.ProjectionConfig.SelectField != tc.projectionSelect {
				t.Fatalf("%s.%s projection selectField = %q, want %q", tc.object, tc.field, field.ProjectionConfig.SelectField, tc.projectionSelect)
			}
			if tc.projectionExcludeGone != nil && field.ProjectionConfig.ExcludeDeleted != *tc.projectionExcludeGone {
				t.Fatalf("%s.%s projection excludeDeleted = %v, want %v", tc.object, tc.field, field.ProjectionConfig.ExcludeDeleted, *tc.projectionExcludeGone)
			}
			gotMatches := make(map[string]string, len(field.ProjectionConfig.Matches))
			for _, match := range field.ProjectionConfig.Matches {
				gotMatches[match.TargetField] = match.SourceField
			}
			if len(gotMatches) != len(tc.projectionMatches) {
				t.Fatalf("%s.%s projection matches = %v, want %v", tc.object, tc.field, gotMatches, tc.projectionMatches)
			}
			for targetField, sourceField := range tc.projectionMatches {
				if gotMatches[targetField] != sourceField {
					t.Fatalf("%s.%s projection match %s = %q, want %q", tc.object, tc.field, targetField, gotMatches[targetField], sourceField)
				}
			}
		}
		if len(tc.referenceTo) > 0 && !equalStringSets(field.ReferenceTo, tc.referenceTo) {
			t.Fatalf("%s.%s referenceTo = %v, want %v", tc.object, tc.field, field.ReferenceTo, tc.referenceTo)
		}
		if tc.relationshipName != "" && field.RelationshipName != tc.relationshipName {
			t.Fatalf("%s.%s relationshipName = %q, want %q", tc.object, tc.field, field.RelationshipName, tc.relationshipName)
		}
		if tc.childRelationshipName != "" && field.ChildRelationshipName != tc.childRelationshipName {
			t.Fatalf("%s.%s childRelationshipName = %q, want %q", tc.object, tc.field, field.ChildRelationshipName, tc.childRelationshipName)
		}
	}
}

func TestCRMStandardPackDeclaresAccountAndContactLastActivityDate(t *testing.T) {
	raw, err := os.ReadFile(filepath.Join(officialPackRootFromThisFile(t), "salesforce-crm-standard", "objects", "standard_objects_spec.json"))
	if err != nil {
		t.Fatalf("read CRM pack object spec: %v", err)
	}
	specs, err := parseObjectSpecsJSON(raw, "official_repo:salesforce-crm-standard/objects/standard_objects_spec.json")
	if err != nil {
		t.Fatalf("parse CRM pack object spec: %v", err)
	}

	objects := make(map[string]ObjectSpec, len(specs))
	for _, spec := range specs {
		objects[spec.APIName] = spec
	}

	for _, tc := range []struct {
		object string
		label  string
	}{
		{object: "Account", label: "Last Activity"},
		{object: "Contact", label: "Last Activity"},
	} {
		spec, ok := objects[tc.object]
		if !ok {
			t.Fatalf("object %s missing from CRM pack", tc.object)
		}
		field, ok := fieldSpecByAPIName(spec.Fields, "LastActivityDate")
		if !ok {
			t.Fatalf("%s.LastActivityDate missing from CRM pack", tc.object)
		}
		if field.Type != "date" {
			t.Fatalf("%s.LastActivityDate type = %q, want %q", tc.object, field.Type, "date")
		}
		if field.Label != tc.label {
			t.Fatalf("%s.LastActivityDate label = %q, want %q", tc.object, field.Label, tc.label)
		}
		if field.Createable == nil || *field.Createable {
			t.Fatalf("%s.LastActivityDate createable = %v, want false", tc.object, field.Createable)
		}
		if field.Updateable == nil || *field.Updateable {
			t.Fatalf("%s.LastActivityDate updateable = %v, want false", tc.object, field.Updateable)
		}
		if field.Required {
			t.Fatalf("%s.LastActivityDate required = true, want false (Salesforce read-only system field)", tc.object)
		}
		if strings.Contains(tc.object, "__c") || strings.Contains(field.APIName, "__c") {
			t.Fatalf("%s.%s must remain standard-pack payload, not tenant payload", tc.object, field.APIName)
		}
		// Verify exactly one LastActivityDate field — no duplicates in the
		// source payload that would indicate a second declaration surface.
		count := 0
		for _, f := range spec.Fields {
			if f.APIName == "LastActivityDate" {
				count++
			}
		}
		if count != 1 {
			t.Fatalf("%s has %d LastActivityDate fields in CRM pack, want exactly 1", tc.object, count)
		}
	}

	// Verify that unrelated objects do NOT carry a LastActivityDate
	// declaration from the pack source. Case is the canonical CRM object
	// that has no such field — if it appeared, the backend would start
	// inventing a second declaration surface.
	caseSpec, caseExists := objects["Case"]
	if caseExists {
		if _, hasField := fieldSpecByAPIName(caseSpec.Fields, "LastActivityDate"); hasField {
			t.Fatal("Case.LastActivityDate must NOT be declared in CRM pack source — Case does not have this field in Salesforce")
		}
	}
}

func objectForKeyPrefixFromDeclarations(decls compatibilityDeclarationSet, keyPrefix string) (string, bool) {
	keyPrefix = strings.TrimSpace(keyPrefix)
	if keyPrefix == "" {
		return "", false
	}
	for _, ident := range decls.ObjectIdentities {
		if strings.TrimSpace(ident.KeyPrefix) == keyPrefix {
			return strings.TrimSpace(ident.ObjectAPIName), true
		}
	}
	return "", false
}

func keyPrefixForObjectFromDeclarations(decls compatibilityDeclarationSet, objectAPIName string) (string, bool) {
	objectAPIName = strings.TrimSpace(objectAPIName)
	if objectAPIName == "" {
		return "", false
	}
	for _, ident := range decls.ObjectIdentities {
		if strings.EqualFold(strings.TrimSpace(ident.ObjectAPIName), objectAPIName) {
			return strings.TrimSpace(ident.KeyPrefix), true
		}
		for _, alias := range ident.Aliases {
			if strings.EqualFold(strings.TrimSpace(alias), objectAPIName) {
				return strings.TrimSpace(ident.KeyPrefix), true
			}
		}
	}
	return "", false
}

func foreignKeyForRelationshipFromDeclarations(decls compatibilityDeclarationSet, objectAPIName, relationshipName string) (string, bool) {
	objectAPIName = strings.TrimSpace(objectAPIName)
	relationshipName = strings.TrimSpace(relationshipName)
	if objectAPIName == "" || relationshipName == "" {
		return "", false
	}
	for _, rel := range decls.Relationships {
		if strings.EqualFold(strings.TrimSpace(rel.ObjectAPIName), objectAPIName) &&
			strings.EqualFold(strings.TrimSpace(rel.RelationshipName), relationshipName) {
			return strings.TrimSpace(rel.ForeignKey), true
		}
	}
	return "", false
}

func targetObjectsForForeignKeyFromDeclarations(decls compatibilityDeclarationSet, objectAPIName, foreignKey string) ([]string, bool) {
	objectAPIName = strings.TrimSpace(objectAPIName)
	foreignKey = strings.TrimSpace(foreignKey)
	if objectAPIName == "" || foreignKey == "" {
		return nil, false
	}
	for _, rel := range decls.Relationships {
		if strings.EqualFold(strings.TrimSpace(rel.ObjectAPIName), objectAPIName) &&
			strings.EqualFold(strings.TrimSpace(rel.ForeignKey), foreignKey) {
			return append([]string(nil), rel.TargetObjectNames...), true
		}
	}
	return nil, false
}

func equalStringSets(left, right []string) bool {
	if len(left) != len(right) {
		return false
	}
	seen := make(map[string]struct{}, len(left))
	for _, value := range left {
		seen[strings.ToLower(value)] = struct{}{}
	}
	for _, value := range right {
		if _, ok := seen[strings.ToLower(value)]; !ok {
			return false
		}
	}
	return true
}

func fieldSpecByAPIName(fields []FieldSpec, apiName string) (FieldSpec, bool) {
	for _, field := range fields {
		if field.APIName == apiName {
			return field, true
		}
	}
	return FieldSpec{}, false
}

func boolPtr(v bool) *bool {
	return &v
}

func productionGoFiles(t *testing.T) []string {
	t.Helper()
	root := requireNexusCRMRepoRootForGuardrail(t)
	roots := []string{
		filepath.Join(root, "backend"),
		filepath.Join(root, "shared"),
	}
	var files []string
	for _, scanRoot := range roots {
		err := filepath.WalkDir(scanRoot, func(path string, entry fs.DirEntry, err error) error {
			if err != nil {
				return err
			}
			if entry.IsDir() {
				switch entry.Name() {
				case ".git", "node_modules", "dist", "vendor":
					return filepath.SkipDir
				}
				return nil
			}
			if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
				return nil
			}
			files = append(files, path)
			return nil
		})
		if err != nil {
			t.Fatalf("walk %s: %v", scanRoot, err)
		}
	}
	sort.Strings(files)
	return files
}

func repoRootFromThisFile(t *testing.T) string {
	t.Helper()
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("runtime.Caller failed")
	}
	return filepath.Clean(filepath.Join(filepath.Dir(file), ".."))
}

func requireNexusCRMRepoRootForGuardrail(t *testing.T) string {
	t.Helper()
	if root, ok := nexusCRMRepoRootForGuardrail(t); ok {
		return root
	}
	t.Skipf("NexusCRM workspace is required for cross-repo guardrails; set %s to a checkout containing backend, frontend, and shared", nexusCRMRepoRootEnvVar)
	return ""
}

func nexusCRMRepoRootForGuardrail(t *testing.T) (string, bool) {
	t.Helper()
	candidates := []string{
		strings.TrimSpace(os.Getenv(nexusCRMRepoRootEnvVar)),
		repoRootFromThisFile(t),
	}
	if root, err := gitRepoRootForPackage(t, "github.com/nexuscrm/shared/pkg/constants"); err == nil {
		candidates = append(candidates, filepath.Dir(filepath.Dir(filepath.Dir(root))))
	}
	for _, candidate := range candidates {
		if candidate == "" {
			continue
		}
		root, err := filepath.EvalSymlinks(candidate)
		if err != nil {
			root = candidate
		}
		if hasNexusCRMWorkspaceShape(root) {
			return filepath.Clean(root), true
		}
	}
	return "", false
}

func gitRepoRootForPackage(t *testing.T, importPath string) (string, error) {
	t.Helper()
	cmd := exec.Command("go", "list", "-f", "{{.Dir}}", importPath)
	cmd.Env = append(os.Environ(), "GOWORK=off")
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(out)), nil
}

func hasNexusCRMWorkspaceShape(root string) bool {
	for _, rel := range []string{
		filepath.Join("backend", "go.mod"),
		filepath.Join("frontend", "src"),
		filepath.Join("shared", "go.mod"),
	} {
		if _, err := os.Stat(filepath.Join(root, rel)); err != nil {
			return false
		}
	}
	return true
}

func officialPackRootFromThisFile(t *testing.T) string {
	t.Helper()
	if root := strings.TrimSpace(os.Getenv(OfficialAppPacksRootEnvVar)); root != "" {
		if resolved, err := filepath.EvalSymlinks(root); err == nil {
			return resolved
		}
		return root
	}
	repoRoot := repoRootFromThisFile(t)
	sibling := filepath.Join(filepath.Dir(repoRoot), "nexuscrm-app-packs")
	if info, err := os.Stat(sibling); err == nil && info.IsDir() {
		if resolved, err := filepath.EvalSymlinks(sibling); err == nil {
			return resolved
		}
		return sibling
	}
	t.Fatalf("official app packs root is required: set %s or provide sibling checkout %s", OfficialAppPacksRootEnvVar, sibling)
	return ""
}

func relRepoPath(t *testing.T, path string) string {
	t.Helper()
	if root, ok := nexusCRMRepoRootForGuardrail(t); ok {
		if rel, err := filepath.Rel(root, path); err == nil && !strings.HasPrefix(rel, "..") {
			return filepath.ToSlash(rel)
		}
	}
	rel, err := filepath.Rel(repoRootFromThisFile(t), path)
	if err != nil {
		return path
	}
	return filepath.ToSlash(rel)
}

func resetDefaultHostPrimitivesForTest(t *testing.T) {
	t.Helper()
	defaultHostPrimitives.Lock()
	previous := make(HostPrimitiveRegistry, len(defaultHostPrimitives.registry))
	for key, primitive := range defaultHostPrimitives.registry {
		previous[key] = primitive
	}
	defaultHostPrimitives.registry = HostPrimitiveRegistry{}
	defaultHostPrimitives.Unlock()
	t.Cleanup(func() {
		defaultHostPrimitives.Lock()
		defaultHostPrimitives.registry = previous
		defaultHostPrimitives.Unlock()
	})
}

func registerCRMHostPrimitivesForTest(t *testing.T) {
	t.Helper()
	resetDefaultHostPrimitivesForTest(t)
	restoreSessionClasses := featureauth.SetProductOAuthSessionIssuerClassOwners([]featureauth.ProductOAuthSessionIssuerClassOwner{{
		Class:          "salesforce_compat",
		OwningAppPacks: []string{"salesforce-crm-standard"},
	}})
	t.Cleanup(restoreSessionClasses)
	RegisterDefaultHostPrimitives(
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/action-renderer/lead-convert"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/action-renderer/create-record-from-source"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/action-renderer/task-activity-quick-create"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/action-renderer/email-compose"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/record-panel/activity-timeline"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/record-panel/record-activity-feed"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/record-panel/record-child-bar-chart"},
		HostPrimitive{Kind: hostPrimitiveKindRenderer, Name: "app-pack:crm/banner/converted-lead"},
		HostPrimitive{Kind: HostPrimitiveKindProductDialect, Name: "salesforce-v63"},
	)
}

func appPackObjectNamesForL0GeneratedGuardrail(t *testing.T) map[string]struct{} {
	t.Helper()
	root := officialPackRootFromThisFile(t)
	return appPackObjectNamesFromPayloadRoot(t, root)
}

func appPackObjectNamesFromPayloadRoot(t *testing.T, root string) map[string]struct{} {
	t.Helper()
	out := make(map[string]struct{})
	err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if entry.IsDir() {
			return nil
		}
		rel, relErr := filepath.Rel(root, path)
		if relErr != nil {
			return relErr
		}
		if rel == "." {
			return nil
		}
		if filepath.ToSlash(rel) == "platform_core/objects/standard_objects_spec.json" {
			return nil
		}
		if filepath.Base(path) != "standard_objects_spec.json" || filepath.Base(filepath.Dir(path)) != "objects" {
			return nil
		}
		raw, readErr := os.ReadFile(path)
		if readErr != nil {
			return readErr
		}
		specs, parseErr := parseObjectSpecsJSON(raw, relRepoPath(t, path))
		if parseErr != nil {
			return parseErr
		}
		for _, spec := range specs {
			if spec.APIName == "" || strings.EqualFold(spec.Category, "system") {
				continue
			}
			out[spec.APIName] = struct{}{}
		}
		return nil
	})
	if err != nil {
		t.Fatalf("collect app-pack object payload names: %v", err)
	}
	if len(out) == 0 {
		t.Fatal("no app-pack object payload names found for generated-artifact guardrail")
	}
	return out
}

func assertGeneratedCommentsExcludeAppPackObjects(t *testing.T, path, raw string, appObjectNames map[string]struct{}) {
	t.Helper()
	for _, comment := range sourceComments(raw) {
		for appObject := range appObjectNames {
			if containsIdentifier(comment, appObject) {
				t.Fatalf("%s generated comment still includes app-pack object literal %q: %s",
					relRepoPath(t, path), appObject, strings.TrimSpace(comment))
			}
		}
	}
}

func sourceComments(raw string) []string {
	var comments []string
	for i := 0; i < len(raw); {
		if i+1 >= len(raw) {
			break
		}
		if raw[i] == '/' && raw[i+1] == '/' {
			start := i + 2
			i = start
			for i < len(raw) && raw[i] != '\n' {
				i++
			}
			comments = append(comments, raw[start:i])
			continue
		}
		if raw[i] == '/' && raw[i+1] == '*' {
			start := i + 2
			i = start
			for i+1 < len(raw) && !(raw[i] == '*' && raw[i+1] == '/') {
				i++
			}
			comments = append(comments, raw[start:i])
			if i+1 < len(raw) {
				i += 2
			}
			continue
		}
		i++
	}
	return comments
}

func containsIdentifier(text, ident string) bool {
	if containsDelimitedIdentifier(text, ident) {
		return true
	}
	if containsDelimitedIdentifier(text, ident+"s") {
		return true
	}
	if strings.HasSuffix(ident, "y") && containsDelimitedIdentifier(text, strings.TrimSuffix(ident, "y")+"ies") {
		return true
	}
	return false
}

func containsDelimitedIdentifier(text, ident string) bool {
	idx := strings.Index(text, ident)
	for idx >= 0 {
		beforeOK := idx == 0 || !isIdentifierContentRune(rune(text[idx-1]))
		afterIdx := idx + len(ident)
		afterOK := afterIdx >= len(text) || !isIdentifierContentRune(rune(text[afterIdx]))
		if beforeOK && afterOK {
			return true
		}
		next := strings.Index(text[idx+len(ident):], ident)
		if next < 0 {
			return false
		}
		idx += len(ident) + next
	}
	return false
}

func isIdentifierContentRune(r rune) bool {
	return r == '$' || unicode.IsLetter(r) || unicode.IsDigit(r)
}
