package service

import (
	"baron-sso-backend/internal/domain"
	"context"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestWorksmobileSyncServiceRejectsAliasLocalPartAlreadyUsedByOtherUser(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	tenantID := "saman-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	tenant := domain.Tenant{
		ID:       tenantID,
		Slug:     "saman",
		Name:     "삼안",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	target := domain.User{
		ID:       "target-user",
		Email:    "target@samaneng.com",
		Name:     "Target",
		TenantID: &tenantID,
		Metadata: domain.JSONMap{
			"aliasEmails": []any{"used@hanmaceng.co.kr"},
		},
	}
	existing := domain.User{
		ID:       "existing-user",
		Email:    "used@samaneng.com",
		Name:     "Existing",
		TenantID: &tenantID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, tenantID: tenant}, list: []domain.Tenant{root, tenant}},
		&fakeWorksmobileUserRepo{byID: map[string]domain.User{target.ID: target}, byTenant: []domain.User{target, existing}},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueUserSync(context.Background(), rootID, target.ID)

	require.Nil(t, item)
	require.Error(t, err)
	require.Contains(t, err.Error(), "이미 사용 중")
	require.Empty(t, outboxRepo.created)
}

func TestWorksmobileSyncServiceEnqueuesSuspendedUserStatusWithOrganizations(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	tenantID := "saman-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "Hanmac Family",
	}
	tenant := domain.Tenant{
		ID:       tenantID,
		Slug:     "saman",
		Name:     "Saman",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	target := domain.User{
		ID:       "target-user",
		Email:    "target@samaneng.com",
		Name:     "Target",
		Status:   domain.UserStatusSuspended,
		TenantID: &tenantID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, tenantID: tenant}, list: []domain.Tenant{root, tenant}},
		&fakeWorksmobileUserRepo{byID: map[string]domain.User{target.ID: target}, byTenant: []domain.User{target}},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueUserSync(context.Background(), rootID, target.ID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, domain.WorksmobileActionSuspend, outboxRepo.created[0].Action)
	require.Equal(t, domain.UserStatusSuspended, outboxRepo.created[0].Payload["baronStatus"])
	request, ok := outboxRepo.created[0].Payload["request"].(WorksmobileUserPayload)
	require.True(t, ok)
	require.NotEmpty(t, request.Organizations)
	require.Equal(t, "target@samaneng.com", outboxRepo.created[0].Payload["loginEmail"])
}

func TestWorksmobileSyncServiceDeprovisionsArchivedUser(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	tenantID := "saman-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "Hanmac Family",
	}
	tenant := domain.Tenant{
		ID:       tenantID,
		Slug:     "saman",
		Name:     "Saman",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	target := domain.User{
		ID:       "archived-user",
		Email:    "archived@samaneng.com",
		Name:     "Archived",
		Status:   domain.UserStatusArchived,
		TenantID: &tenantID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, tenantID: tenant}, list: []domain.Tenant{root, tenant}},
		&fakeWorksmobileUserRepo{byID: map[string]domain.User{target.ID: target}, byTenant: []domain.User{target}},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueUserSync(context.Background(), rootID, target.ID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Equal(t, domain.WorksmobileActionDelete, item.Action)
	require.Len(t, outboxRepo.created, 1)

	err = service.EnqueueUserUpsertIfInScope(context.Background(), target)
	require.NoError(t, err)
	require.Len(t, outboxRepo.created, 2)
	require.Equal(t, domain.WorksmobileActionDelete, outboxRepo.created[1].Action)
}

func TestWorksmobileSyncServiceOverviewExposesAdminTenantIDForPasswordManageLink(t *testing.T) {
	t.Setenv("WORKS_ADMIN_TENANT_ID", "works-tenant-1")
	root := domain.Tenant{
		ID:   "root-tenant",
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{root.ID: root}},
		&fakeWorksmobileUserRepo{},
		&fakeWorksmobileOutboxRepo{},
		nil,
	)

	overview, err := service.GetTenantOverview(context.Background(), root.ID)

	require.NoError(t, err)
	require.Equal(t, "works-tenant-1", overview.Config.AdminTenantID)
}

