package service

import (
	"baron-sso-backend/internal/domain"
	"baron-sso-backend/internal/repository"
	"context"
	"errors"
	"os"
	"sort"
	"strings"
)

const HanmacFamilyTenantSlug = "hanmac-family"

type WorksmobileSyncer interface {
	EnqueueTenantUpsertIfInScope(ctx context.Context, tenant domain.Tenant) error
	EnqueueTenantDeleteIfInScope(ctx context.Context, tenant domain.Tenant) error
	EnqueueUserUpsertIfInScope(ctx context.Context, user domain.User) error
	EnqueueUserDeleteIfInScope(ctx context.Context, user domain.User) error
}

type WorksmobileAdminService interface {
	GetTenantOverview(ctx context.Context, tenantID string) (WorksmobileTenantOverview, error)
	GetComparison(ctx context.Context, tenantID string, includeMatched bool) (WorksmobileComparison, error)
	EnqueueBackfillDryRun(ctx context.Context, tenantID string) (WorksmobileBackfillDryRun, error)
	EnqueueOrgUnitSync(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error)
	EnqueueOrgUnitDelete(ctx context.Context, tenantID, worksmobileOrgUnitID string) (*domain.WorksmobileOutbox, error)
	EnqueueUserSync(ctx context.Context, tenantID, userID string) (*domain.WorksmobileOutbox, error)
	RetryJob(ctx context.Context, tenantID, jobID string) (*domain.WorksmobileOutbox, error)
	ListInitialPasswordCredentials(ctx context.Context, tenantID string) ([]WorksmobileInitialPasswordCredential, error)
}

type WorksmobileConfigSummary struct {
	Enabled         bool             `json:"enabled"`
	DomainMappings  map[string]int64 `json:"domainMappings"`
	TokenConfigured bool             `json:"tokenConfigured"`
	AdminTenantID   string           `json:"adminTenantId,omitempty"`
}

type WorksmobileTenantOverview struct {
	Tenant     domain.Tenant              `json:"tenant"`
	Config     WorksmobileConfigSummary   `json:"config"`
	RecentJobs []domain.WorksmobileOutbox `json:"recentJobs"`
}

type WorksmobileBackfillDryRun struct {
	OrgUnitCount int `json:"orgUnitCount"`
	UserCount    int `json:"userCount"`
}

type WorksmobileInitialPasswordCredential struct {
	Email           string `json:"email"`
	InitialPassword string `json:"initialPassword"`
	Status          string `json:"status"`
	LastError       string `json:"lastError,omitempty"`
}

type WorksmobileComparison struct {
	Users  []WorksmobileComparisonItem `json:"users"`
	Groups []WorksmobileComparisonItem `json:"groups"`
}

type WorksmobileComparisonItem struct {
	ResourceType                      string `json:"resourceType"`
	BaronID                           string `json:"baronId,omitempty"`
	BaronSlug                         string `json:"baronSlug,omitempty"`
	BaronName                         string `json:"baronName,omitempty"`
	BaronEmail                        string `json:"baronEmail,omitempty"`
	BaronPrimaryOrgID                 string `json:"baronPrimaryOrgId,omitempty"`
	BaronPrimaryOrgSlug               string `json:"baronPrimaryOrgSlug,omitempty"`
	BaronPrimaryOrgName               string `json:"baronPrimaryOrgName,omitempty"`
	BaronParentID                     string `json:"baronParentId,omitempty"`
	BaronParentSlug                   string `json:"baronParentSlug,omitempty"`
	BaronParentName                   string `json:"baronParentName,omitempty"`
	WorksmobileID                     string `json:"worksmobileId,omitempty"`
	ExternalKey                       string `json:"externalKey,omitempty"`
	WorksmobileName                   string `json:"worksmobileName,omitempty"`
	WorksmobileEmail                  string `json:"worksmobileEmail,omitempty"`
	WorksmobileLevelID                string `json:"worksmobileLevelId,omitempty"`
	WorksmobileLevelName              string `json:"worksmobileLevelName,omitempty"`
	WorksmobileTask                   string `json:"worksmobileTask,omitempty"`
	WorksmobileDomainID               int64  `json:"worksmobileDomainId,omitempty"`
	WorksmobileDomainName             string `json:"worksmobileDomainName,omitempty"`
	WorksmobilePrimaryOrgID           string `json:"worksmobilePrimaryOrgId,omitempty"`
	WorksmobilePrimaryOrgName         string `json:"worksmobilePrimaryOrgName,omitempty"`
	WorksmobilePrimaryOrgPositionID   string `json:"worksmobilePrimaryOrgPositionId,omitempty"`
	WorksmobilePrimaryOrgPositionName string `json:"worksmobilePrimaryOrgPositionName,omitempty"`
	WorksmobilePrimaryOrgIsManager    *bool  `json:"worksmobilePrimaryOrgIsManager,omitempty"`
	BaronParentWorksmobileID          string `json:"baronParentWorksmobileId,omitempty"`
	BaronParentWorksmobileName        string `json:"baronParentWorksmobileName,omitempty"`
	BaronParentWorksmobileEmail       string `json:"baronParentWorksmobileEmail,omitempty"`
	WorksmobileParentID               string `json:"worksmobileParentId,omitempty"`
	WorksmobileParentName             string `json:"worksmobileParentName,omitempty"`
	WorksmobileParentEmail            string `json:"worksmobileParentEmail,omitempty"`
	WorksmobileParentExternalKey      string `json:"worksmobileParentExternalKey,omitempty"`
	Status                            string `json:"status"`
}

type worksmobileSyncService struct {
	tenantService TenantService
	userRepo      repository.UserRepository
	outboxRepo    repository.WorksmobileOutboxRepository
	client        WorksmobileDirectoryClient
}

func NewWorksmobileSyncService(tenantService TenantService, userRepo repository.UserRepository, outboxRepo repository.WorksmobileOutboxRepository, client WorksmobileDirectoryClient) *worksmobileSyncService {
	return &worksmobileSyncService{
		tenantService: tenantService,
		userRepo:      userRepo,
		outboxRepo:    outboxRepo,
		client:        client,
	}
}

