package uiapi

import (
	"context"
	"strings"
	"time"

	"github.com/nexuscrm/backend/internal/domain/ports"
	"github.com/nexuscrm/backend/internal/domain/schema"
	"github.com/nexuscrm/backend/internal/features/metadata"
	"github.com/nexuscrm/backend/internal/features/permissions"
	"github.com/nexuscrm/backend/internal/features/records"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// =============================================================================
// Mock PermissionRepository — minimal implementation for unit tests
// =============================================================================

type mockPermRepo struct {
	roles       []*models.UserRole
	objectPerms map[string]*models.ObjectPermissions
	fieldPerms  map[string]*models.FieldPermissions
}

func (m *mockPermRepo) GetAllRoles(_ context.Context) ([]*models.UserRole, error) {
	return m.roles, nil
}

// Stubs for remaining interface methods (not exercised in tests)
func (m *mockPermRepo) LoadObjectPermission(_ context.Context, _, _ string) (*models.ObjectPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) LoadFieldPermission(_ context.Context, _, _, _ string) (*models.FieldPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) LoadEffectiveObjectPermission(_ context.Context, _ *models.UserSession, objectAPIName string) (*models.ObjectPermissions, error) {
	if m.objectPerms == nil {
		return nil, nil
	}
	return m.objectPerms[objectAPIName], nil
}
func (m *mockPermRepo) LoadEffectiveFieldPermission(_ context.Context, _ *models.UserSession, objectAPIName, fieldAPIName string) (*models.FieldPermissions, error) {
	if m.fieldPerms == nil {
		return nil, nil
	}
	return m.fieldPerms[objectAPIName+"."+fieldAPIName], nil
}
func (m *mockPermRepo) ListObjectPermissions(_ context.Context, _ string) ([]models.ObjectPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) ListFieldPermissions(_ context.Context, _ string) ([]models.FieldPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) UpsertObjectPermission(_ context.Context, _ models.ObjectPermissions) error {
	return nil
}
func (m *mockPermRepo) BatchUpsertObjectPermissions(_ context.Context, _ []models.ObjectPermissions) error {
	return nil
}
func (m *mockPermRepo) BatchUpsertFieldPermissions(_ context.Context, _ []models.FieldPermissions) error {
	return nil
}
func (m *mockPermRepo) DeleteFieldPermissionsForFields(_ context.Context, _ string, _ []string) error {
	return nil
}
func (m *mockPermRepo) CountObjectPermissionsByProfile(_ context.Context, _ string) (int, error) {
	return 0, nil
}
func (m *mockPermRepo) UpsertFieldPermission(_ context.Context, _ models.FieldPermissions) error {
	return nil
}
func (m *mockPermRepo) ListPermissionSetObjectPermissions(_ context.Context, _ string) ([]models.ObjectPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) ListPermissionSetFieldPermissions(_ context.Context, _ string) ([]models.FieldPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) ListEffectiveObjectPermissionsForUser(_ context.Context, _, _ string) ([]models.ObjectPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) ListEffectiveFieldPermissionsForUser(_ context.Context, _, _ string) ([]models.FieldPermissions, error) {
	return nil, nil
}
func (m *mockPermRepo) CreatePermissionSet(_ context.Context, _, _, _ string) (string, error) {
	return "", nil
}
func (m *mockPermRepo) UpdatePermissionSet(_ context.Context, _, _, _, _ string) error { return nil }
func (m *mockPermRepo) DeletePermissionSet(_ context.Context, _ string) error          { return nil }
func (m *mockPermRepo) GetUserProfileID(_ context.Context, _ string) (string, error)   { return "", nil }
func (m *mockPermRepo) CreateRole(_ context.Context, _, _ string, _ *string) (string, error) {
	return "", nil
}
func (m *mockPermRepo) GetRole(_ context.Context, _ string) (*models.UserRole, error) {
	return nil, nil
}
func (m *mockPermRepo) UpdateRole(_ context.Context, _, _, _ string, _ *string) error { return nil }
func (m *mockPermRepo) DeleteRole(_ context.Context, _ string) error                  { return nil }
func (m *mockPermRepo) IsUserInGroup(_ context.Context, _, _ string) (bool, error)    { return false, nil }
func (m *mockPermRepo) GetManualShareAccessLevels(_ context.Context, _, _, _ string) ([]string, error) {
	return nil, nil
}
func (m *mockPermRepo) GetTeamMemberAccessLevel(_ context.Context, _, _, _ string) (*string, error) {
	return nil, nil
}
func (m *mockPermRepo) GetRecordOwnerID(_ context.Context, _, _ string) (string, error) {
	return "", nil
}
func (m *mockPermRepo) GetRecordByID(_ context.Context, _, _ string) (models.SObject, error) {
	return nil, nil
}
func (m *mockPermRepo) GetPendingApprovalLockInfo(_ context.Context, _ string) (string, string, error) {
	return "", "", nil
}
func (m *mockPermRepo) HasPendingApprovalWorkitemForUser(_ context.Context, _, _ string) (bool, error) {
	return false, nil
}
func (m *mockPermRepo) GetUserGroupIDs(_ context.Context, _ string) ([]string, error) {
	return nil, nil
}
func (m *mockPermRepo) GetManualShareRecordIDs(_ context.Context, _, _ string) ([]string, error) {
	return nil, nil
}
func (m *mockPermRepo) GetTeamMemberRecordIDs(_ context.Context, _, _ string) ([]string, error) {
	return nil, nil
}
func (m *mockPermRepo) GetAllProfiles(_ context.Context) ([]*models.Profile, error) { return nil, nil }
func (m *mockPermRepo) ListSystemPermissions(_ context.Context) ([]models.SystemPermission, error) {
	return nil, nil
}
func (m *mockPermRepo) GetEffectiveSystemPermissions(_ context.Context, _, _ string) (map[string]bool, error) {
	return nil, nil
}
func (m *mockPermRepo) GetEffectiveCustomPermissions(_ context.Context, _, _ string) (map[string]bool, error) {
	return nil, nil
}
func (m *mockPermRepo) LockPermissionSet(_ context.Context, _ string) (bool, error) {
	return true, nil
}
func (m *mockPermRepo) GetPermissionSetGrants(_ context.Context, _ string) (map[string]bool, error) {
	return nil, nil
}
func (m *mockPermRepo) GetPermissionSetGrantsMany(_ context.Context, _ []string) (map[string]map[string]bool, error) {
	return nil, nil
}
func (m *mockPermRepo) SetSystemPermission(_ context.Context, _, _ string, _ bool) error {
	return nil
}
func (m *mockPermRepo) ListCustomPermissions(_ context.Context) ([]models.SystemCustomPermission, error) {
	return nil, nil
}
func (m *mockPermRepo) GetCustomPermission(_ context.Context, _ string) (*models.SystemCustomPermission, error) {
	return nil, nil
}
func (m *mockPermRepo) CreateCustomPermission(_ context.Context, _ *models.SystemCustomPermission) error {
	return nil
}
func (m *mockPermRepo) UpdateCustomPermission(_ context.Context, _ *models.SystemCustomPermission) error {
	return nil
}
func (m *mockPermRepo) DeleteCustomPermission(_ context.Context, _ string) error { return nil }
func (m *mockPermRepo) ListPermissionSetGroups(_ context.Context) ([]models.PermissionSetGroup, error) {
	return nil, nil
}
func (m *mockPermRepo) GetPermissionSetGroupByID(_ context.Context, _ string) (*models.PermissionSetGroup, error) {
	return nil, nil
}
func (m *mockPermRepo) GetPermissionSetGroupComponents(_ context.Context, _ string) ([]ports.PermissionSetRef, error) {
	return nil, nil
}
func (m *mockPermRepo) CreatePermissionSetGroup(_ context.Context, _ *models.PermissionSetGroup, _ []string) error {
	return nil
}
func (m *mockPermRepo) UpdatePermissionSetGroup(_ context.Context, _ *models.PermissionSetGroup, _ []string) error {
	return nil
}
func (m *mockPermRepo) DeletePermissionSetGroup(_ context.Context, _ string) error { return nil }
func (m *mockPermRepo) CleanupDirectProfilePermissions(_ context.Context, _ []string) error {
	return nil
}
func (m *mockPermRepo) BulkEnsureProfilePermissionSets(_ context.Context, _ []struct{ ID, Name string }) (map[string]string, error) {
	return nil, nil
}
func (m *mockPermRepo) BulkCountObjectPermissionsByProfiles(_ context.Context, _ []string) (map[string]int, error) {
	return nil, nil
}
func (m *mockPermRepo) BulkCountFieldPermissionsByProfiles(_ context.Context, _ []string) (map[string]int, error) {
	return nil, nil
}
func (m *mockPermRepo) BulkCountObjectPermissionsByProfilesAndTypes(_ context.Context, _ []string, _ []string) (map[string]int, error) {
	return nil, nil
}
func (m *mockPermRepo) BulkCountFieldPermissionsByProfilesAndTypes(_ context.Context, _ []string, _ []string) (map[string]int, error) {
	return nil, nil
}
func (m *mockPermRepo) EnsureProfilePermissionSet(_ context.Context, profileID, _ string) (string, error) {
	return "ps_" + profileID, nil
}
func (m *mockPermRepo) CleanupProfilePermissionSet(_ context.Context, _ string) error {
	return nil
}

// =============================================================================
// Mock MetadataRepository — minimal implementation for unit tests
// =============================================================================

type mockMetaRepo struct {
	schemas             []*models.ObjectMetadata
	apps                map[string]*models.AppConfig
	layouts             map[string][]*models.PageLayout
	assignmentsByObject map[string][]*models.LayoutAssignment
	listViews           map[string][]*models.ListView
	approvalLayouts     map[string][]*models.ApprovalPageLayout
	validationRules     []*models.ValidationRule
}

func newMockMetaRepo() *mockMetaRepo {
	return &mockMetaRepo{
		apps:                make(map[string]*models.AppConfig),
		layouts:             make(map[string][]*models.PageLayout),
		assignmentsByObject: make(map[string][]*models.LayoutAssignment),
		listViews:           make(map[string][]*models.ListView),
		approvalLayouts:     make(map[string][]*models.ApprovalPageLayout),
	}
}

func (m *mockMetaRepo) GetAllSchemas(_ context.Context) ([]*models.ObjectMetadata, error) {
	return m.schemas, nil
}
func (m *mockMetaRepo) GetSchemaByAPIName(_ context.Context, apiName string) (*models.ObjectMetadata, error) {
	for _, s := range m.schemas {
		if s.APIName == apiName {
			return s, nil
		}
	}
	return nil, nil
}
func (m *mockMetaRepo) GetFieldsForObject(_ context.Context, _ string) ([]models.FieldMetadata, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetRecordTypes(_ context.Context, _ string) ([]*models.RecordType, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAutoNumbers(_ context.Context, _ string) ([]*models.AutoNumber, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAllAutoNumbers(_ context.Context) ([]*models.AutoNumber, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAllIdentifierRules(_ context.Context) ([]*models.IdentifierRule, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAllValidationRules(_ context.Context) ([]*models.ValidationRule, error) {
	return m.validationRules, nil
}
func (m *mockMetaRepo) GetAllRelationships(_ context.Context) ([]*models.Relationship, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetRelationships(_ context.Context, _ string) ([]*models.Relationship, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFieldDependencies(_ context.Context, _ string) ([]*models.FieldDependency, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetChildRelationships(_ context.Context, _ string) ([]*models.ObjectMetadata, error) {
	return nil, nil
}
func (m *mockMetaRepo) ConfigureAutoNumberField(_ context.Context, _, _, _ string, _ int64) error {
	return nil
}
func (m *mockMetaRepo) UpsertUIComponent(_ context.Context, _ *models.UIComponent) error { return nil }
func (m *mockMetaRepo) UpsertSetupPage(_ context.Context, _ *models.SetupPage) error     { return nil }
func (m *mockMetaRepo) GetSetupPages(_ context.Context) ([]*models.SetupPage, error)     { return nil, nil }
func (m *mockMetaRepo) UpsertLayout(_ context.Context, _ *models.PageLayout) error       { return nil }
func (m *mockMetaRepo) SaveLayout(_ context.Context, _ *models.PageLayout) error         { return nil }
func (m *mockMetaRepo) GetLayout(_ context.Context, id string) (*models.PageLayout, error) {
	for _, layouts := range m.layouts {
		for _, l := range layouts {
			if l.ID == id {
				cloned := *l
				cloned.IsDefault = cloned.ID == m.effectiveDefaultLayoutID(cloned.ObjectAPIName)
				return &cloned, nil
			}
		}
	}
	return nil, nil
}
func (m *mockPermRepo) MarkLayoutCustomized(_ context.Context, _ string) {}
func (m *mockMetaRepo) GetLayouts(_ context.Context, objAPIName string) ([]*models.PageLayout, error) {
	defaultLayoutID := m.effectiveDefaultLayoutID(objAPIName)
	layouts := m.layouts[objAPIName]
	result := make([]*models.PageLayout, 0, len(layouts))
	for _, layout := range layouts {
		if layout == nil {
			continue
		}
		cloned := *layout
		cloned.IsDefault = cloned.ID == defaultLayoutID
		result = append(result, &cloned)
	}
	return result, nil
}
func (m *mockMetaRepo) DeleteLayout(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) AssignLayoutToProfile(_ context.Context, _, _, _ string) error {
	return nil
}
func (m *mockMetaRepo) GetLayoutIDForProfile(_ context.Context, _, _ string) (string, error) {
	return "", nil
}
func (m *mockMetaRepo) GetRelatedListConfigs(_ context.Context, parentAPI string) ([]ports.RelatedListConfig, error) {
	var results []ports.RelatedListConfig
	lower := strings.ToLower(parentAPI)
	for _, s := range m.schemas {
		for _, f := range s.Fields {
			if f.Type != constants.FieldTypeReference {
				continue
			}
			for _, ref := range f.ReferenceTo {
				if strings.ToLower(ref) == lower {
					results = append(results, ports.RelatedListConfig{
						LookupFieldAPI:   f.APIName,
						ChildObjectAPI:   s.APIName,
						ChildPluralLabel: s.PluralLabel,
					})
				}
			}
		}
	}
	return results, nil
}
func (m *mockMetaRepo) GetActiveTheme(_ context.Context) (*models.Theme, error) { return nil, nil }
func (m *mockMetaRepo) GetThemeByName(_ context.Context, _ string) (*models.Theme, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateTheme(_ context.Context, _ *models.Theme) error { return nil }
func (m *mockMetaRepo) UpdateTheme(_ context.Context, _ *models.Theme) error { return nil }
func (m *mockMetaRepo) ActivateTheme(_ context.Context, _ string) error      { return nil }
func (m *mockMetaRepo) GetAllApps(_ context.Context) ([]*models.AppConfig, error) {
	var result []*models.AppConfig
	for _, a := range m.apps {
		result = append(result, a)
	}
	return result, nil
}
func (m *mockMetaRepo) GetApp(_ context.Context, id string) (*models.AppConfig, error) {
	if a, ok := m.apps[id]; ok {
		return a, nil
	}
	return nil, nil
}
func (m *mockMetaRepo) GetAppWithTx(_ context.Context, _ interface{}, id string) (*models.AppConfig, error) {
	return m.GetApp(context.Background(), id)
}
func (m *mockMetaRepo) CreateApp(_ context.Context, app *models.AppConfig) error {
	m.apps[app.ID] = app
	return nil
}
func (m *mockMetaRepo) CreateAppMenuItem(_ context.Context, _ *models.AppConfig) error { return nil }
func (m *mockMetaRepo) UpdateApp(_ context.Context, id string, app *models.AppConfig) error {
	m.apps[id] = app
	return nil
}
func (m *mockMetaRepo) UpdateAppWithTx(_ context.Context, _ interface{}, id string, app *models.AppConfig) error {
	return m.UpdateApp(context.Background(), id, app)
}
func (m *mockMetaRepo) DeleteApp(_ context.Context, id string) error {
	delete(m.apps, id)
	return nil
}
func (m *mockMetaRepo) GetAllDashboards(_ context.Context) ([]*models.DashboardConfig, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetDashboard(_ context.Context, _ string) (*models.DashboardConfig, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateDashboard(_ context.Context, _ *models.DashboardConfig) error {
	return nil
}
func (m *mockMetaRepo) UpdateDashboard(_ context.Context, _ string, _ *models.DashboardConfig) error {
	return nil
}
func (m *mockMetaRepo) DeleteDashboard(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) GetListViews(_ context.Context, objAPIName string) ([]*models.ListView, error) {
	return m.listViews[objAPIName], nil
}
func (m *mockMetaRepo) CreateListView(_ context.Context, _ *models.ListView) error { return nil }
func (m *mockMetaRepo) UpdateListView(_ context.Context, _ string, _ *models.ListView) error {
	return nil
}
func (m *mockMetaRepo) DeleteListView(_ context.Context, _ string) error        { return nil }
func (m *mockMetaRepo) CountListViews(_ context.Context, _ string) (int, error) { return 0, nil }
func (m *mockMetaRepo) GetActions(_ context.Context, _ string) ([]*models.ActionMetadata, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAction(_ context.Context, _ string) (*models.ActionMetadata, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAllActions(_ context.Context) ([]*models.ActionMetadata, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateAction(_ context.Context, _ *models.ActionMetadata) error { return nil }
func (m *mockMetaRepo) UpdateAction(_ context.Context, _ string, _ *models.ActionMetadata) error {
	return nil
}
func (m *mockMetaRepo) DeleteAction(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) CheckActionExists(_ context.Context, _, _ string) (bool, error) {
	return false, nil
}
func (m *mockMetaRepo) GetSharingRules(_ context.Context, _ string) ([]*models.SharingRule, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetAllFlowDefinitions(_ context.Context) ([]*models.FlowDefinition, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFlowDefinition(_ context.Context, _ string) (*models.FlowDefinition, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFlowDefinitionByName(_ context.Context, _ string) (*models.FlowDefinition, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateFlowDefinition(_ context.Context, _ *models.FlowDefinition) error {
	return nil
}
func (m *mockMetaRepo) UpdateFlowDefinition(_ context.Context, _ string, _ *models.FlowDefinition) error {
	return nil
}
func (m *mockMetaRepo) PatchFlowDefinition(_ context.Context, _ string, _ *models.FlowDefinitionPatch) error {
	return nil
}
func (m *mockMetaRepo) SetLatestVersionId(_ context.Context, _, _ string) error { return nil }
func (m *mockMetaRepo) DeleteFlowDefinition(_ context.Context, _ string) error  { return nil }
func (m *mockMetaRepo) GetFlowVersions(_ context.Context, _ string) ([]*models.FlowVersion, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFlowVersionsByIDs(_ context.Context, _ []string) (map[string]*models.FlowVersion, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFlowVersion(_ context.Context, _ string) (*models.FlowVersion, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetActiveFlowVersion(_ context.Context, _ string) (*models.FlowVersion, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateFlowVersion(_ context.Context, _ *models.FlowVersion) error {
	return nil
}
func (m *mockMetaRepo) CreateSeededFlowVersion(_ context.Context, _ *models.FlowVersion) error {
	return nil
}
func (m *mockMetaRepo) CreateTenantFlowVersion(_ context.Context, _ *models.FlowVersion) error {
	return nil
}
func (m *mockMetaRepo) UpdateFlowVersion(_ context.Context, _ string, _ *models.FlowVersion) error {
	return nil
}
func (m *mockMetaRepo) ActivateFlowVersion(_ context.Context, _ string) error   { return nil }
func (m *mockMetaRepo) DeactivateFlowVersion(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) DeleteFlowVersion(_ context.Context, _ string) error     { return nil }
func (m *mockMetaRepo) GetFlowInterviews(_ context.Context) ([]*models.FlowInterview, error) {
	return nil, nil
}
func (m *mockMetaRepo) GetFlowInterview(_ context.Context, _ string) (*models.FlowInterview, error) {
	return nil, nil
}
func (m *mockMetaRepo) SaveFlowInterview(_ context.Context, _ *models.FlowInterview) error {
	return nil
}
func (m *mockMetaRepo) DeleteFlowInterview(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) GetValidationRules(_ context.Context, objectAPIName string) ([]*models.ValidationRule, error) {
	var rules []*models.ValidationRule
	for _, rule := range m.validationRules {
		if strings.EqualFold(rule.ObjectAPIName, objectAPIName) {
			rules = append(rules, rule)
		}
	}
	return rules, nil
}
func (m *mockMetaRepo) GetValidationRule(_ context.Context, _ string) (*models.ValidationRule, error) {
	return nil, nil
}
func (m *mockMetaRepo) CreateValidationRule(_ context.Context, _ *models.ValidationRule) error {
	return nil
}
func (m *mockMetaRepo) UpdateValidationRule(_ context.Context, _ string, _ *models.ValidationRule) error {
	return nil
}
func (m *mockMetaRepo) DeleteValidationRule(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) CheckValidationRuleExists(_ context.Context, _, _ string) (bool, error) {
	return false, nil
}
func (m *mockMetaRepo) SetDefaultLayout(_ context.Context, _, _ string, _ *string) error {
	return nil
}
func (m *mockMetaRepo) SetTriggerInfo(_ context.Context, _, _, _ string) error { return nil }

// =============================================================================
// Mock RecordRepository — minimal implementation for unit tests
// =============================================================================

type mockRecordRepo struct {
	records map[string]map[string]models.SObject // tableName -> id -> record
}

func newMockRecordRepo() *mockRecordRepo {
	return &mockRecordRepo{
		records: make(map[string]map[string]models.SObject),
	}
}

func (m *mockRecordRepo) addRecord(tableName, id string, rec models.SObject) {
	if m.records[tableName] == nil {
		m.records[tableName] = make(map[string]models.SObject)
	}
	m.records[tableName][id] = rec
}

func (m *mockRecordRepo) FindOne(_ context.Context, tableName, id string) (models.SObject, error) {
	if table, ok := m.records[tableName]; ok {
		if rec, ok := table[id]; ok {
			return rec, nil
		}
	}
	return nil, nil
}

func (m *mockRecordRepo) FindMany(_ context.Context, tableName string, ids []string) (map[string]models.SObject, error) {
	result := make(map[string]models.SObject)
	if table, ok := m.records[tableName]; ok {
		for _, id := range ids {
			if rec, ok := table[id]; ok {
				result[id] = rec
			}
		}
	}
	return result, nil
}

func (m *mockRecordRepo) Insert(_ context.Context, _ string, _ models.SObject) error { return nil }
func (m *mockRecordRepo) BulkInsert(_ context.Context, _ string, _ []models.SObject, _ []string, _ interface{}) error {
	return nil
}
func (m *mockRecordRepo) BulkInsertWithLanding(_ context.Context, _ string, records []models.SObject, _ []string, _ interface{}) ([]bool, error) {
	landed := make([]bool, len(records))
	for i := range landed {
		landed[i] = true
	}
	return landed, nil
}
func (m *mockRecordRepo) Update(_ context.Context, _ string, _ string, _ models.SObject) error {
	return nil
}
func (m *mockRecordRepo) Delete(_ context.Context, _, _ string) error         { return nil }
func (m *mockRecordRepo) Exists(_ context.Context, _, _ string) (bool, error) { return false, nil }
func (m *mockRecordRepo) GetLock(_ context.Context, _, _ string) (models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) GetChildren(_ context.Context, _, _, _ string) ([]models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) ExistsByField(_ context.Context, _, _ string, _ interface{}) (bool, error) {
	return false, nil
}
func (m *mockRecordRepo) FindOneByField(_ context.Context, _, _ string, _ interface{}) (models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) FindAny(_ context.Context, _, _ string) (models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) PhysicalDelete(_ context.Context, _, _ string) error { return nil }
func (m *mockRecordRepo) DeleteByField(_ context.Context, _, _ string, _ interface{}) error {
	return nil
}
func (m *mockRecordRepo) CheckUniqueness(_ context.Context, _ *models.ObjectMetadata, _ string, _ interface{}, _ string) (bool, error) {
	return false, nil
}
func (m *mockRecordRepo) FindRecycleBinItemsByUser(_ context.Context, _ string) ([]models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) FindAllRecycleBinItems(_ context.Context) ([]models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) FindRecycleBinEntry(_ context.Context, _ string) (models.SObject, error) {
	return nil, nil
}
func (m *mockRecordRepo) CheckQueueSupport(_ context.Context, _, _ string) (bool, error) {
	return false, nil
}
func (m *mockRecordRepo) GetRecordType(_ context.Context, _ string) (string, error) { return "", nil }
func (m *mockRecordRepo) FindObjectTypesForIDs(_ context.Context, _ []string, _ []string) (map[string]string, error) {
	return nil, nil
}

// =============================================================================
// Mock SchemaRepository — minimal stub
// =============================================================================

type mockSchemaRepo struct{}

func (m *mockSchemaRepo) CreatePhysicalTable(_ context.Context, _ schema.TableDefinition) error {
	return nil
}
func (m *mockSchemaRepo) CreateTableWithStrictMetadata(_ context.Context, _ schema.TableDefinition, _ *models.ObjectMetadata) error {
	return nil
}
func (m *mockSchemaRepo) BatchCreatePhysicalTables(_ context.Context, _ []schema.TableDefinition) error {
	return nil
}
func (m *mockSchemaRepo) DropTable(_ string) error                                { return nil }
func (m *mockSchemaRepo) ValidateSchema(_ string) error                           { return nil }
func (m *mockSchemaRepo) ValidateFieldDefinition(_ schema.ColumnDefinition) error { return nil }
func (m *mockSchemaRepo) ValidateSchemaRegistry() (*schema.SchemaHealth, error) {
	return &schema.SchemaHealth{}, nil
}
func (m *mockSchemaRepo) BatchRegisterTables(_ []schema.TableDefinition, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) AddColumn(_ string, _ schema.ColumnDefinition) error    { return nil }
func (m *mockSchemaRepo) EnsureColumn(_ string, _ schema.ColumnDefinition) error { return nil }
func (m *mockSchemaRepo) DropColumn(_, _ string) error                           { return nil }
func (m *mockSchemaRepo) ModifyColumn(_, _ string, _ schema.ColumnDefinition) error {
	return nil
}
func (m *mockSchemaRepo) IsSystemColumn(_ string) bool                        { return false }
func (m *mockSchemaRepo) GetStandardSystemColumns() []schema.ColumnDefinition { return nil }
func (m *mockSchemaRepo) GetStandardFieldMetadata() []models.FieldMetadata    { return nil }
func (m *mockSchemaRepo) SaveObjectMetadata(_ context.Context, _ *models.ObjectMetadata, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) InsertObjectMetadata(_ context.Context, _ *models.ObjectMetadata, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) BatchSaveObjectMetadata(_ context.Context, _ []*models.ObjectMetadata, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) DeleteObjectMetadata(_ context.Context, _ string, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) SaveFieldMetadata(_ context.Context, _ string, _ *models.FieldMetadata, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) DeleteFieldMetadata(_ context.Context, _, _ string, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) GetDB() interface{} { return nil }
func (m *mockSchemaRepo) SaveFieldMetadataWithIDs(_ context.Context, _ *models.FieldMetadata, _, _, _ string, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) BatchSaveFieldMetadata(_ context.Context, _ []schema.FieldWithContext, _ interface{}) error {
	return nil
}
func (m *mockSchemaRepo) PrepareFieldForBatch(_ string, _ schema.ColumnDefinition) schema.FieldWithContext {
	return schema.FieldWithContext{}
}
func (m *mockSchemaRepo) GetTableRegistry(_ context.Context) ([]*schema.TableRegistryItem, error) {
	return nil, nil
}
func (m *mockSchemaRepo) MapFieldTypeToSQL(_ string) string                        { return "" }
func (m *mockSchemaRepo) ValidateFormula(_ string, _ map[string]interface{}) error { return nil }
func (m *mockSchemaRepo) BatchAddColumns(_ string, _ []models.FieldMetadata) error { return nil }
func (m *mockSchemaRepo) RegisterFieldOnly(_ string, _ schema.ColumnDefinition) error {
	return nil
}

// =============================================================================
// testService builds a fully wired Service for unit testing
// =============================================================================

// testService constructs a Service with mock dependencies.
// The returned user is a system admin, bypassing permission checks.
func testService(schemas []*models.ObjectMetadata) (*Service, *models.UserSession, func()) {
	ctx := context.Background()

	// Build MetadataService with mock repos
	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx) // Load schemas into cache

	// Build PermissionService with mock repo
	permRepo := &mockPermRepo{}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	// Build PersistenceService (records) — uses mock record repo
	// PersistenceService requires many deps; we use nil for unused ones
	recordRepo := newMockRecordRepo()
	rs := records.NewPersistenceService(recordRepo, nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)

	svc := NewService(rs, ms, ps, nil)

	user := &models.UserSession{
		ID:            "test-user-001",
		Name:          "Test Admin",
		ProfileID:     "system_admin",
		IsSystemAdmin: true,
	}

	cleanup := func() {
		svc.Close()
	}

	return svc, user, cleanup
}

// testServiceWithRecords builds a Service with pre-loaded records.
func testServiceWithRecords(schemas []*models.ObjectMetadata, recordData map[string]map[string]models.SObject) (*Service, *models.UserSession, func()) {
	ctx := context.Background()

	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx)

	permRepo := &mockPermRepo{}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	recordRepo := newMockRecordRepo()
	for tableName, records := range recordData {
		for id, rec := range records {
			recordRepo.addRecord(tableName, id, rec)
		}
	}

	rs := records.NewPersistenceService(recordRepo, nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)
	svc := NewService(rs, ms, ps, nil)

	user := &models.UserSession{
		ID:            "test-user-001",
		Name:          "Test Admin",
		ProfileID:     "system_admin",
		IsSystemAdmin: true,
	}

	cleanup := func() {
		svc.Close()
	}

	return svc, user, cleanup
}

func testServiceWithRecordsAndValidationRules(
	schemas []*models.ObjectMetadata,
	recordData map[string]map[string]models.SObject,
	validationRules []*models.ValidationRule,
) (*Service, *models.UserSession, func()) {
	user := &models.UserSession{
		ID:            "test-user-001",
		Name:          "Test Admin",
		ProfileID:     "system_admin",
		IsSystemAdmin: true,
	}

	return testServiceWithRecordsValidationRulesPermsAndUser(schemas, recordData, validationRules, &mockPermRepo{}, user)
}

func testServiceWithRecordsValidationRulesPermsAndUser(
	schemas []*models.ObjectMetadata,
	recordData map[string]map[string]models.SObject,
	validationRules []*models.ValidationRule,
	permRepo *mockPermRepo,
	user *models.UserSession,
) (*Service, *models.UserSession, func()) {
	ctx := context.Background()

	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	metaRepo.validationRules = validationRules
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx)

	if permRepo == nil {
		permRepo = &mockPermRepo{}
	}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	recordRepo := newMockRecordRepo()
	for tableName, records := range recordData {
		for id, rec := range records {
			recordRepo.addRecord(tableName, id, rec)
		}
	}

	rs := records.NewPersistenceService(recordRepo, nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)
	svc := NewService(rs, ms, ps, nil)

	if user == nil {
		user = &models.UserSession{
			ID:        "test-user-001",
			Name:      "Test User",
			ProfileID: "profile-001",
		}
	}

	cleanup := func() {
		svc.Close()
	}

	return svc, user, cleanup
}

func testServiceWithRecordsAndUser(
	schemas []*models.ObjectMetadata,
	recordData map[string]map[string]models.SObject,
	objectPerms map[string]*models.ObjectPermissions,
	user *models.UserSession,
) (*Service, func()) {
	ctx := context.Background()

	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx)

	permRepo := &mockPermRepo{objectPerms: objectPerms}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	recordRepo := newMockRecordRepo()
	for tableName, records := range recordData {
		for id, rec := range records {
			recordRepo.addRecord(tableName, id, rec)
		}
	}

	rs := records.NewPersistenceService(recordRepo, nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)
	svc := NewService(rs, ms, ps, nil)

	return svc, func() { svc.Close() }
}

// testServiceWithLayouts builds a Service with pre-loaded layouts.
func testServiceWithLayouts(schemas []*models.ObjectMetadata, layoutData map[string][]*models.PageLayout) (*Service, *models.UserSession, func()) {
	ctx := context.Background()

	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	metaRepo.layouts = layoutData
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx)

	permRepo := &mockPermRepo{}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	rs := records.NewPersistenceService(newMockRecordRepo(), nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)
	svc := NewService(rs, ms, ps, nil)

	user := &models.UserSession{
		ID:            "test-user-001",
		Name:          "Test Admin",
		ProfileID:     "system_admin",
		IsSystemAdmin: true,
	}

	return svc, user, func() { svc.Close() }
}

// testServiceWithListViews builds a Service with pre-loaded list views.
func testServiceWithListViews(schemas []*models.ObjectMetadata, lvData map[string][]*models.ListView) (*Service, *models.UserSession, func()) {
	ctx := context.Background()

	metaRepo := newMockMetaRepo()
	metaRepo.schemas = schemas
	metaRepo.listViews = lvData
	schemaRepo := &mockSchemaRepo{}
	schemaMgr := metadata.NewSchemaManager(schemaRepo)
	ms := metadata.NewMetadataService(metaRepo, schemaMgr, nil)
	_ = ms.RefreshCache(ctx)

	permRepo := &mockPermRepo{}
	ps := permissions.NewPermissionService(permRepo, ms, nil)

	rs := records.NewPersistenceService(newMockRecordRepo(), nil, ms, ps, nil, nil, nil, nil, nil, nil, nil, nil, nil)
	svc := NewService(rs, ms, ps, nil)

	user := &models.UserSession{
		ID:            "test-user-001",
		Name:          "Test Admin",
		ProfileID:     "system_admin",
		IsSystemAdmin: true,
	}

	return svc, user, func() { svc.Close() }
}

// fakeTimer creates a cache with specific TTL for tests that need time control.
func fakeTimer(ttl time.Duration) *cache {
	return newCache(ttl)
}
func (m *mockMetaRepo) MarkLayoutCustomized(_ context.Context, _ string) {}
func (m *mockMetaRepo) MarkFlowDefinitionCustomized(_ context.Context, _ string) error {
	return nil
}

// --- Layout Assignment interface stubs (WP2) ---
func (m *mockMetaRepo) FindBestAssignment(_ context.Context, objectAPIName string, _ []string, _ string, _ string) (*models.LayoutAssignment, error) {
	layouts, ok := m.layouts[objectAPIName]
	if !ok {
		layouts, ok = m.layouts[strings.ToLower(objectAPIName)]
	}
	if ok && len(layouts) > 0 {
		l := layouts[0]
		if l.Source == "" {
			l.Source = "auto_generated"
		}
		if l.GenerationVersion == 0 && l.LayoutVersion > 0 {
			l.GenerationVersion = l.LayoutVersion
		}
		return &models.LayoutAssignment{
			ID:            "mock_assignment_" + l.ID,
			ObjectAPIName: objectAPIName,
			LayoutID:      l.ID,
		}, nil
	}
	return nil, nil
}
func (m *mockMetaRepo) CreateAssignment(_ context.Context, assignment *models.LayoutAssignment) error {
	m.upsertAssignment(assignment)
	return nil
}
func (m *mockMetaRepo) DeleteAssignment(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) GetAssignmentsForObject(_ context.Context, objectAPIName string) ([]*models.LayoutAssignment, error) {
	assignments := m.assignmentsByObject[objectAPIName]
	if len(assignments) == 0 {
		assignments = m.assignmentsByObject[strings.ToLower(objectAPIName)]
	}
	return append([]*models.LayoutAssignment(nil), assignments...), nil
}
func (m *mockMetaRepo) DeleteAssignmentsByLayoutID(_ context.Context, _ string) error { return nil }
func (m *mockMetaRepo) effectiveDefaultLayoutID(objectAPIName string) string {
	assignments := m.assignmentsByObject[objectAPIName]
	if len(assignments) == 0 {
		assignments = m.assignmentsByObject[strings.ToLower(objectAPIName)]
	}
	var wildcard *models.LayoutAssignment
	for _, assignment := range assignments {
		if assignment == nil {
			continue
		}
		if assignment.RoleID != nil || assignment.ProfileID != nil || assignment.RecordTypeID != nil {
			continue
		}
		if !m.layoutExists(objectAPIName, assignment.LayoutID) {
			continue
		}
		if wildcard == nil {
			wildcard = assignment
			continue
		}
		if wildcard.CreatedDate.IsZero() {
			if !assignment.CreatedDate.IsZero() {
				wildcard = assignment
			}
			continue
		}
		if !assignment.CreatedDate.IsZero() && assignment.CreatedDate.Before(wildcard.CreatedDate) {
			wildcard = assignment
		}
	}
	if wildcard == nil {
		return ""
	}
	return wildcard.LayoutID
}

func (m *mockMetaRepo) layoutExists(objectAPIName, layoutID string) bool {
	for _, key := range []string{objectAPIName, strings.ToLower(objectAPIName)} {
		for _, layout := range m.layouts[key] {
			if layout != nil && layout.ID == layoutID {
				return true
			}
		}
	}
	return false
}

func (m *mockMetaRepo) upsertAssignment(assignment *models.LayoutAssignment) {
	if assignment == nil {
		return
	}
	key := strings.ToLower(assignment.ObjectAPIName)
	assignments := m.assignmentsByObject[key]
	for i, existing := range assignments {
		if sameAssignmentScope(existing, assignment) {
			assignments[i] = assignment
			m.assignmentsByObject[key] = assignments
			return
		}
	}
	m.assignmentsByObject[key] = append(assignments, assignment)
}

func sameAssignmentScope(left *models.LayoutAssignment, right *models.LayoutAssignment) bool {
	if left == nil || right == nil {
		return false
	}
	return sameOptionalString(left.RoleID, right.RoleID) &&
		sameOptionalString(left.ProfileID, right.ProfileID) &&
		sameOptionalString(left.RecordTypeID, right.RecordTypeID)
}

func sameOptionalString(left *string, right *string) bool {
	if left == nil || right == nil {
		return left == nil && right == nil
	}
	return *left == *right
}
func (m *mockMetaRepo) WriteLayoutHistory(_ context.Context, _ *models.PageLayoutHistoryEntry) error {
	return nil
}
func (m *mockMetaRepo) GetLayoutHistoryMaxVersion(_ context.Context, _ string) (int, error) {
	return 0, nil
}
func (m *mockMetaRepo) GetMigrationVersion(_ context.Context, _ string) (int, error) { return 0, nil }
func (m *mockMetaRepo) SetMigrationVersion(_ context.Context, _ string, _ int, _ string) error {
	return nil
}

func (m *mockMetaRepo) GetLayoutHistoryConfig(_ context.Context, _ string, _ int) (string, error) {
	return "", nil
}

func (m *mockMetaRepo) PruneLayoutHistory(_ context.Context, _ string, _ int) error { return nil }

// Mock methods for ApprovalPageLayout
func (m *mockMetaRepo) GetApprovalLayouts(_ context.Context, _ string) ([]*models.ApprovalPageLayout, error) {
	return []*models.ApprovalPageLayout{}, nil
}

func (m *mockMetaRepo) GetApprovalLayout(_ context.Context, _ string) (*models.ApprovalPageLayout, error) {
	return nil, nil
}

func (m *mockMetaRepo) SaveApprovalLayout(_ context.Context, _ *models.ApprovalPageLayout) error {
	return nil
}

func (m *mockMetaRepo) DeleteApprovalLayout(_ context.Context, _ string) error {
	return nil
}

func (m *mockMetaRepo) SetDefaultApprovalLayout(_ context.Context, _, _ string) error {
	return nil
}