func TestCompareWorksmobileGroupsUsesOrganizationsAndBarongroupChildCompanies(t *testing.T) {
	parentID := "root-tenant"
	root := domain.Tenant{
		ID:   parentID,
		Name: "한맥가족",
		Slug: HanmacFamilyTenantSlug,
		Type: domain.TenantTypeCompanyGroup,
	}
	hanmac := domain.Tenant{
		ID:       "hanmac-tenant",
		Name:     "한맥기술",
		Slug:     "hanmac",
		Type:     domain.TenantTypeCompany,
		ParentID: &parentID,
	}
	barongroup := domain.Tenant{
		ID:       "barongroup-tenant",
		Name:     "바론그룹",
		Slug:     "baron-group",
		Type:     domain.TenantTypeCompany,
		ParentID: &parentID,
	}
	barongroupChildCompany := domain.Tenant{
		ID:       "barongroup-child-company",
		Name:     "바론그룹 하위 회사",
		Type:     domain.TenantTypeCompany,
		ParentID: &barongroup.ID,
	}
	organization := domain.Tenant{
		ID:       "organization-tenant",
		Name:     "정규 조직",
		Type:     domain.TenantTypeOrganization,
		ParentID: &hanmac.ID,
	}
	userGroup := domain.Tenant{
		ID:       "legacy-user-group-tenant",
		Name:     "사용자 그룹 조직",
		Type:     domain.TenantTypeUserGroup,
		ParentID: &hanmac.ID,
	}

	items := compareWorksmobileGroups(
		[]domain.Tenant{root, hanmac, barongroup, barongroupChildCompany, organization, userGroup},
		[]WorksmobileRemoteGroup{
			{ID: "works-root", ExternalID: root.ID, DisplayName: root.Name},
			{ID: "works-hanmac", ExternalID: hanmac.ID, DisplayName: hanmac.Name, Email: "hanmac@hanmaceng.co.kr"},
			{ID: "works-barongroup", ExternalID: barongroup.ID, DisplayName: barongroup.Name},
			{ID: "works-barongroup-child", ExternalID: barongroupChildCompany.ID, DisplayName: barongroupChildCompany.Name},
			{ID: "works-organization", ExternalID: organization.ID, DisplayName: organization.Name, ParentID: "works-hanmac"},
			{ID: "works-user-group", ExternalID: userGroup.ID, DisplayName: userGroup.Name, ParentID: "works-hanmac"},
			{ID: "works-orphan", ExternalID: "works-orphan", DisplayName: "WORKS 전용 조직"},
		},
		true,
	)

	require.Len(t, items, 4)
	require.Equal(t, barongroupChildCompany.ID, items[0].BaronID)
	require.Equal(t, "matched", items[0].Status)
	require.Equal(t, organization.ID, items[1].BaronID)
	require.Equal(t, "matched", items[1].Status)
	require.Equal(t, "works-hanmac", items[1].WorksmobileParentID)
	require.Equal(t, hanmac.Name, items[1].WorksmobileParentName)
	require.Equal(t, "hanmac@hanmaceng.co.kr", items[1].WorksmobileParentEmail)
	require.Equal(t, hanmac.ID, items[1].WorksmobileParentExternalKey)
	require.Equal(t, "works-hanmac", items[1].BaronParentWorksmobileID)
	require.Equal(t, hanmac.Name, items[1].BaronParentWorksmobileName)
	require.Equal(t, "hanmac@hanmaceng.co.kr", items[1].BaronParentWorksmobileEmail)
	require.Equal(t, userGroup.ID, items[2].BaronID)
	require.Equal(t, "matched", items[2].Status)
	require.Equal(t, "works-orphan", items[3].ExternalKey)
	require.Equal(t, "missing_in_baron", items[3].Status)
}

func TestCompareWorksmobileGroupsShowsUserGroupMissingInWorksmobile(t *testing.T) {
	parentID := "company-tenant"
	userGroup := domain.Tenant{
		ID:       "team-tenant",
		Name:     "신규 팀",
		Slug:     "new-team",
		Type:     domain.TenantTypeUserGroup,
		ParentID: &parentID,
	}

	items := compareWorksmobileGroups(
		[]domain.Tenant{
			{ID: parentID, Slug: "company", Name: "계열사", Type: domain.TenantTypeCompany},
			userGroup,
		},
		nil,
		false,
	)

	require.Len(t, items, 1)
	require.Equal(t, userGroup.ID, items[0].BaronID)
	require.Equal(t, "missing_in_worksmobile", items[0].Status)
}

func TestCompareWorksmobileGroupsMarksMatchedOrgUnitNeedsUpdate(t *testing.T) {
	parentID := "parent-tenant"
	tenant := domain.Tenant{
		ID:       "team-tenant",
		Name:     "변경된 팀명",
		Slug:     "team",
		Type:     domain.TenantTypeUserGroup,
		ParentID: &parentID,
	}

	items := compareWorksmobileGroups(
		[]domain.Tenant{
			{ID: parentID, Slug: "parent", Name: "상위 조직", Type: domain.TenantTypeUserGroup},
			tenant,
		},
		[]WorksmobileRemoteGroup{
			{ID: "works-parent", ExternalID: parentID, DisplayName: "상위 조직"},
			{ID: "works-team", ExternalID: tenant.ID, DisplayName: "이전 팀명", ParentID: "works-parent"},
		},
		false,
	)

	require.Len(t, items, 1)
	require.Equal(t, tenant.ID, items[0].BaronID)
	require.Equal(t, "needs_update", items[0].Status)
}