func (s *worksmobileSyncService) GetTenantOverview(ctx context.Context, tenantID string) (WorksmobileTenantOverview, error) {
	tenant, err := s.tenantService.GetTenant(ctx, tenantID)
	if err != nil {
		return WorksmobileTenantOverview{}, err
	}
	jobs, _ := s.outboxRepo.ListRecent(ctx, 50)
	jobs = redactWorksmobileOutboxPayloads(jobs)
	return WorksmobileTenantOverview{
		Tenant: *tenant,
		Config: WorksmobileConfigSummary{
			Enabled:         WorksmobileEnabled(tenant.Config),
			DomainMappings:  WorksmobileDomainMappings(tenant.Config),
			TokenConfigured: worksmobileDirectoryAuthConfigured(),
			AdminTenantID:   strings.TrimSpace(os.Getenv("WORKS_ADMIN_TENANT_ID")),
		},
		RecentJobs: jobs,
	}, nil
}

func worksmobileDirectoryAuthConfigured() bool {
	if strings.TrimSpace(os.Getenv("WORKS_ADMIN_ACCESS_TOKEN")) != "" || strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_ACCESS_TOKEN")) != "" {
		return true
	}
	return strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_ID")) != "" &&
		strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SECRET")) != "" &&
		strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SERVICE_ACCOUNT")) != "" &&
		(strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_PRIVATE_KEY")) != "" ||
			strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_PRIVATE_KEY_FILE")) != "")
}

func redactWorksmobileOutboxPayloads(jobs []domain.WorksmobileOutbox) []domain.WorksmobileOutbox {
	for i := range jobs {
		if jobs[i].Payload != nil {
			jobs[i].Payload = nil
		}
	}
	return jobs
}

func (s *worksmobileSyncService) GetComparison(ctx context.Context, tenantID string, includeMatched bool) (WorksmobileComparison, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return WorksmobileComparison{}, err
	}
	if s.client == nil {
		return WorksmobileComparison{}, errors.New("worksmobile client is not configured")
	}

	tenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return WorksmobileComparison{}, err
	}
	tenantByID := worksmobileTenantByID(tenants)
	tenantByID[root.ID] = *root
	tenantIDs := make([]string, 0, len(tenants))
	for _, tenant := range tenants {
		if isWorksmobileUserScopeTenant(tenant) {
			tenantIDs = append(tenantIDs, tenant.ID)
		}
	}
	users, err := s.userRepo.FindByTenantIDs(ctx, tenantIDs)
	if err != nil {
		return WorksmobileComparison{}, err
	}
	remoteUsers, err := s.client.ListUsers(ctx)
	if err != nil {
		return WorksmobileComparison{}, err
	}
	remoteGroups, err := s.client.ListGroups(ctx)
	if err != nil {
		return WorksmobileComparison{}, err
	}

	return WorksmobileComparison{
		Users:  compareWorksmobileUsers(users, remoteUsers, includeMatched, tenantByID),
		Groups: compareWorksmobileGroups(append([]domain.Tenant{*root}, tenants...), remoteGroups, includeMatched),
	}, nil
}

func (s *worksmobileSyncService) EnqueueBackfillDryRun(ctx context.Context, tenantID string) (WorksmobileBackfillDryRun, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return WorksmobileBackfillDryRun{}, err
	}
	tenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return WorksmobileBackfillDryRun{}, err
	}
	orgUnitTenantIDs := make([]string, 0, len(tenants))
	userTenantIDs := make([]string, 0, len(tenants))
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, tenants...))
	for _, tenant := range tenants {
		if isWorksmobileOrgUnitTenant(tenant, tenantByID) {
			orgUnitTenantIDs = append(orgUnitTenantIDs, tenant.ID)
		}
		if isWorksmobileUserScopeTenant(tenant) {
			userTenantIDs = append(userTenantIDs, tenant.ID)
		}
	}
	users, err := s.userRepo.FindByTenantIDs(ctx, userTenantIDs)
	if err != nil {
		return WorksmobileBackfillDryRun{}, err
	}
	users = worksmobileSyncScopeUsers(users)
	_ = s.outboxRepo.Create(ctx, &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceOrgUnit,
		ResourceID:   root.ID,
		Action:       domain.WorksmobileActionDryRun,
		DedupeKey:    "backfill:dry-run:" + root.ID,
		Payload: domain.JSONMap{
			"tenantIds": orgUnitTenantIDs,
			"userCount": len(users),
		},
	})
	return WorksmobileBackfillDryRun{OrgUnitCount: len(orgUnitTenantIDs), UserCount: len(users)}, nil
}

func (s *worksmobileSyncService) EnqueueOrgUnitSync(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return nil, err
	}
	tenant, err := s.tenantService.GetTenant(ctx, orgUnitID)
	if err != nil {
		return nil, err
	}
	tenantRoot, ok, err := s.rootForTenant(ctx, *tenant)
	if err != nil {
		return nil, err
	}
	if !ok || tenantRoot.ID != root.ID {
		return nil, errors.New("target orgunit is outside hanmac-family subtree")
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return nil, err
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	if !isWorksmobileOrgUnitTenant(*tenant, tenantByID) {
		return nil, errors.New("target tenant is not a worksmobile orgunit tenant")
	}
	return s.enqueueOrgUnitUpsert(ctx, root, *tenant, scopeTenants)
}

func (s *worksmobileSyncService) enqueueOrgUnitUpsert(ctx context.Context, root *domain.Tenant, tenant domain.Tenant, scopeTenants []domain.Tenant) (*domain.WorksmobileOutbox, error) {
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	payload, err := BuildWorksmobileOrgUnitPayloadForDomainTenant(
		tenant,
		worksmobileDomainClassificationTenant(tenant, tenantByID),
		root.Config,
		0,
	)
	if err != nil {
		return nil, err
	}
	payload = normalizeWorksmobileOrgUnitParent(payload, tenant, tenantByID, root.ID)
	item := &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceOrgUnit,
		ResourceID:   tenant.ID,
		Action:       domain.WorksmobileActionUpsert,
		DedupeKey:    "orgunit:upsert:" + tenant.ID,
		Payload: domain.JSONMap{
			"request":        payload,
			"matchLocalPart": tenant.Slug,
		},
	}
	if err := s.outboxRepo.Create(ctx, item); err != nil {
		return nil, err
	}
	return item, nil
}