func TestCompareWorksmobileGroupsCoversHanmacOrganizationRegressionIDs(t *testing.T) {
	rootID := "038326b6-954a-48a7-a85f-efd83f62b82a"
	samanID := "9caf62e1-297d-4e8f-870b-61780998bbeb"
	hanmacID := "369c1843-56af-4344-9c21-0e01197ab861"
	baronGroupID := "96369f12-6b66-4b2a-a916-d1c99d326f02"
	changedID := "818c856b-9545-442f-b827-d1c569f200b0"
	hanmacOnlyID := "2d217948-9c5a-42ea-805b-eef9c7421775"
	baronOnlyID := "32464fd6-da51-473f-844a-ab88603ad1f0"
	localTenants := []domain.Tenant{
		{ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", Type: domain.TenantTypeCompanyGroup},
		{ID: samanID, Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID},
		{ID: hanmacID, Slug: "hanmac", Name: "한맥기술", Type: domain.TenantTypeCompany, ParentID: &rootID},
		{ID: baronGroupID, Slug: "baron-group", Name: "바론그룹", Type: domain.TenantTypeCompanyGroup, ParentID: &rootID},
		{ID: changedID, Slug: "rnd-saman", Name: "삼안기술개발센터(조직도용)", Type: domain.TenantTypeOrganization, ParentID: &samanID},
		{ID: hanmacOnlyID, Slug: "rnd-hanmac", Name: "한맥기술개발센터(조직도용)", Type: domain.TenantTypeOrganization, ParentID: &hanmacID},
		{ID: baronOnlyID, Slug: "rnd-baron", Name: "바론기술개발센터(조직도용)", Type: domain.TenantTypeOrganization, ParentID: &baronGroupID},
	}
	remoteGroups := []WorksmobileRemoteGroup{
		{ID: "works-saman", ExternalID: samanID, DisplayName: "삼안"},
		{ID: "works-hanmac", ExternalID: hanmacID, DisplayName: "한맥기술"},
		{
			ID:          "works-rnd-saman",
			ExternalID:  changedID,
			DisplayName: "삼안기술개발센터(조직도용)",
		},
	}

	items := compareWorksmobileGroups(localTenants, remoteGroups, false)
	itemsByBaronID := map[string]WorksmobileComparisonItem{}
	for _, item := range items {
		itemsByBaronID[item.BaronID] = item
	}

	require.Equal(t, "needs_update", itemsByBaronID[changedID].Status)
	require.Equal(t, "missing_in_worksmobile", itemsByBaronID[hanmacOnlyID].Status)
	require.Equal(t, "missing_in_worksmobile", itemsByBaronID[baronOnlyID].Status)
}

func TestCompareWorksmobileGroupsDoesNotMatchBaronGroupOrganizationInGPDTDCDomain(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "038326b6-954a-48a7-a85f-efd83f62b82a"
	baronGroupID := "96369f12-6b66-4b2a-a916-d1c99d326f02"
	orgID := "32464fd6-da51-473f-844a-ab88603ad1f0"
	localTenants := []domain.Tenant{
		{ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", Type: domain.TenantTypeCompanyGroup},
		{ID: baronGroupID, Slug: "baron-group", Name: "바론그룹", Type: domain.TenantTypeCompanyGroup, ParentID: &rootID},
		{ID: orgID, Slug: "rnd-baron", Name: "바론기술개발센터(조직도용)", Type: domain.TenantTypeOrganization, ParentID: &baronGroupID},
	}
	remoteGroups := []WorksmobileRemoteGroup{
		{
			ID:          "works-rnd-baron-gpdtdc",
			ExternalID:  orgID,
			DisplayName: "바론기술개발센터(조직도용)",
			DomainID:    1003,
			DomainName:  "총괄기획&기술개발센터",
		},
	}

	items := compareWorksmobileGroups(localTenants, remoteGroups, false)

	require.Len(t, items, 1)
	require.Equal(t, orgID, items[0].BaronID)
	require.Equal(t, "missing_in_worksmobile", items[0].Status)
	require.Empty(t, items[0].WorksmobileID)
}

func TestCompareWorksmobileGroupsMatchesBaronGroupOrganizationInBaronGroupDomain(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "038326b6-954a-48a7-a85f-efd83f62b82a"
	baronGroupID := "96369f12-6b66-4b2a-a916-d1c99d326f02"
	orgID := "32464fd6-da51-473f-844a-ab88603ad1f0"
	localTenants := []domain.Tenant{
		{ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", Type: domain.TenantTypeCompanyGroup},
		{ID: baronGroupID, Slug: "baron-group", Name: "바론그룹", Type: domain.TenantTypeCompanyGroup, ParentID: &rootID},
		{ID: orgID, Slug: "rnd-baron", Name: "바론기술개발센터(조직도용)", Type: domain.TenantTypeOrganization, ParentID: &baronGroupID},
	}
	remoteGroups := []WorksmobileRemoteGroup{
		{
			ID:          "works-rnd-baron",
			ExternalID:  orgID,
			DisplayName: "바론기술개발센터(조직도용)",
			DomainID:    1004,
			DomainName:  "바론그룹",
		},
	}

	diffOnly := compareWorksmobileGroups(localTenants, remoteGroups, false)
	all := compareWorksmobileGroups(localTenants, remoteGroups, true)

	require.Empty(t, diffOnly)
	require.Len(t, all, 1)
	require.Equal(t, orgID, all[0].BaronID)
	require.Equal(t, "matched", all[0].Status)
	require.Equal(t, int64(1004), all[0].WorksmobileDomainID)
}

func TestWorksmobileSyncServiceRejectsDomainCompanyOrgUnitSync(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	companyID := "company-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Slug:     "saman",
		Name:     "삼안",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company}, list: []domain.Tenant{root, company}},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, companyID)

	require.Nil(t, item)
	require.Error(t, err)
	require.Contains(t, err.Error(), "worksmobile orgunit tenant")
	require.Empty(t, outboxRepo.created)
}