func (s *worksmobileSyncService) EnqueueOrgUnitDelete(ctx context.Context, tenantID, worksmobileOrgUnitID string) (*domain.WorksmobileOutbox, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return nil, err
	}
	if s.client == nil {
		return nil, errors.New("worksmobile client is not configured")
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return nil, err
	}
	worksmobileOrgUnitID = strings.TrimSpace(worksmobileOrgUnitID)
	if worksmobileOrgUnitID == "" {
		return nil, errors.New("worksmobile orgunit id is required")
	}
	groups, err := s.client.ListGroups(ctx)
	if err != nil {
		return nil, err
	}
	var target *WorksmobileRemoteGroup
	for i := range groups {
		if strings.TrimSpace(groups[i].ID) == worksmobileOrgUnitID {
			target = &groups[i]
			break
		}
	}
	if target == nil {
		return nil, errors.New("worksmobile orgunit not found")
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	if tenant, ok := findWorksmobileOrgUnitTenantByRemoteLocalPart(*target, scopeTenants, tenantByID); ok {
		return s.enqueueOrgUnitUpsert(ctx, root, tenant, scopeTenants)
	}
	if isProtectedWorksmobileRemoteOrgUnit(*root, scopeTenants, *target) {
		return nil, errors.New("protected worksmobile domain root orgunit cannot be deleted")
	}
	item := &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceOrgUnit,
		ResourceID:   worksmobileOrgUnitID,
		Action:       domain.WorksmobileActionDelete,
		DedupeKey:    "orgunit:delete:works:" + worksmobileOrgUnitID,
		Payload: domain.JSONMap{
			"worksmobileId": worksmobileOrgUnitID,
			"externalKey":   target.ExternalID,
			"domainId":      target.DomainID,
			"name":          target.DisplayName,
			"email":         target.Email,
		},
	}
	if err := s.outboxRepo.Create(ctx, item); err != nil {
		return nil, err
	}
	return item, nil
}

func (s *worksmobileSyncService) EnqueueUserSync(ctx context.Context, tenantID, userID string) (*domain.WorksmobileOutbox, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return nil, err
	}
	user, err := s.userRepo.FindByID(ctx, userID)
	if err != nil {
		return nil, err
	}
	if user.TenantID == nil {
		return nil, errors.New("target user has no tenant")
	}
	tenant, err := s.tenantService.GetTenant(ctx, *user.TenantID)
	if err != nil {
		return nil, err
	}
	tenantRoot, ok, err := s.rootForTenant(ctx, *tenant)
	if err != nil {
		return nil, err
	}
	if !ok || tenantRoot.ID != root.ID {
		return nil, errors.New("target user is outside hanmac-family subtree")
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return nil, err
	}
	if domain.IsWorksDeprovisionUserStatus(user.Status) {
		return s.enqueueUserDelete(ctx, *user, "user:delete:"+user.ID, root.ID)
	}
	if !domain.IsWorksProvisionedUserStatus(user.Status) {
		return nil, errors.New("target user status is excluded from Worksmobile sync")
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	payload, err := BuildWorksmobileUserPayloadForDomainTenants(
		*user,
		*tenant,
		tenantByID,
		root.Config,
	)
	if err != nil {
		return nil, err
	}
	if err := s.validateUserAliasLocalParts(ctx, root, *user, payload); err != nil {
		return nil, err
	}
	action := WorksmobileUserStatusAction(user.Status)
	item := &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceUser,
		ResourceID:   user.ID,
		Action:       action,
		DedupeKey:    "user:" + strings.ToLower(action) + ":" + user.ID,
		Payload:      worksmobileUserOutboxPayload(root.ID, payload, user.Status),
	}
	if err := s.outboxRepo.Create(ctx, item); err != nil {
		return nil, err
	}
	return item, nil
}

func (s *worksmobileSyncService) ListInitialPasswordCredentials(ctx context.Context, tenantID string) ([]WorksmobileInitialPasswordCredential, error) {
	root, err := s.hanmacRoot(ctx, tenantID)
	if err != nil {
		return nil, err
	}
	jobs, err := s.outboxRepo.ListRecent(ctx, 1000)
	if err != nil {
		return nil, err
	}
	credentials := make([]WorksmobileInitialPasswordCredential, 0)
	seen := map[string]bool{}
	for _, job := range jobs {
		if job.ResourceType != domain.WorksmobileResourceUser {
			continue
		}
		if stringValue(job.Payload["tenantRootId"]) != root.ID {
			continue
		}
		email := stringValue(job.Payload["loginEmail"])
		password := stringValue(job.Payload["initialPassword"])
		if email == "" || password == "" || seen[email] {
			continue
		}
		seen[email] = true
		credentials = append(credentials, WorksmobileInitialPasswordCredential{
			Email:           email,
			InitialPassword: password,
			Status:          job.Status,
			LastError:       job.LastError,
		})
	}
	return credentials, nil
}

func (s *worksmobileSyncService) RetryJob(ctx context.Context, tenantID, jobID string) (*domain.WorksmobileOutbox, error) {
	if _, err := s.hanmacRoot(ctx, tenantID); err != nil {
		return nil, err
	}
	if err := s.outboxRepo.MarkRetry(ctx, jobID); err != nil {
		return nil, err
	}
	return s.outboxRepo.FindByID(ctx, jobID)
}

func (s *worksmobileSyncService) EnqueueTenantUpsertIfInScope(ctx context.Context, tenant domain.Tenant) error {
	root, ok, err := s.rootForTenant(ctx, tenant)
	if err != nil || !ok {
		return err
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return err
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	if !isWorksmobileOrgUnitTenant(tenant, tenantByID) {
		return nil
	}
	payload, err := BuildWorksmobileOrgUnitPayloadForDomainTenant(
		tenant,
		worksmobileDomainClassificationTenant(tenant, tenantByID),
		root.Config,
		0,
	)
	if err != nil {
		return err
	}
	payload = normalizeWorksmobileOrgUnitParent(payload, tenant, tenantByID, root.ID)
	return s.outboxRepo.Create(ctx, &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceOrgUnit,
		ResourceID:   tenant.ID,
		Action:       domain.WorksmobileActionUpsert,
		DedupeKey:    "orgunit:upsert:" + tenant.ID,
		Payload: domain.JSONMap{
			"request":        payload,
			"matchLocalPart": tenant.Slug,
		},
	})
}