func TestWorksmobileSyncServiceEnqueuesBarongroupChildCompanyOrgUnitSync(t *testing.T) {
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "root-tenant"
	barongroupID := "barongroup-tenant"
	companyID := "barongroup-child-company"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	barongroup := domain.Tenant{
		ID:       barongroupID,
		Slug:     "baron-group",
		Name:     "바론그룹",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
	}
	company := domain.Tenant{
		ID:       companyID,
		Slug:     "barongroup-child",
		Name:     "바론그룹 하위 회사",
		Type:     domain.TenantTypeCompany,
		ParentID: &barongroupID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, barongroupID: barongroup, companyID: company}, list: []domain.Tenant{root, barongroup, company}},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, companyID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, companyID, request.OrgUnitExternalKey)
	require.Empty(t, request.ParentOrgUnitID)
}

func TestWorksmobileSyncServiceEnqueuesOrganizationOrgUnitSync(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	companyID := "company-tenant"
	organizationID := "organization-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Slug:     "saman",
		Name:     "삼안",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	organization := domain.Tenant{
		ID:       organizationID,
		Slug:     "engineering",
		Name:     "기술본부",
		Type:     domain.TenantTypeOrganization,
		ParentID: &companyID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{
			tenants: map[string]domain.Tenant{rootID: root, companyID: company, organizationID: organization},
			list:    []domain.Tenant{root, company, organization},
		},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organizationID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, organizationID, request.OrgUnitExternalKey)
	require.Empty(t, request.ParentOrgUnitID)
	require.Equal(t, 1, request.DisplayOrder)
}

func TestWorksmobileSyncServiceEnqueuesExternalKeyMissingOrgUnitDelete(t *testing.T) {
	rootID := "root-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	client := &fakeWorksmobileDirectoryClient{
		groups: []WorksmobileRemoteGroup{
			{
				ID:          "works-org-1",
				DisplayName: "WORKS 전용 조직",
				DomainID:    1001,
				ParentID:    "works-parent",
			},
		},
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		client,
	)

	item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1")

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, domain.WorksmobileActionDelete, outboxRepo.created[0].Action)
	require.Equal(t, "works-org-1", outboxRepo.created[0].Payload["worksmobileId"])
}

func TestWorksmobileSyncServiceEnqueuesExternalKeyPresentWorksOnlyOrgUnitDelete(t *testing.T) {
	rootID := "root-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	client := &fakeWorksmobileDirectoryClient{
		groups: []WorksmobileRemoteGroup{
			{
				ID:         "works-org-1",
				ExternalID: "baron-tenant-1",
				ParentID:   "works-parent",
			},
		},
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		client,
	)

	item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1")

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, domain.WorksmobileActionDelete, outboxRepo.created[0].Action)
	require.Equal(t, "baron-tenant-1", outboxRepo.created[0].Payload["externalKey"])
}

func TestWorksmobileSyncServiceReconcilesWorksOnlyOrgUnitBySlugLocalPart(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	orgID := "baron-org-1"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	organization := domain.Tenant{
		ID:       orgID,
		Slug:     "tech-dev-center",
		Name:     "기술개발센터",
		Type:     domain.TenantTypeOrganization,
		ParentID: &rootID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	client := &fakeWorksmobileDirectoryClient{
		groups: []WorksmobileRemoteGroup{
			{
				ID:            "works-org-1",
				ExternalID:    "legacy-external-key",
				DisplayName:   "기술개발센터",
				MailLocalPart: "tech-dev-center",
				DomainID:      1001,
				ParentID:      "works-parent",
			},
		},
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{
			tenants: map[string]domain.Tenant{rootID: root, orgID: organization},
			list:    []domain.Tenant{root, organization},
		},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		client,
	)

	item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1")

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, domain.WorksmobileActionUpsert, outboxRepo.created[0].Action)
	require.Equal(t, orgID, outboxRepo.created[0].ResourceID)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, orgID, request.OrgUnitExternalKey)
	require.Equal(t, "tech-dev-center", outboxRepo.created[0].Payload["matchLocalPart"])
}

func TestCompareWorksmobileGroupsFillsParentDisplayFromBaronParentMatch(t *testing.T) {
	rootID := "root-tenant"
	parent := domain.Tenant{
		ID:       "parent-tenant",
		Name:     "삼안",
		Slug:     "saman",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
	}
	child := domain.Tenant{
		ID:       "child-tenant",
		Name:     "업무",
		Slug:     "operations",
		Type:     domain.TenantTypeOrganization,
		ParentID: &parent.ID,
	}

	items := compareWorksmobileGroups(
		[]domain.Tenant{
			{ID: rootID, Name: "한맥가족", Slug: HanmacFamilyTenantSlug, Type: domain.TenantTypeCompanyGroup},
			parent,
			child,
		},
		[]WorksmobileRemoteGroup{
			{ID: "works-parent", ExternalID: parent.ID, DisplayName: "삼안", Email: "saman@samaneng.com"},
			{ID: "works-child", ExternalID: child.ID, DisplayName: "업무", ParentID: "works-parent"},
		},
		true,
	)

	require.Len(t, items, 1)
	require.Equal(t, child.ID, items[0].BaronID)
	require.Equal(t, "works-parent", items[0].WorksmobileParentID)
	require.Equal(t, "삼안", items[0].WorksmobileParentName)
	require.Equal(t, "saman@samaneng.com", items[0].WorksmobileParentEmail)
	require.Equal(t, parent.ID, items[0].WorksmobileParentExternalKey)
}

func TestWorksmobileSyncServiceReconcilesTopLevelWorksOnlyOrgUnitBeforeProtectedDeleteGuard(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	rootID := "root-tenant"
	orgID := "baron-operations"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	samanID := "saman-tenant"
	saman := domain.Tenant{
		ID:       samanID,
		Slug:     "saman",
		Name:     "삼안",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
	}
	organization := domain.Tenant{
		ID:       orgID,
		Slug:     "operations",
		Name:     "업무",
		Type:     domain.TenantTypeOrganization,
		ParentID: &samanID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	client := &fakeWorksmobileDirectoryClient{
		groups: []WorksmobileRemoteGroup{
			{
				ID:            "works-operations",
				ExternalID:    "legacy-operations-id",
				DisplayName:   "업무팀",
				Email:         "operations@samaneng.com",
				MailLocalPart: "operations",
				DomainID:      1001,
			},
		},
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{
			tenants: map[string]domain.Tenant{rootID: root, samanID: saman, orgID: organization},
			list:    []domain.Tenant{root, saman, organization},
		},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		client,
	)

	item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-operations")

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, domain.WorksmobileActionUpsert, outboxRepo.created[0].Action)
	require.Equal(t, orgID, outboxRepo.created[0].ResourceID)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, orgID, request.OrgUnitExternalKey)
	require.Equal(t, "operations", outboxRepo.created[0].Payload["matchLocalPart"])
}

func TestWorksmobileSyncServiceRejectsProtectedDomainRootOrgUnitDelete(t *testing.T) {
	rootID := "root-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	client := &fakeWorksmobileDirectoryClient{
		groups: []WorksmobileRemoteGroup{
			{
				ID:          "works-root",
				DisplayName: "한맥기술",
			},
		},
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}, list: []domain.Tenant{root}},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		client,
	)

	item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-root")

	require.Nil(t, item)
	require.Error(t, err)
	require.Contains(t, err.Error(), "protected worksmobile domain root")
	require.Empty(t, outboxRepo.created)
}