func (s *worksmobileSyncService) EnqueueTenantDeleteIfInScope(ctx context.Context, tenant domain.Tenant) error {
	root, ok, err := s.rootForTenant(ctx, tenant)
	if err != nil || !ok {
		return err
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return err
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	if !isWorksmobileOrgUnitTenant(tenant, tenantByID) {
		return nil
	}
	return s.outboxRepo.Create(ctx, &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceOrgUnit,
		ResourceID:   tenant.ID,
		Action:       domain.WorksmobileActionDelete,
		DedupeKey:    "orgunit:delete:" + tenant.ID,
		Payload:      domain.JSONMap{"orgUnitExternalKey": tenant.ID},
	})
}

func (s *worksmobileSyncService) EnqueueUserUpsertIfInScope(ctx context.Context, user domain.User) error {
	if user.TenantID == nil || *user.TenantID == "" {
		return nil
	}
	tenant, err := s.tenantService.GetTenant(ctx, *user.TenantID)
	if err != nil {
		return err
	}
	root, ok, err := s.rootForTenant(ctx, *tenant)
	if err != nil || !ok {
		return err
	}
	scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return err
	}
	if domain.IsWorksDeprovisionUserStatus(user.Status) {
		_, err := s.enqueueUserDelete(ctx, user, "user:delete:"+user.ID, root.ID)
		return err
	}
	if !domain.IsWorksProvisionedUserStatus(user.Status) {
		return nil
	}
	tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
	payload, err := BuildWorksmobileUserPayloadForDomainTenants(
		user,
		*tenant,
		tenantByID,
		root.Config,
	)
	if err != nil {
		return err
	}
	if err := s.validateUserAliasLocalParts(ctx, root, user, payload); err != nil {
		return err
	}
	action := WorksmobileUserStatusAction(user.Status)
	return s.outboxRepo.Create(ctx, &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceUser,
		ResourceID:   user.ID,
		Action:       action,
		DedupeKey:    "user:" + strings.ToLower(action) + ":" + user.ID,
		Payload:      worksmobileUserOutboxPayload(root.ID, payload, user.Status),
	})
}

func (s *worksmobileSyncService) EnqueueUserDeleteIfInScope(ctx context.Context, user domain.User) error {
	if user.TenantID == nil || *user.TenantID == "" {
		return nil
	}
	tenant, err := s.tenantService.GetTenant(ctx, *user.TenantID)
	if err != nil {
		return err
	}
	_, ok, err := s.rootForTenant(ctx, *tenant)
	if err != nil || !ok {
		return err
	}
	_, err = s.enqueueUserDelete(ctx, user, "user:delete:"+user.ID, "")
	return err
}

func (s *worksmobileSyncService) enqueueUserDelete(ctx context.Context, user domain.User, dedupeKey string, rootID string) (*domain.WorksmobileOutbox, error) {
	payload := domain.JSONMap{
		"userExternalKey": user.ID,
		"loginEmail":      user.Email,
	}
	if rootID != "" {
		payload["tenantRootId"] = rootID
	}
	if status := domain.NormalizeUserStatus(user.Status); status != "" {
		payload["baronStatus"] = status
	}
	item := &domain.WorksmobileOutbox{
		ResourceType: domain.WorksmobileResourceUser,
		ResourceID:   user.ID,
		Action:       domain.WorksmobileActionDelete,
		DedupeKey:    dedupeKey,
		Payload:      payload,
	}
	if err := s.outboxRepo.Create(ctx, item); err != nil {
		return nil, err
	}
	return item, nil
}

func (s *worksmobileSyncService) hanmacRoot(ctx context.Context, tenantID string) (*domain.Tenant, error) {
	tenant, err := s.tenantService.GetTenant(ctx, tenantID)
	if err != nil {
		return nil, err
	}
	if tenant.Slug != HanmacFamilyTenantSlug || tenant.ParentID != nil {
		return nil, errors.New("worksmobile is only available for hanmac-family root tenant")
	}
	return tenant, nil
}

func (s *worksmobileSyncService) hanmacSubtree(ctx context.Context, rootID string) ([]domain.Tenant, error) {
	all, _, err := s.tenantService.ListTenants(ctx, 10000, 0, "")
	if err != nil {
		return nil, err
	}
	byParent := map[string][]domain.Tenant{}
	for _, tenant := range all {
		if tenant.ParentID != nil {
			byParent[*tenant.ParentID] = append(byParent[*tenant.ParentID], tenant)
		}
	}
	result := []domain.Tenant{}
	var visit func(id string)
	visit = func(id string) {
		for _, child := range byParent[id] {
			result = append(result, child)
			visit(child.ID)
		}
	}
	visit(rootID)
	sort.Slice(result, func(i, j int) bool {
		return result[i].Name < result[j].Name
	})
	return result, nil
}

func (s *worksmobileSyncService) rootForTenant(ctx context.Context, tenant domain.Tenant) (*domain.Tenant, bool, error) {
	current := tenant
	for current.ParentID != nil && *current.ParentID != "" {
		parent, err := s.tenantService.GetTenant(ctx, *current.ParentID)
		if err != nil {
			return nil, false, err
		}
		current = *parent
	}
	return &current, current.Slug == HanmacFamilyTenantSlug, nil
}