func TestWorksmobileSyncServiceTreatsHanmacFamilyChildCompaniesAsDomainRoots(t *testing.T) {
	t.Setenv("SAMAN_DOMAIN_ID", "1001")
	t.Setenv("HANMAC_DOMAIN_ID", "1002")
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "root-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	tests := []struct {
		name         string
		company      domain.Tenant
		organization domain.Tenant
		wantDomainID int64
		wantEmail    string
	}{
		{
			name: "saman",
			company: domain.Tenant{
				ID:       "company-saman",
				Slug:     "saman",
				Name:     "삼안",
				Type:     domain.TenantTypeCompany,
				ParentID: &rootID,
				Domains:  []domain.TenantDomain{{Domain: "samaneng.com"}},
			},
			organization: domain.Tenant{
				ID:   "org-saman-planning",
				Slug: "saman-planning",
				Name: "삼안 기획팀",
				Type: domain.TenantTypeOrganization,
			},
			wantDomainID: 1001,
			wantEmail:    "saman-planning@samaneng.com",
		},
		{
			name: "hanmac",
			company: domain.Tenant{
				ID:       "company-hanmac",
				Slug:     "hanmac",
				Name:     "한맥기술",
				Type:     domain.TenantTypeCompany,
				ParentID: &rootID,
				Domains:  []domain.TenantDomain{{Domain: "hanmaceng.co.kr"}},
			},
			organization: domain.Tenant{
				ID:   "org-hanmac-planning",
				Slug: "hanmac-planning",
				Name: "한맥 기획팀",
				Type: domain.TenantTypeOrganization,
			},
			wantDomainID: 1002,
			wantEmail:    "hanmac-planning@hanmaceng.co.kr",
		},
		{
			name: "gpdtdc",
			company: domain.Tenant{
				ID:       "company-gpdtdc",
				Slug:     "gpdtdc",
				Name:     "총괄기획&기술개발센터",
				Type:     domain.TenantTypeCompany,
				ParentID: &rootID,
			},
			organization: domain.Tenant{
				ID:   "org-gpdtdc-planning",
				Slug: "gpdtdc-planning",
				Name: "총괄 기획팀",
				Type: domain.TenantTypeOrganization,
			},
			wantDomainID: 1003,
			wantEmail:    "gpdtdc-planning@baroncs.co.kr",
		},
		{
			name: "baron-group",
			company: domain.Tenant{
				ID:       "company-barongroup",
				Slug:     "baron-group",
				Name:     "바론그룹",
				Type:     domain.TenantTypeCompany,
				ParentID: &rootID,
			},
			organization: domain.Tenant{
				ID:   "org-baron-planning",
				Slug: "baron-planning",
				Name: "바론 기획팀",
				Type: domain.TenantTypeOrganization,
			},
			wantDomainID: 1004,
			wantEmail:    "baron-planning@brsw.kr",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			organization := tt.organization
			organization.ParentID = &tt.company.ID
			outboxRepo := &fakeWorksmobileOutboxRepo{}
			service := NewWorksmobileSyncService(
				&fakeWorksmobileTenantService{
					tenants: map[string]domain.Tenant{
						rootID:          root,
						tt.company.ID:   tt.company,
						organization.ID: organization,
					},
					list: []domain.Tenant{root, tt.company, organization},
				},
				&fakeWorksmobileUserRepo{},
				outboxRepo,
				nil,
			)

			item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organization.ID)

			require.NoError(t, err)
			require.NotNil(t, item)
			require.Len(t, outboxRepo.created, 1)
			request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
			require.Equal(t, organization.ID, request.OrgUnitExternalKey)
			require.Equal(t, tt.wantDomainID, request.DomainID)
			require.Equal(t, tt.wantEmail, request.Email)
			require.Empty(t, request.ParentOrgUnitID)
		})
	}
}

func TestWorksmobileSyncServiceUsesBaronGroupDomainForBaronGroupChildOrganization(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "038326b6-954a-48a7-a85f-efd83f62b82a"
	baronGroupID := "96369f12-6b66-4b2a-a916-d1c99d326f02"
	orgID := "32464fd6-da51-473f-844a-ab88603ad1f0"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
		Type: domain.TenantTypeCompanyGroup,
	}
	baronGroup := domain.Tenant{
		ID:       baronGroupID,
		Slug:     "baron-group",
		Name:     "바론그룹",
		Type:     domain.TenantTypeCompanyGroup,
		ParentID: &rootID,
	}
	organization := domain.Tenant{
		ID:       orgID,
		Slug:     "rnd-baron",
		Name:     "바론기술개발센터(조직도용)",
		Type:     domain.TenantTypeOrganization,
		ParentID: &baronGroupID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{
			tenants: map[string]domain.Tenant{
				root.ID:         root,
				baronGroup.ID:   baronGroup,
				organization.ID: organization,
			},
			list: []domain.Tenant{root, baronGroup, organization},
		},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organization.ID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, int64(1004), request.DomainID)
	require.Equal(t, "rnd-baron@brsw.kr", request.Email)
}