func (s *worksmobileSyncService) validateUserAliasLocalParts(ctx context.Context, root *domain.Tenant, user domain.User, payload WorksmobileUserPayload) error {
	if len(payload.AliasEmails) == 0 {
		return nil
	}
	tenants, err := s.hanmacSubtree(ctx, root.ID)
	if err != nil {
		return err
	}
	tenantByID := make(map[string]domain.Tenant, len(tenants)+1)
	tenantByID[root.ID] = *root
	tenantIDs := make([]string, 0, len(tenants)+1)
	tenantIDs = append(tenantIDs, root.ID)
	for _, tenant := range tenants {
		tenantByID[tenant.ID] = tenant
		if isWorksmobileUserScopeTenant(tenant) {
			tenantIDs = append(tenantIDs, tenant.ID)
		}
	}
	users, err := s.userRepo.FindByTenantIDs(ctx, tenantIDs)
	if err != nil {
		return err
	}
	existing := map[string]string{}
	for _, existingUser := range users {
		if existingUser.ID == user.ID {
			continue
		}
		addWorksmobileLocalPart(existing, existingUser.Email, existingUser.ID)
		if existingUser.TenantID == nil {
			continue
		}
		tenant, ok := tenantByID[*existingUser.TenantID]
		if !ok {
			continue
		}
		for _, alias := range BuildWorksmobileAliasEmails(existingUser, tenant) {
			addWorksmobileLocalPart(existing, alias, existingUser.ID)
		}
	}
	return ValidateWorksmobileAliasLocalParts(payload.Email, payload.AliasEmails, existing)
}

func addWorksmobileLocalPart(target map[string]string, email string, owner string) {
	localPart, err := domain.ExtractNormalizedEmailLocalPart(email)
	if err == nil && localPart != "" {
		target[localPart] = owner
	}
}

func findWorksmobileOrgUnitTenantByRemoteLocalPart(remote WorksmobileRemoteGroup, localTenants []domain.Tenant, tenantByID map[string]domain.Tenant) (domain.Tenant, bool) {
	candidates := worksmobileRemoteGroupLocalPartCandidates(remote)
	for _, tenant := range localTenants {
		if !isWorksmobileOrgUnitTenant(tenant, tenantByID) {
			continue
		}
		if candidates[normalizeWorksmobileSlugLocalPart(tenant.Slug)] {
			return tenant, true
		}
	}
	return domain.Tenant{}, false
}

func isProtectedWorksmobileRemoteOrgUnit(root domain.Tenant, localTenants []domain.Tenant, remote WorksmobileRemoteGroup) bool {
	if strings.TrimSpace(remote.ParentID) == "" {
		return true
	}
	candidates := worksmobileRemoteGroupLocalPartCandidates(remote)
	if len(candidates) == 0 {
		return false
	}
	for _, tenant := range localTenants {
		if tenant.ParentID == nil || *tenant.ParentID != root.ID || tenant.Type != domain.TenantTypeCompany {
			continue
		}
		if candidates[normalizeWorksmobileSlugLocalPart(tenant.Slug)] {
			return true
		}
	}
	return false
}

func worksmobileRemoteGroupLocalPartCandidates(remote WorksmobileRemoteGroup) map[string]bool {
	result := map[string]bool{}
	if localPart := normalizeWorksmobileSlugLocalPart(remote.MailLocalPart); localPart != "" {
		result[localPart] = true
	}
	if localPart, err := domain.ExtractNormalizedEmailLocalPart(remote.Email); err == nil && localPart != "" {
		result[localPart] = true
	}
	return result
}