func TestWorksmobileSyncServiceUsesGPDTDCDomainForGPDTDCChildOrganization(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
	rootID := "038326b6-954a-48a7-a85f-efd83f62b82a"
	gpdtdcID := "5530ca6e-c5e6-4bf0-84d6-76c6a8fb70ee"
	orgID := "gpdtdc-child-organization"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
		Type: domain.TenantTypeCompanyGroup,
	}
	gpdtdc := domain.Tenant{
		ID:       gpdtdcID,
		Slug:     "gpdtdc",
		Name:     "총괄기획&기술개발센터",
		Type:     domain.TenantTypeOrganization,
		ParentID: &rootID,
	}
	organization := domain.Tenant{
		ID:       orgID,
		Slug:     "planning",
		Name:     "기획",
		Type:     domain.TenantTypeOrganization,
		ParentID: &gpdtdcID,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{
			tenants: map[string]domain.Tenant{
				root.ID:         root,
				gpdtdc.ID:       gpdtdc,
				organization.ID: organization,
			},
			list: []domain.Tenant{root, gpdtdc, organization},
		},
		&fakeWorksmobileUserRepo{},
		outboxRepo,
		nil,
	)

	item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organization.ID)

	require.NoError(t, err)
	require.NotNil(t, item)
	require.Len(t, outboxRepo.created, 1)
	request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
	require.Equal(t, int64(1003), request.DomainID)
	require.Equal(t, "planning@baroncs.co.kr", request.Email)
}

func TestWorksmobileDomainClassificationUsesAncestorCompanyForGPDTDCOrganization(t *testing.T) {
	t.Setenv("GPDTDC_DOMAIN_ID", "1003")
	rootID := "root-tenant"
	companyID := "company-tenant"
	organizationID := "organization-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Slug:     "gpdtdc",
		Name:     "총괄기획&기술개발센터",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
		Domains:  []domain.TenantDomain{{Domain: "baroncs.co.kr"}},
	}
	organization := domain.Tenant{
		ID:       organizationID,
		Slug:     "gpd",
		Name:     "총괄기획실",
		Type:     domain.TenantTypeOrganization,
		ParentID: &companyID,
	}
	tenantByID := worksmobileTenantByID([]domain.Tenant{root, company, organization})

	domainTenant := worksmobileDomainClassificationTenant(organization, tenantByID)
	payload, err := BuildWorksmobileOrgUnitPayloadForDomainTenant(organization, domainTenant, nil, 1)

	require.NoError(t, err)
	require.Equal(t, companyID, domainTenant.ID)
	require.Equal(t, int64(1003), payload.DomainID)
	require.Equal(t, "gpd@baroncs.co.kr", payload.Email)
}

func TestWorksmobileSyncServiceKeepsCompanyUsersInComparisonScope(t *testing.T) {
	rootID := "root-tenant"
	companyID := "company-tenant"
	userGroupID := "user-group-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Name:     "계열사",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
	}
	userGroup := domain.Tenant{
		ID:       userGroupID,
		Name:     "연동 조직",
		Type:     domain.TenantTypeOrganization,
		ParentID: &companyID,
	}
	userRepo := &fakeWorksmobileUserRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company, userGroupID: userGroup}, list: []domain.Tenant{root, company, userGroup}},
		userRepo,
		&fakeWorksmobileOutboxRepo{},
		&fakeWorksmobileDirectoryClient{},
	)

	_, err := service.GetComparison(context.Background(), rootID, true)

	require.NoError(t, err)
	require.ElementsMatch(t, []string{companyID, userGroupID}, userRepo.requestedTenantIDs)
}

func TestWorksmobileSyncServiceSkipsArchivedUsersInComparison(t *testing.T) {
	rootID := "root-tenant"
	companyID := "company-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Name:     "계열사",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
	}
	archived := domain.User{
		ID:       "archived-user",
		Email:    "archived@samaneng.com",
		Name:     "Archived",
		TenantID: &companyID,
		Status:   domain.UserStatusArchived,
	}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company}, list: []domain.Tenant{root, company}},
		&fakeWorksmobileUserRepo{byTenant: []domain.User{archived}},
		&fakeWorksmobileOutboxRepo{},
		&fakeWorksmobileDirectoryClient{users: []WorksmobileRemoteUser{{
			ID:         "works-archived",
			ExternalID: archived.ID,
			Email:      archived.Email,
		}}},
	)

	comparison, err := service.GetComparison(context.Background(), rootID, true)

	require.NoError(t, err)
	require.Empty(t, comparison.Users)
}

func TestWorksmobileSyncServiceBackfillDryRunSkipsArchivedUsers(t *testing.T) {
	rootID := "root-tenant"
	companyID := "company-tenant"
	root := domain.Tenant{
		ID:   rootID,
		Slug: HanmacFamilyTenantSlug,
		Name: "한맥가족",
	}
	company := domain.Tenant{
		ID:       companyID,
		Name:     "계열사",
		Type:     domain.TenantTypeCompany,
		ParentID: &rootID,
	}
	active := domain.User{
		ID:       "active-user",
		Email:    "active@samaneng.com",
		Name:     "Active",
		TenantID: &companyID,
		Status:   domain.UserStatusActive,
	}
	archived := domain.User{
		ID:       "archived-user",
		Email:    "archived@samaneng.com",
		Name:     "Archived",
		TenantID: &companyID,
		Status:   domain.UserStatusArchived,
	}
	outboxRepo := &fakeWorksmobileOutboxRepo{}
	service := NewWorksmobileSyncService(
		&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company}, list: []domain.Tenant{root, company}},
		&fakeWorksmobileUserRepo{byTenant: []domain.User{active, archived}},
		outboxRepo,
		nil,
	)

	dryRun, err := service.EnqueueBackfillDryRun(context.Background(), rootID)

	require.NoError(t, err)
	require.Equal(t, 1, dryRun.UserCount)
	require.Len(t, outboxRepo.created, 1)
	require.Equal(t, 1, outboxRepo.created[0].Payload["userCount"])
}

type fakeWorksmobileTenantService struct {
	tenants map[string]domain.Tenant
	list    []domain.Tenant
}

func (f *fakeWorksmobileTenantService) RegisterTenant(ctx context.Context, name, slug, tenantType, description string, domains []string, parentID *string, creatorID string) (*domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) RequestRegistration(ctx context.Context, name, slug, description string, domainName string, adminEmail string) (*domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) GetTenantByDomain(ctx context.Context, emailDomain string) (*domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) GetTenant(ctx context.Context, id string) (*domain.Tenant, error) {
	tenant := f.tenants[id]
	return &tenant, nil
}

func (f *fakeWorksmobileTenantService) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
	return f.list, int64(len(f.list)), nil
}

func (f *fakeWorksmobileTenantService) ListManageableTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) ListJoinedTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) IsDomainAllowed(ctx context.Context, domainName string) (bool, error) {
	return false, nil
}

func (f *fakeWorksmobileTenantService) ApproveTenant(ctx context.Context, id string) error {
	return nil
}

func (f *fakeWorksmobileTenantService) ProvisionTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) {
	return nil, nil
}

func (f *fakeWorksmobileTenantService) SetKetoService(keto KetoService) {}

func (f *fakeWorksmobileTenantService) DeleteTenantsBulk(ctx context.Context, ids []string) error {
	return nil
}

type fakeWorksmobileUserRepo struct {
	byID               map[string]domain.User
	byTenant           []domain.User
	requestedTenantIDs []string
}

func (f *fakeWorksmobileUserRepo) Create(ctx context.Context, user *domain.User) error { return nil }
func (f *fakeWorksmobileUserRepo) Update(ctx context.Context, user *domain.User) error { return nil }
func (f *fakeWorksmobileUserRepo) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) {
	user := f.byID[id]
	return &user, nil
}

func (f *fakeWorksmobileUserRepo) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) List(ctx context.Context, offset, limit int, search string, tenantSlug string) ([]domain.User, int64, error) {
	return nil, 0, nil
}

func (f *fakeWorksmobileUserRepo) CountByTenant(ctx context.Context, tenantID string) (int64, error) {
	return 0, nil
}

func (f *fakeWorksmobileUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) {
	f.requestedTenantIDs = append([]string(nil), tenantIDs...)
	return f.byTenant, nil
}

func (f *fakeWorksmobileUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) {
	return nil, nil
}
func (f *fakeWorksmobileUserRepo) Delete(ctx context.Context, id string) error { return nil }
func (f *fakeWorksmobileUserRepo) UpdateUserLoginIDs(ctx context.Context, userID string, loginIDs []domain.UserLoginID) error {
	return nil
}

func (f *fakeWorksmobileUserRepo) GetUserLoginIDs(ctx context.Context, userID string) ([]domain.UserLoginID, error) {
	return nil, nil
}

func (f *fakeWorksmobileUserRepo) IsLoginIDTaken(ctx context.Context, loginID string) (bool, error) {
	return false, nil
}

func (f *fakeWorksmobileUserRepo) FindTenantIDByLoginID(ctx context.Context, loginID string) (string, error) {
	return "", nil
}