func normalizeWorksmobileSlugLocalPart(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func isWorksmobileOrgUnitTenant(tenant domain.Tenant, tenantByID map[string]domain.Tenant) bool {
	if tenant.Type == domain.TenantTypeOrganization {
		return true
	}
	if tenant.Type == domain.TenantTypeUserGroup {
		return true
	}
	if tenant.Type == domain.TenantTypeCompany {
		return isWorksmobileBarongroupChildCompany(tenant, tenantByID)
	}
	return false
}

func isWorksmobileUserScopeTenant(tenant domain.Tenant) bool {
	return tenant.Type == domain.TenantTypeCompany || tenant.Type == domain.TenantTypeOrganization || tenant.Type == domain.TenantTypeUserGroup
}

func worksmobileDomainClassificationTenant(tenant domain.Tenant, tenantByID map[string]domain.Tenant) domain.Tenant {
	current := tenant
	for {
		if isWorksmobileDomainRootTenant(current) {
			return current
		}
		parentID := worksmobileTenantParentID(current)
		if parentID == "" {
			return tenant
		}
		parent, ok := tenantByID[parentID]
		if !ok {
			return tenant
		}
		current = parent
	}
}

func isWorksmobileDomainRootTenant(tenant domain.Tenant) bool {
	slug := strings.ToLower(strings.TrimSpace(tenant.Slug))
	switch slug {
	case "saman", "hanmac", "gpdtdc", "baron-group":
		return true
	}
	if tenantHasDomain(tenant, "samaneng.com") ||
		tenantHasDomain(tenant, "hanmaceng.co.kr") ||
		tenantHasDomain(tenant, "baroncs.co.kr") ||
		tenantHasDomain(tenant, "brsw.kr") {
		return true
	}
	name := strings.TrimSpace(tenant.Name)
	return name == "삼안" ||
		name == "한맥기술" ||
		name == "총괄기획&기술개발센터" ||
		name == "바론그룹"
}

func isWorksmobileBarongroupChildCompany(tenant domain.Tenant, tenantByID map[string]domain.Tenant) bool {
	if tenant.Type != domain.TenantTypeCompany || tenant.Slug == "baron-group" {
		return false
	}
	parentID := worksmobileTenantParentID(tenant)
	for parentID != "" {
		parent, ok := tenantByID[parentID]
		if !ok {
			return false
		}
		if parent.Slug == "baron-group" {
			return true
		}
		parentID = worksmobileTenantParentID(parent)
	}
	return false
}

func normalizeWorksmobileOrgUnitParent(payload WorksmobileOrgUnitPayload, tenant domain.Tenant, tenantByID map[string]domain.Tenant, rootID string) WorksmobileOrgUnitPayload {
	if tenant.ParentID != nil && *tenant.ParentID == rootID {
		payload.ParentOrgUnitID = ""
	}
	if tenant.ParentID != nil {
		if parent, ok := tenantByID[*tenant.ParentID]; ok {
			if parent.Slug == "baron-group" || !isWorksmobileOrgUnitTenant(parent, tenantByID) {
				payload.ParentOrgUnitID = ""
			}
		}
	}
	return payload
}

func worksmobileUserOutboxPayload(rootID string, payload WorksmobileUserPayload, statuses ...string) domain.JSONMap {
	outboxPayload := domain.JSONMap{
		"request":         payload,
		"tenantRootId":    rootID,
		"loginEmail":      payload.Email,
		"initialPassword": payload.PasswordConfig.Password,
	}
	if len(statuses) > 0 {
		if status := strings.TrimSpace(statuses[0]); status != "" {
			outboxPayload["baronStatus"] = status
		}
	}
	return outboxPayload
}

func stringValue(value any) string {
	switch v := value.(type) {
	case string:
		return strings.TrimSpace(v)
	default:
		return ""
	}
}

func compareWorksmobileUsers(localUsers []domain.User, remoteUsers []WorksmobileRemoteUser, includeMatched bool, localTenants map[string]domain.Tenant) []WorksmobileComparisonItem {
	remoteByExternalID := map[string]WorksmobileRemoteUser{}
	remoteByEmail := map[string]WorksmobileRemoteUser{}
	for _, remote := range remoteUsers {
		if remote.ExternalID != "" {
			remoteByExternalID[remote.ExternalID] = remote
		}
		if normalizedEmail := strings.ToLower(strings.TrimSpace(remote.Email)); normalizedEmail != "" {
			remoteByEmail[normalizedEmail] = remote
		}
	}
	localByID := map[string]domain.User{}
	matchedRemoteIDs := map[string]bool{}
	excludedLocalIDs := map[string]bool{}
	result := make([]WorksmobileComparisonItem, 0)
	for _, user := range localUsers {
		if !domain.IsWorksProvisionedUserStatus(user.Status) {
			excludedLocalIDs[user.ID] = true
			if remote, ok := remoteByExternalID[user.ID]; ok {
				matchedRemoteIDs[remote.ID] = true
			} else if remote, ok := remoteByEmail[strings.ToLower(strings.TrimSpace(user.Email))]; ok {
				matchedRemoteIDs[remote.ID] = true
			}
			continue
		}
		localByID[user.ID] = user
		remote, matched := remoteByExternalID[user.ID]
		if !matched {
			remote, matched = remoteByEmail[strings.ToLower(strings.TrimSpace(user.Email))]
		}
		if matched && !includeMatched {
			matchedRemoteIDs[remote.ID] = true
			continue
		}
		item := WorksmobileComparisonItem{
			ResourceType:        "USER",
			BaronID:             user.ID,
			BaronName:           user.Name,
			BaronEmail:          user.Email,
			BaronPrimaryOrgID:   worksmobileUserPrimaryOrgID(user),
			BaronPrimaryOrgSlug: worksmobileUserPrimaryOrgSlug(user, localTenants),
			BaronPrimaryOrgName: worksmobileUserPrimaryOrgName(user, localTenants),
			Status:              "missing_in_worksmobile",
		}
		if matched {
			item.Status = "matched"
			item.WorksmobileID = remote.ID
			item.ExternalKey = remote.ExternalID
			item.WorksmobileName = remote.DisplayName
			item.WorksmobileEmail = remote.Email
			item.WorksmobileLevelID = remote.LevelID
			item.WorksmobileLevelName = remote.LevelName
			item.WorksmobileTask = remote.Task
			item.WorksmobileDomainID = remote.DomainID
			item.WorksmobileDomainName = remote.DomainName
			item.WorksmobilePrimaryOrgID = remote.PrimaryOrgUnitID
			item.WorksmobilePrimaryOrgName = remote.PrimaryOrgUnitName
			item.WorksmobilePrimaryOrgPositionID = remote.PrimaryOrgUnitPositionID
			item.WorksmobilePrimaryOrgPositionName = remote.PrimaryOrgUnitPositionName
			item.WorksmobilePrimaryOrgIsManager = remote.PrimaryOrgUnitIsManager
			matchedRemoteIDs[remote.ID] = true
		}
		result = append(result, item)
	}
	for _, remote := range remoteUsers {
		if matchedRemoteIDs[remote.ID] {
			continue
		}
		if excludedLocalIDs[remote.ExternalID] {
			continue
		}
		if remote.ExternalID == "" {
			result = append(result, WorksmobileComparisonItem{
				ResourceType:                      "USER",
				WorksmobileID:                     remote.ID,
				ExternalKey:                       remote.ExternalID,
				WorksmobileName:                   remote.DisplayName,
				WorksmobileEmail:                  remote.Email,
				WorksmobileLevelID:                remote.LevelID,
				WorksmobileLevelName:              remote.LevelName,
				WorksmobileTask:                   remote.Task,
				WorksmobileDomainID:               remote.DomainID,
				WorksmobileDomainName:             remote.DomainName,
				WorksmobilePrimaryOrgID:           remote.PrimaryOrgUnitID,
				WorksmobilePrimaryOrgName:         remote.PrimaryOrgUnitName,
				WorksmobilePrimaryOrgPositionID:   remote.PrimaryOrgUnitPositionID,
				WorksmobilePrimaryOrgPositionName: remote.PrimaryOrgUnitPositionName,
				WorksmobilePrimaryOrgIsManager:    remote.PrimaryOrgUnitIsManager,
				Status:                            "missing_external_key",
			})
			continue
		}
		if _, ok := localByID[remote.ExternalID]; !ok {
			result = append(result, WorksmobileComparisonItem{
				ResourceType:                      "USER",
				WorksmobileID:                     remote.ID,
				ExternalKey:                       remote.ExternalID,
				WorksmobileName:                   remote.DisplayName,
				WorksmobileEmail:                  remote.Email,
				WorksmobileLevelID:                remote.LevelID,
				WorksmobileLevelName:              remote.LevelName,
				WorksmobileTask:                   remote.Task,
				WorksmobileDomainID:               remote.DomainID,
				WorksmobileDomainName:             remote.DomainName,
				WorksmobilePrimaryOrgID:           remote.PrimaryOrgUnitID,
				WorksmobilePrimaryOrgName:         remote.PrimaryOrgUnitName,
				WorksmobilePrimaryOrgPositionID:   remote.PrimaryOrgUnitPositionID,
				WorksmobilePrimaryOrgPositionName: remote.PrimaryOrgUnitPositionName,
				WorksmobilePrimaryOrgIsManager:    remote.PrimaryOrgUnitIsManager,
				Status:                            "missing_in_baron",
			})
		}
	}
	return result
}

func worksmobileUserPrimaryOrgID(user domain.User) string {
	if user.TenantID == nil {
		return ""
	}
	return strings.TrimSpace(*user.TenantID)
}

func worksmobileUserPrimaryOrgName(user domain.User, localTenants map[string]domain.Tenant) string {
	tenantID := worksmobileUserPrimaryOrgID(user)
	if tenantID == "" {
		return ""
	}
	if tenant, ok := localTenants[tenantID]; ok {
		return strings.TrimSpace(tenant.Name)
	}
	if user.Tenant != nil && user.Tenant.ID == tenantID {
		return strings.TrimSpace(user.Tenant.Name)
	}
	return ""
}

func worksmobileUserPrimaryOrgSlug(user domain.User, localTenants map[string]domain.Tenant) string {
	tenantID := worksmobileUserPrimaryOrgID(user)
	if tenantID == "" {
		return ""
	}
	if tenant, ok := localTenants[tenantID]; ok {
		return strings.TrimSpace(tenant.Slug)
	}
	if user.Tenant != nil && user.Tenant.ID == tenantID {
		return strings.TrimSpace(user.Tenant.Slug)
	}
	return ""
}

func compareWorksmobileGroups(localTenants []domain.Tenant, remoteGroups []WorksmobileRemoteGroup, includeMatched bool) []WorksmobileComparisonItem {
	remoteByExternalID := map[string][]WorksmobileRemoteGroup{}
	remoteByID := map[string]WorksmobileRemoteGroup{}
	for _, remote := range remoteGroups {
		if remote.ID != "" {
			remoteByID[remote.ID] = remote
		}
		if remote.ExternalID != "" {
			remoteByExternalID[remote.ExternalID] = append(remoteByExternalID[remote.ExternalID], remote)
		}
	}
	tenantByID := worksmobileTenantByID(localTenants)
	localByID := map[string]domain.Tenant{}
	ignoredLocalByID := map[string]bool{}
	matchedRemoteIDs := map[string]bool{}
	result := make([]WorksmobileComparisonItem, 0)
	for _, tenant := range localTenants {
		if !isWorksmobileOrgUnitTenant(tenant, tenantByID) {
			ignoredLocalByID[tenant.ID] = true
			continue
		}
		localByID[tenant.ID] = tenant
		remote, matched := matchingWorksmobileRemoteGroupForTenant(tenant, remoteByExternalID[tenant.ID], tenantByID)
		item := WorksmobileComparisonItem{
			ResourceType:    "GROUP",
			BaronID:         tenant.ID,
			BaronSlug:       tenant.Slug,
			BaronName:       tenant.Name,
			BaronParentID:   worksmobileTenantParentID(tenant),
			BaronParentSlug: worksmobileTenantParentSlug(tenant, tenantByID),
			BaronParentName: worksmobileTenantParentName(tenant, tenantByID),
			Status:          "missing_in_worksmobile",
		}
		if matched {
			item.Status = "matched"
			item.WorksmobileID = remote.ID
			item.ExternalKey = remote.ExternalID
			item.WorksmobileName = remote.DisplayName
			item.WorksmobileEmail = remote.Email
			item.WorksmobileDomainID = remote.DomainID
			item.WorksmobileDomainName = remote.DomainName
			item.WorksmobileParentID = remote.ParentID
			item.WorksmobileParentName = remote.ParentName
			if parent, ok := tenantByID[item.BaronParentID]; ok {
				if parentRemote, ok := matchingWorksmobileRemoteGroupForTenant(parent, remoteByExternalID[item.BaronParentID], tenantByID); ok {
					item.BaronParentWorksmobileID = parentRemote.ID
					item.BaronParentWorksmobileName = parentRemote.DisplayName
					item.BaronParentWorksmobileEmail = parentRemote.Email
				}
			} else if parentRemote, ok := firstWorksmobileRemoteGroup(remoteByExternalID[item.BaronParentID]); ok {
				item.BaronParentWorksmobileID = parentRemote.ID
				item.BaronParentWorksmobileName = parentRemote.DisplayName
				item.BaronParentWorksmobileEmail = parentRemote.Email
			}
			if parentRemote, ok := remoteByID[remote.ParentID]; ok {
				if item.WorksmobileParentName == "" {
					item.WorksmobileParentName = parentRemote.DisplayName
				}
				item.WorksmobileParentEmail = parentRemote.Email
				item.WorksmobileParentExternalKey = parentRemote.ExternalID
			}
			item = fillWorksmobileParentFromBaronParentMatch(item)
			if worksmobileGroupNeedsUpdate(tenant, remote, remoteByID, remoteByExternalID, tenantByID) {
				item.Status = "needs_update"
			}
			matchedRemoteIDs[remote.ID] = true
		}
		if matched && item.Status == "matched" && !includeMatched {
			continue
		}
		result = append(result, item)
	}
	for _, remote := range remoteGroups {
		if matchedRemoteIDs[remote.ID] {
			continue
		}
		if remote.ExternalID == "" {
			result = append(result, WorksmobileComparisonItem{
				ResourceType:          "GROUP",
				WorksmobileID:         remote.ID,
				ExternalKey:           remote.ExternalID,
				WorksmobileName:       remote.DisplayName,
				WorksmobileEmail:      remote.Email,
				WorksmobileDomainID:   remote.DomainID,
				WorksmobileDomainName: remote.DomainName,
				WorksmobileParentID:   remote.ParentID,
				WorksmobileParentName: remote.ParentName,
				Status:                "missing_external_key",
			})
			if parentRemote, ok := remoteByID[remote.ParentID]; ok {
				last := &result[len(result)-1]
				if last.WorksmobileParentName == "" {
					last.WorksmobileParentName = parentRemote.DisplayName
				}
				last.WorksmobileParentEmail = parentRemote.Email
				last.WorksmobileParentExternalKey = parentRemote.ExternalID
			}
			continue
		}
		if ignoredLocalByID[remote.ExternalID] {
			continue
		}
		if _, ok := localByID[remote.ExternalID]; !ok {
			result = append(result, WorksmobileComparisonItem{
				ResourceType:          "GROUP",
				WorksmobileID:         remote.ID,
				ExternalKey:           remote.ExternalID,
				WorksmobileName:       remote.DisplayName,
				WorksmobileEmail:      remote.Email,
				WorksmobileDomainID:   remote.DomainID,
				WorksmobileDomainName: remote.DomainName,
				WorksmobileParentID:   remote.ParentID,
				WorksmobileParentName: remote.ParentName,
				Status:                "missing_in_baron",
			})
			if parentRemote, ok := remoteByID[remote.ParentID]; ok {
				last := &result[len(result)-1]
				if last.WorksmobileParentName == "" {
					last.WorksmobileParentName = parentRemote.DisplayName
				}
				last.WorksmobileParentEmail = parentRemote.Email
				last.WorksmobileParentExternalKey = parentRemote.ExternalID
			}
		}
	}
	return result
}

func matchingWorksmobileRemoteGroupForTenant(tenant domain.Tenant, remotes []WorksmobileRemoteGroup, tenantByID map[string]domain.Tenant) (WorksmobileRemoteGroup, bool) {
	if len(remotes) == 0 {
		return WorksmobileRemoteGroup{}, false
	}
	expectedDomainID, hasExpectedDomainID := expectedWorksmobileDomainIDForTenant(tenant, tenantByID)
	if !hasExpectedDomainID {
		return remotes[0], true
	}
	var unknownDomain WorksmobileRemoteGroup
	hasUnknownDomain := false
	for i := range remotes {
		remote := remotes[i]
		if remote.DomainID == expectedDomainID {
			return remote, true
		}
		if remote.DomainID == 0 && !hasUnknownDomain {
			unknownDomain = remote
			hasUnknownDomain = true
		}
	}
	if hasUnknownDomain {
		return unknownDomain, true
	}
	return WorksmobileRemoteGroup{}, false
}

func firstWorksmobileRemoteGroup(remotes []WorksmobileRemoteGroup) (WorksmobileRemoteGroup, bool) {
	if len(remotes) == 0 {
		return WorksmobileRemoteGroup{}, false
	}
	return remotes[0], true
}

func expectedWorksmobileDomainIDForTenant(tenant domain.Tenant, tenantByID map[string]domain.Tenant) (int64, bool) {
	domainTenant := worksmobileDomainClassificationTenant(tenant, tenantByID)
	domainID, err := ResolveWorksmobileDomainIDFromTenant(domainTenant, nil)
	if err != nil || domainID <= 0 {
		return 0, false
	}
	return domainID, true
}

func worksmobileGroupNeedsUpdate(tenant domain.Tenant, remote WorksmobileRemoteGroup, remoteByID map[string]WorksmobileRemoteGroup, remoteByExternalID map[string][]WorksmobileRemoteGroup, tenantByID map[string]domain.Tenant) bool {
	if strings.TrimSpace(tenant.Name) != strings.TrimSpace(remote.DisplayName) {
		return true
	}

	expectedParentExternalKey := expectedWorksmobileParentExternalKey(tenant, remoteByExternalID, tenantByID)
	actualParentExternalKey := ""
	if remote.ParentID != "" {
		actualParentExternalKey = strings.TrimSpace(remoteByID[remote.ParentID].ExternalID)
	}
	return expectedParentExternalKey != actualParentExternalKey
}

func expectedWorksmobileParentExternalKey(tenant domain.Tenant, remoteByExternalID map[string][]WorksmobileRemoteGroup, tenantByID map[string]domain.Tenant) string {
	parentID := worksmobileTenantParentID(tenant)
	if parentID == "" {
		return ""
	}
	if parent, ok := tenantByID[parentID]; ok && parent.Slug == "baron-group" {
		return ""
	}
	parent, ok := tenantByID[parentID]
	if !ok {
		return ""
	}
	if _, ok := matchingWorksmobileRemoteGroupForTenant(parent, remoteByExternalID[parentID], tenantByID); !ok {
		return ""
	}
	return parentID
}

func fillWorksmobileParentFromBaronParentMatch(item WorksmobileComparisonItem) WorksmobileComparisonItem {
	if item.WorksmobileParentID == "" || item.WorksmobileParentID != item.BaronParentWorksmobileID {
		return item
	}
	if item.WorksmobileParentName == "" {
		item.WorksmobileParentName = item.BaronParentWorksmobileName
	}
	if item.WorksmobileParentEmail == "" {
		item.WorksmobileParentEmail = item.BaronParentWorksmobileEmail
	}
	if item.WorksmobileParentExternalKey == "" {
		item.WorksmobileParentExternalKey = item.BaronParentID
	}
	return item
}

func worksmobileTenantByID(tenants []domain.Tenant) map[string]domain.Tenant {
	result := make(map[string]domain.Tenant, len(tenants))
	for _, tenant := range tenants {
		result[tenant.ID] = tenant
	}
	return result
}

func worksmobileTenantParentID(tenant domain.Tenant) string {
	if tenant.ParentID == nil {
		return ""
	}
	return strings.TrimSpace(*tenant.ParentID)
}

func worksmobileTenantParentName(tenant domain.Tenant, tenantByID map[string]domain.Tenant) string {
	parentID := worksmobileTenantParentID(tenant)
	if parentID == "" {
		return ""
	}
	return strings.TrimSpace(tenantByID[parentID].Name)
}

func worksmobileTenantParentSlug(tenant domain.Tenant, tenantByID map[string]domain.Tenant) string {
	parentID := worksmobileTenantParentID(tenant)
	if parentID == "" {
		return ""
	}
	return strings.TrimSpace(tenantByID[parentID].Slug)
}

func worksmobileSyncScopeUsers(users []domain.User) []domain.User {
	if len(users) == 0 {
		return users
	}
	filtered := make([]domain.User, 0, len(users))
	for _, user := range users {
		if !domain.IsWorksProvisionedUserStatus(user.Status) {
			continue
		}
		filtered = append(filtered, user)
	}
	return filtered
}
