// Package mysqltest provides real MySQL repositories for user-service tests.
package mysqltest

import (
	"context"
	"database/sql"
	"strings"
	"testing"
	"time"

	"hyapp/pkg/appcode"
	authdomain "hyapp/services/user-service/internal/domain/auth"
	hostdomain "hyapp/services/user-service/internal/domain/host"
	invitedomain "hyapp/services/user-service/internal/domain/invite"
	userdomain "hyapp/services/user-service/internal/domain/user"
	mysqlstorage "hyapp/services/user-service/internal/storage/mysql"
	"hyapp/services/user-service/internal/testutil/mysqlschema"
)

// Repository wraps the production MySQL repository and adds test-only seed helpers.
type Repository struct {
	*mysqlstorage.Repository

	t      testing.TB
	schema *mysqlschema.Schema
}

// NewRepository creates an isolated MySQL schema and initializes the user-service tables.
func NewRepository(t testing.TB) *Repository {
	t.Helper()

	schema := mysqlschema.New(t)
	repository, err := mysqlstorage.Open(context.Background(), schema.DSN)
	if err != nil {
		t.Fatalf("open storage repository failed: %v", err)
	}

	wrapped := &Repository{
		Repository: repository,
		t:          t,
		schema:     schema,
	}
	t.Cleanup(wrapped.Close)
	return wrapped
}

// Close shuts down the storage repository; mysqlschema owns schema cleanup.
func (r *Repository) Close() {
	r.t.Helper()

	if r.Repository != nil {
		_ = r.Repository.Close()
	}
}

// RawDB exposes the isolated test schema for assertions that span read-model tables.
func (r *Repository) RawDB() *sql.DB {
	if r == nil || r.schema == nil {
		return nil
	}
	return r.schema.DB
}

// GetUser 让测试 wrapper 继续满足业务层用户主数据接口。
func (r *Repository) GetUser(ctx context.Context, userID int64) (userdomain.User, error) {
	return r.Repository.UserRepository().GetUser(ctx, userID)
}

// GetMyProfileStats 让测试 wrapper 继续满足我的页统计 read model 接口。
func (r *Repository) GetMyProfileStats(ctx context.Context, userID int64) (userdomain.ProfileStats, error) {
	return r.Repository.UserRepository().GetMyProfileStats(ctx, userID)
}

func (r *Repository) GetInviteAttribution(ctx context.Context, invitedUserID int64) (invitedomain.Attribution, error) {
	return r.Repository.UserRepository().GetInviteAttribution(ctx, invitedUserID)
}

func (r *Repository) RecordProfileVisit(ctx context.Context, visitorUserID int64, targetUserID int64, nowMs int64) (bool, userdomain.ProfileStats, error) {
	return r.Repository.UserRepository().RecordProfileVisit(ctx, visitorUserID, targetUserID, nowMs)
}

func (r *Repository) ListProfileVisitors(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.ProfileVisitRecord, int64, error) {
	return r.Repository.UserRepository().ListProfileVisitors(ctx, userID, page, pageSize)
}

func (r *Repository) FollowUser(ctx context.Context, followerUserID int64, followeeUserID int64, nowMs int64) (userdomain.ProfileStats, error) {
	return r.Repository.UserRepository().FollowUser(ctx, followerUserID, followeeUserID, nowMs)
}

func (r *Repository) UnfollowUser(ctx context.Context, followerUserID int64, followeeUserID int64, nowMs int64) (userdomain.ProfileStats, error) {
	return r.Repository.UserRepository().UnfollowUser(ctx, followerUserID, followeeUserID, nowMs)
}

func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FollowRecord, int64, error) {
	return r.Repository.UserRepository().ListFollowing(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}

func (r *Repository) ApplyFriend(ctx context.Context, requesterUserID int64, targetUserID int64, nowMs int64) (userdomain.FriendApplication, bool, error) {
	return r.Repository.UserRepository().ApplyFriend(ctx, requesterUserID, targetUserID, nowMs)
}

func (r *Repository) AcceptFriendApplication(ctx context.Context, accepterUserID int64, requesterUserID int64, nowMs int64) (userdomain.FriendRecord, error) {
	return r.Repository.UserRepository().AcceptFriendApplication(ctx, accepterUserID, requesterUserID, nowMs)
}

func (r *Repository) DeleteFriend(ctx context.Context, userID int64, friendUserID int64, nowMs int64) (bool, error) {
	return r.Repository.UserRepository().DeleteFriend(ctx, userID, friendUserID, nowMs)
}

func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) {
	return r.Repository.UserRepository().ListFriends(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}

func (r *Repository) ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error) {
	return r.Repository.UserRepository().ListFriendApplications(ctx, userID, direction, page, pageSize)
}

func (r *Repository) SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) {
	return r.Repository.UserRepository().SubmitReport(ctx, report)
}

// BusinessUserLookup 让测试 wrapper 继续满足业务场景用户查询接口。
func (r *Repository) BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) {
	return r.Repository.UserRepository().BusinessUserLookup(ctx, keyword, numericKeyword, pageSize)
}

// BatchGetUsers 让测试 wrapper 继续满足业务层用户主数据接口。
func (r *Repository) BatchGetUsers(ctx context.Context, userIDs []int64) (map[int64]userdomain.User, error) {
	return r.Repository.UserRepository().BatchGetUsers(ctx, userIDs)
}

// BatchGetUserAdminProfiles 让跨包 MySQL 测试继续复用真实后台投影查询。
func (r *Repository) BatchGetUserAdminProfiles(ctx context.Context, userIDs []int64, nowMs int64) (map[int64]userdomain.AdminProfile, error) {
	return r.Repository.UserRepository().BatchGetUserAdminProfiles(ctx, userIDs, nowMs)
}

// BatchGetRoomBasicUsers 让测试 wrapper 继续满足房间首屏基础资料查询接口。
func (r *Repository) BatchGetRoomBasicUsers(ctx context.Context, userIDs []int64) (map[int64]userdomain.RoomBasicUser, error) {
	return r.Repository.UserRepository().BatchGetRoomBasicUsers(ctx, userIDs)
}

// ListUserIDs 让测试 wrapper 继续满足后台 fanout 目标用户查询接口。
func (r *Repository) ListUserIDs(ctx context.Context, filter userdomain.UserIDPageFilter) ([]int64, error) {
	return r.Repository.UserRepository().ListUserIDs(ctx, filter)
}

// CreateUserWithIdentity 让测试 wrapper 继续满足业务层用户创建接口。
func (r *Repository) CreateUserWithIdentity(ctx context.Context, user userdomain.User, identity userdomain.Identity) error {
	return r.Repository.UserRepository().CreateUserWithIdentity(ctx, user, identity)
}

// UpdateUserProfile 让测试 wrapper 继续满足业务层资料更新接口。
func (r *Repository) UpdateUserProfile(ctx context.Context, command userdomain.ProfileUpdateCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().UpdateUserProfile(ctx, command)
}

// UpdateUserProfileBackground 让测试 wrapper 继续满足业务层背景图更新接口。
func (r *Repository) UpdateUserProfileBackground(ctx context.Context, command userdomain.ProfileBackgroundUpdateCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().UpdateUserProfileBackground(ctx, command)
}

// UpdateUserContactInfo 让测试 wrapper 继续满足业务层用户级联系方式更新接口。
func (r *Repository) UpdateUserContactInfo(ctx context.Context, command userdomain.ProfileContactUpdateCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().UpdateUserContactInfo(ctx, command)
}

// UpdateUserWithdrawAddress 让测试 wrapper 继续满足业务层用户默认提现地址更新接口。
func (r *Repository) UpdateUserWithdrawAddress(ctx context.Context, command userdomain.WithdrawAddressUpdateCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().UpdateUserWithdrawAddress(ctx, command)
}

// CompleteOnboarding 让测试 wrapper 继续满足业务层注册资料接口。
func (r *Repository) CompleteOnboarding(ctx context.Context, command userdomain.CompleteOnboardingCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().CompleteOnboarding(ctx, command)
}

// BindInviteReferrer 让测试 wrapper 继续满足 H5 邀请人绑定接口。
func (r *Repository) BindInviteReferrer(ctx context.Context, command userdomain.BindInviteReferrerCommand) (userdomain.User, error) {
	return r.Repository.UserRepository().BindInviteReferrer(ctx, command)
}

// FindInviteReferrerByCode 让测试 wrapper 继续满足 H5 邀请人搜索接口。
func (r *Repository) FindInviteReferrerByCode(ctx context.Context, appCode string, inviteCode string) (userdomain.User, error) {
	return r.Repository.UserRepository().FindInviteReferrerByCode(ctx, appCode, inviteCode)
}

// ChangeUserCountry 让测试 wrapper 继续满足业务层国家修改接口。
func (r *Repository) ChangeUserCountry(ctx context.Context, command userdomain.CountryChangeCommand) (userdomain.User, int64, []string, error) {
	return r.Repository.UserRepository().ChangeUserCountry(ctx, command)
}

// BindPushToken 让测试 wrapper 继续满足设备 push token repository。
func (r *Repository) BindPushToken(ctx context.Context, command userdomain.PushTokenCommand) error {
	return r.Repository.DeviceRepository().BindPushToken(ctx, command)
}

// DeletePushToken 让测试 wrapper 继续满足设备 push token repository。
func (r *Repository) DeletePushToken(ctx context.Context, command userdomain.DeletePushTokenCommand) (bool, error) {
	return r.Repository.DeviceRepository().DeletePushToken(ctx, command)
}

// AllocateDisplayUserID 让测试 wrapper 继续满足认证注册接口。
func (r *Repository) AllocateDisplayUserID(ctx context.Context, appCode string) (string, error) {
	return r.Repository.AuthRepository().AllocateDisplayUserID(ctx, appCode)
}

// GetUserIdentity 让测试 wrapper 继续满足短号接口。
func (r *Repository) GetUserIdentity(ctx context.Context, userID int64) (userdomain.Identity, error) {
	return r.Repository.IdentityRepository().GetUserIdentity(ctx, userID)
}

// ResolveDisplayUserID 让测试 wrapper 继续满足短号接口。
func (r *Repository) ResolveDisplayUserID(ctx context.Context, displayUserID string, nowMs int64) (userdomain.Identity, error) {
	return r.Repository.IdentityRepository().ResolveDisplayUserID(ctx, displayUserID, nowMs)
}

func (r *Repository) ResolveAdminUserIdentifier(ctx context.Context, userIdentifier string, nowMs int64) (int64, error) {
	return r.Repository.IdentityRepository().ResolveAdminUserIdentifier(ctx, userIdentifier, nowMs)
}

// ChangeDisplayUserID 让测试 wrapper 继续满足短号接口。
func (r *Repository) ChangeDisplayUserID(ctx context.Context, command userdomain.DisplayUserIDChangeCommand) (userdomain.Identity, error) {
	return r.Repository.IdentityRepository().ChangeDisplayUserID(ctx, command)
}

// ApplyPrettyDisplayUserID 让测试 wrapper 继续满足靓号接口。
func (r *Repository) ApplyPrettyDisplayUserID(ctx context.Context, command userdomain.PrettyDisplayUserIDCommand) (userdomain.Identity, string, error) {
	return r.Repository.IdentityRepository().ApplyPrettyDisplayUserID(ctx, command)
}

// ListAvailablePrettyDisplayIDs 让测试 wrapper 继续满足靓号池可申请列表接口。
func (r *Repository) ListAvailablePrettyDisplayIDs(ctx context.Context, query userdomain.AvailablePrettyDisplayIDQuery) ([]userdomain.PrettyDisplayID, int64, error) {
	return r.Repository.IdentityRepository().ListAvailablePrettyDisplayIDs(ctx, query)
}

// ApplyPrettyDisplayIDFromPool 让测试 wrapper 继续满足靓号池申请接口。
func (r *Repository) ApplyPrettyDisplayIDFromPool(ctx context.Context, command userdomain.PrettyDisplayIDPoolApplyCommand) (userdomain.Identity, error) {
	return r.Repository.IdentityRepository().ApplyPrettyDisplayIDFromPool(ctx, command)
}

// ListPrettyDisplayIDPools 让测试 wrapper 继续满足后台靓号池列表接口。
func (r *Repository) ListPrettyDisplayIDPools(ctx context.Context, query userdomain.PrettyDisplayIDPoolQuery) ([]userdomain.PrettyDisplayIDPool, int64, error) {
	return r.Repository.IdentityRepository().ListPrettyDisplayIDPools(ctx, query)
}

// CreatePrettyDisplayIDPool 让测试 wrapper 继续满足后台靓号池创建接口。
func (r *Repository) CreatePrettyDisplayIDPool(ctx context.Context, command userdomain.PrettyDisplayIDPoolCommand) (userdomain.PrettyDisplayIDPool, error) {
	return r.Repository.IdentityRepository().CreatePrettyDisplayIDPool(ctx, command)
}

// UpdatePrettyDisplayIDPool 让测试 wrapper 继续满足后台靓号池更新接口。
func (r *Repository) UpdatePrettyDisplayIDPool(ctx context.Context, command userdomain.PrettyDisplayIDPoolCommand) (userdomain.PrettyDisplayIDPool, error) {
	return r.Repository.IdentityRepository().UpdatePrettyDisplayIDPool(ctx, command)
}

// GeneratePrettyDisplayIDs 让测试 wrapper 继续满足后台靓号批量生成接口。
func (r *Repository) GeneratePrettyDisplayIDs(ctx context.Context, command userdomain.PrettyDisplayIDGenerateCommand) (userdomain.PrettyDisplayIDGenerationBatch, error) {
	return r.Repository.IdentityRepository().GeneratePrettyDisplayIDs(ctx, command)
}

// ListPrettyDisplayIDs 让测试 wrapper 继续满足后台靓号列表接口。
func (r *Repository) ListPrettyDisplayIDs(ctx context.Context, query userdomain.PrettyDisplayIDQuery) ([]userdomain.PrettyDisplayID, int64, error) {
	return r.Repository.IdentityRepository().ListPrettyDisplayIDs(ctx, query)
}

// SetPrettyDisplayIDStatus 让测试 wrapper 继续满足后台靓号状态修改接口。
func (r *Repository) SetPrettyDisplayIDStatus(ctx context.Context, command userdomain.PrettyDisplayIDStatusCommand) (userdomain.PrettyDisplayID, error) {
	return r.Repository.IdentityRepository().SetPrettyDisplayIDStatus(ctx, command)
}

// RecyclePrettyDisplayID 让测试 wrapper 继续满足后台靓号回收接口。
func (r *Repository) RecyclePrettyDisplayID(ctx context.Context, command userdomain.PrettyDisplayIDRecycleCommand) (userdomain.PrettyDisplayID, error) {
	return r.Repository.IdentityRepository().RecyclePrettyDisplayID(ctx, command)
}

// AdminGrantPrettyDisplayID 让测试 wrapper 继续满足后台发放靓号接口。
func (r *Repository) AdminGrantPrettyDisplayID(ctx context.Context, command userdomain.AdminGrantPrettyDisplayIDCommand) (userdomain.Identity, string, error) {
	return r.Repository.IdentityRepository().AdminGrantPrettyDisplayID(ctx, command)
}

// ExpirePrettyDisplayUserID 让测试 wrapper 继续满足靓号接口。
func (r *Repository) ExpirePrettyDisplayUserID(ctx context.Context, command userdomain.ExpirePrettyDisplayUserIDCommand) (userdomain.Identity, error) {
	return r.Repository.IdentityRepository().ExpirePrettyDisplayUserID(ctx, command)
}

// ResolveEnabledCountryByCode 让测试 wrapper 继续满足国家/区域查询接口。
func (r *Repository) ResolveEnabledCountryByCode(ctx context.Context, countryCode string) (userdomain.Country, bool, error) {
	return r.Repository.RegionRepository().ResolveEnabledCountryByCode(ctx, countryCode)
}

// ListRegistrationCountries 让测试 wrapper 继续满足国家/区域查询接口。
func (r *Repository) ListRegistrationCountries(ctx context.Context) ([]userdomain.Country, error) {
	return r.Repository.RegionRepository().ListRegistrationCountries(ctx)
}

// ResolveActiveRegionByCountry 让测试 wrapper 继续满足国家/区域查询接口。
func (r *Repository) ResolveActiveRegionByCountry(ctx context.Context, countryCode string) (userdomain.Region, bool, error) {
	return r.Repository.RegionRepository().ResolveActiveRegionByCountry(ctx, countryCode)
}

// UpdateCountry 让测试 wrapper 继续满足国家管理接口。
func (r *Repository) UpdateCountry(ctx context.Context, command userdomain.UpdateCountryCommand) (userdomain.Country, error) {
	return r.Repository.RegionRepository().UpdateCountry(ctx, command)
}

// ListCountries 让测试 wrapper 继续满足国家管理接口。
func (r *Repository) ListCountries(ctx context.Context, filter userdomain.CountryFilter) ([]userdomain.Country, error) {
	return r.Repository.RegionRepository().ListCountries(ctx, filter)
}

// UpdateRegion 让测试 wrapper 继续满足区域管理接口。
func (r *Repository) UpdateRegion(ctx context.Context, command userdomain.UpdateRegionCommand) (userdomain.Region, error) {
	return r.Repository.RegionRepository().UpdateRegion(ctx, command)
}

// ListRegions 让测试 wrapper 继续满足区域管理接口。
func (r *Repository) ListRegions(ctx context.Context, filter userdomain.RegionFilter) ([]userdomain.Region, error) {
	return r.Repository.RegionRepository().ListRegions(ctx, filter)
}

// GetRegion 让测试 wrapper 继续满足区域管理接口。
func (r *Repository) GetRegion(ctx context.Context, regionID int64) (userdomain.Region, error) {
	return r.Repository.RegionRepository().GetRegion(ctx, regionID)
}

// ReplaceRegionCountries 让测试 wrapper 继续满足区域管理接口。
func (r *Repository) ReplaceRegionCountries(ctx context.Context, command userdomain.ReplaceRegionCountriesCommand) (userdomain.Region, error) {
	return r.Repository.RegionRepository().ReplaceRegionCountries(ctx, command)
}

// ProcessNextRegionRebuildTask 让测试 wrapper 继续满足历史用户区域重算接口。
func (r *Repository) ProcessNextRegionRebuildTask(ctx context.Context, workerID string, nowMs int64, lockTTL time.Duration, batchSize int) (userdomain.RegionRebuildProcessResult, error) {
	return r.Repository.RegionRepository().ProcessNextRegionRebuildTask(ctx, workerID, nowMs, lockTTL, batchSize)
}

// SetPassword 让测试 wrapper 继续满足认证接口。
func (r *Repository) SetPassword(ctx context.Context, account authdomain.PasswordAccount) error {
	return r.Repository.AuthRepository().SetPassword(ctx, account)
}

// CreatePasswordUser 让测试 wrapper 继续满足认证接口。
func (r *Repository) CreatePasswordUser(ctx context.Context, user userdomain.User, identity userdomain.Identity, account authdomain.PasswordAccount, session authdomain.Session, invite invitedomain.BindCommand, maxAccountsPerDevice int32) error {
	return r.Repository.AuthRepository().CreatePasswordUser(ctx, user, identity, account, session, invite, maxAccountsPerDevice)
}

// FindPasswordByDisplayUserID 让测试 wrapper 继续满足认证接口。
func (r *Repository) FindPasswordByDisplayUserID(ctx context.Context, displayUserID string, nowMs int64) (authdomain.PasswordAccount, error) {
	return r.Repository.AuthRepository().FindPasswordByDisplayUserID(ctx, displayUserID, nowMs)
}

// CreateSession 让测试 wrapper 继续满足认证接口。
func (r *Repository) CreateSession(ctx context.Context, session authdomain.Session) error {
	return r.Repository.AuthRepository().CreateSession(ctx, session)
}

// FindActiveSessionByRefreshHash 让测试 wrapper 继续满足认证接口。
func (r *Repository) FindActiveSessionByRefreshHash(ctx context.Context, refreshTokenHash string, nowMs int64) (authdomain.Session, error) {
	return r.Repository.AuthRepository().FindActiveSessionByRefreshHash(ctx, refreshTokenHash, nowMs)
}

// FindSessionByRefreshHash 让 refresh rotation 测试读取已轮换 lineage。
func (r *Repository) FindSessionByRefreshHash(ctx context.Context, refreshTokenHash string) (authdomain.Session, error) {
	return r.Repository.AuthRepository().FindSessionByRefreshHash(ctx, refreshTokenHash)
}

// FindLatestActiveSessionByUser 让测试 wrapper 继续满足认证接口。
func (r *Repository) FindLatestActiveSessionByUser(ctx context.Context, userID int64, nowMs int64) (authdomain.Session, error) {
	return r.Repository.AuthRepository().FindLatestActiveSessionByUser(ctx, userID, nowMs)
}

// UpsertUserDevice 让测试 wrapper 继续满足认证接口。
func (r *Repository) UpsertUserDevice(ctx context.Context, device authdomain.UserDevice) error {
	return r.Repository.AuthRepository().UpsertUserDevice(ctx, device)
}

// FindActiveSessionByID 让测试 wrapper 继续满足认证接口。
func (r *Repository) FindActiveSessionByID(ctx context.Context, sessionID string, nowMs int64) (authdomain.Session, error) {
	return r.Repository.AuthRepository().FindActiveSessionByID(ctx, sessionID, nowMs)
}

// RotateRefreshSession 让测试 wrapper 继续使用生产 MySQL 原子轮换实现。
func (r *Repository) RotateRefreshSession(ctx context.Context, command authdomain.RefreshRotationCommand) (authdomain.RefreshRotationResult, error) {
	return r.Repository.AuthRepository().RotateRefreshSession(ctx, command)
}

// RevokeSession 让测试 wrapper 继续满足认证接口。
func (r *Repository) RevokeSession(ctx context.Context, sessionID string, refreshTokenHash string, revokedAtMs int64) (bool, error) {
	return r.Repository.AuthRepository().RevokeSession(ctx, sessionID, refreshTokenHash, revokedAtMs)
}

// RevokeSessionFamily 让 logout/refresh 竞态测试覆盖真实 token family 事务。
func (r *Repository) RevokeSessionFamily(ctx context.Context, sessionID string, refreshTokenHash string, revokedAtMs int64, denyUntilMs int64, requestID string) (string, []string, bool, error) {
	return r.Repository.AuthRepository().RevokeSessionFamily(ctx, sessionID, refreshTokenHash, revokedAtMs, denyUntilMs, requestID)
}

// RevokeSessionWithReason 让测试 wrapper 继续满足认证接口。
func (r *Repository) RevokeSessionWithReason(ctx context.Context, sessionID string, revokedAtMs int64, reason string, requestID string, revokedBy string) (bool, error) {
	return r.Repository.AuthRepository().RevokeSessionWithReason(ctx, sessionID, revokedAtMs, reason, requestID, revokedBy)
}

// RevokeUserDeviceSessionsForRisk 让测试 wrapper 继续满足认证接口。
func (r *Repository) RevokeUserDeviceSessionsForRisk(ctx context.Context, sourceSessionID string, userID int64, revokedAtMs int64, reason string, requestID string, revokedBy string) ([]string, error) {
	return r.Repository.AuthRepository().RevokeUserDeviceSessionsForRisk(ctx, sourceSessionID, userID, revokedAtMs, reason, requestID, revokedBy)
}

func (r *Repository) ClaimSessionDenylistJobs(ctx context.Context, workerID string, nowMs int64, lockUntilMs int64, limit int) ([]authdomain.SessionDenylistJob, error) {
	return r.Repository.AuthRepository().ClaimSessionDenylistJobs(ctx, workerID, nowMs, lockUntilMs, limit)
}

func (r *Repository) MarkSessionDenylistJobDelivered(ctx context.Context, jobID int64, appCode string, sessionID string, reason string, appliedDenyUntilMs int64, nowMs int64, nextRehydrateAtMs int64) error {
	return r.Repository.AuthRepository().MarkSessionDenylistJobDelivered(ctx, jobID, appCode, sessionID, reason, appliedDenyUntilMs, nowMs, nextRehydrateAtMs)
}

func (r *Repository) MarkSessionDenylistJobRetryable(ctx context.Context, jobID int64, errorMessage string, nextRetryAtMs int64, nowMs int64) error {
	return r.Repository.AuthRepository().MarkSessionDenylistJobRetryable(ctx, jobID, errorMessage, nextRetryAtMs, nowMs)
}

func (r *Repository) MaxSessionDenylistJobID(ctx context.Context) (int64, error) {
	return r.Repository.AuthRepository().MaxSessionDenylistJobID(ctx)
}

func (r *Repository) ListSessionDenylistJobsForHydration(ctx context.Context, nowMs int64, afterJobID int64, throughJobID int64, limit int) ([]authdomain.SessionDenylistJob, int64, error) {
	return r.Repository.AuthRepository().ListSessionDenylistJobsForHydration(ctx, nowMs, afterJobID, throughJobID, limit)
}

func (r *Repository) DeleteExpiredSessionDenylistJobs(ctx context.Context, nowMs int64, limit int) (int, error) {
	return r.Repository.AuthRepository().DeleteExpiredSessionDenylistJobs(ctx, nowMs, limit)
}

func (r *Repository) DeleteExpiredRefreshOutcomes(ctx context.Context, nowMs int64, limit int) (int, error) {
	return r.Repository.AuthRepository().DeleteExpiredRefreshOutcomes(ctx, nowMs, limit)
}

// UpdateSessionHeartbeat 让测试 wrapper 继续满足认证接口。
func (r *Repository) UpdateSessionHeartbeat(ctx context.Context, sessionID string, userID int64, heartbeatAtMs int64, requestID string) (authdomain.Session, error) {
	return r.Repository.AuthRepository().UpdateSessionHeartbeat(ctx, sessionID, userID, heartbeatAtMs, requestID)
}

// FindThirdPartyIdentity 让测试 wrapper 继续满足认证接口。
func (r *Repository) FindThirdPartyIdentity(ctx context.Context, provider string, providerSubject string) (authdomain.ThirdPartyIdentity, error) {
	return r.Repository.AuthRepository().FindThirdPartyIdentity(ctx, provider, providerSubject)
}

// CountUsersByRegisterDeviceID 让测试 wrapper 继续满足认证接口。
func (r *Repository) CountUsersByRegisterDeviceID(ctx context.Context, appCode string, deviceID string) (int64, error) {
	return r.Repository.AuthRepository().CountUsersByRegisterDeviceID(ctx, appCode, deviceID)
}

// ListDisplayUserIDsByRegisterDeviceID 让测试 wrapper 继续满足认证接口。
func (r *Repository) ListDisplayUserIDsByRegisterDeviceID(ctx context.Context, appCode string, deviceID string) ([]string, error) {
	return r.Repository.AuthRepository().ListDisplayUserIDsByRegisterDeviceID(ctx, appCode, deviceID)
}

// CreateThirdPartyUser 让测试 wrapper 继续满足认证接口。
func (r *Repository) CreateThirdPartyUser(ctx context.Context, user userdomain.User, identity userdomain.Identity, thirdParty authdomain.ThirdPartyIdentity, session authdomain.Session, invite invitedomain.BindCommand, maxAccountsPerDevice int32) error {
	return r.Repository.AuthRepository().CreateThirdPartyUser(ctx, user, identity, thirdParty, session, invite, maxAccountsPerDevice)
}

// CompleteThirdPartyRegistration 让测试 wrapper 继续满足注册完成兼容接口。
func (r *Repository) CompleteThirdPartyRegistration(ctx context.Context, command userdomain.CompleteOnboardingCommand, session authdomain.Session) (userdomain.User, error) {
	return r.Repository.AuthRepository().CompleteThirdPartyRegistration(ctx, command, session)
}

// GetRegisterRiskConfig 让测试 wrapper 继续满足认证接口。
func (r *Repository) GetRegisterRiskConfig(ctx context.Context, appCode string) (authdomain.RegisterRiskConfig, error) {
	return r.Repository.AuthRepository().GetRegisterRiskConfig(ctx, appCode)
}

// UpsertRegisterRiskConfig 让测试 wrapper 继续满足认证接口。
func (r *Repository) UpsertRegisterRiskConfig(ctx context.Context, config authdomain.RegisterRiskConfig) (authdomain.RegisterRiskConfig, error) {
	return r.Repository.AuthRepository().UpsertRegisterRiskConfig(ctx, config)
}

// RecordLoginAudit 让测试 wrapper 继续满足认证接口。
func (r *Repository) RecordLoginAudit(ctx context.Context, audit authdomain.LoginAudit) error {
	return r.Repository.AuthRepository().RecordLoginAudit(ctx, audit)
}

// CreateLoginIPRiskJob 让测试 wrapper 继续满足认证接口。
func (r *Repository) CreateLoginIPRiskJob(ctx context.Context, job authdomain.LoginIPRiskJob) error {
	return r.Repository.AuthRepository().CreateLoginIPRiskJob(ctx, job)
}

// ClaimLoginIPRiskJobs 让测试 wrapper 继续满足认证接口。
func (r *Repository) ClaimLoginIPRiskJobs(ctx context.Context, workerID string, nowMs int64, lockTTL time.Duration, batchSize int) ([]authdomain.LoginIPRiskJob, error) {
	return r.Repository.AuthRepository().ClaimLoginIPRiskJobs(ctx, workerID, nowMs, lockTTL, batchSize)
}

// CompleteLoginIPRiskJob 让测试 wrapper 继续满足认证接口。
func (r *Repository) CompleteLoginIPRiskJob(ctx context.Context, job authdomain.LoginIPRiskJob) error {
	return r.Repository.AuthRepository().CompleteLoginIPRiskJob(ctx, job)
}

// CreateLoginIPRiskDecision 让测试 wrapper 继续满足认证接口。
func (r *Repository) CreateLoginIPRiskDecision(ctx context.Context, decision authdomain.LoginIPRiskDecision) error {
	return r.Repository.AuthRepository().CreateLoginIPRiskDecision(ctx, decision)
}

// ListEnabledLoginRiskCountryBlocks 让测试 wrapper 继续满足认证风控接口。
func (r *Repository) ListEnabledLoginRiskCountryBlocks(ctx context.Context) ([]authdomain.LoginRiskCountryBlock, error) {
	return r.Repository.AuthRepository().ListEnabledLoginRiskCountryBlocks(ctx)
}

// ListEnabledLoginRiskIPWhitelist 让测试 wrapper 继续满足认证风控接口。
func (r *Repository) ListEnabledLoginRiskIPWhitelist(ctx context.Context) ([]authdomain.LoginRiskIPWhitelist, error) {
	return r.Repository.AuthRepository().ListEnabledLoginRiskIPWhitelist(ctx)
}

// ListEnabledLoginRiskUserWhitelist 让测试 wrapper 继续满足认证风控接口。
func (r *Repository) ListEnabledLoginRiskUserWhitelist(ctx context.Context) ([]authdomain.LoginRiskUserWhitelist, error) {
	return r.Repository.AuthRepository().ListEnabledLoginRiskUserWhitelist(ctx)
}

// MarkLoginAuditBlocked 让测试 wrapper 继续满足认证接口。
func (r *Repository) MarkLoginAuditBlocked(ctx context.Context, requestID string, userID int64, countryCode string, blockReason string) error {
	return r.Repository.AuthRepository().MarkLoginAuditBlocked(ctx, requestID, userID, countryCode, blockReason)
}

// PutUser seeds or overwrites a user row plus its active default display_user_id.
func (r *Repository) PutUser(user userdomain.User) {
	r.t.Helper()

	if user.DefaultDisplayUserID == "" {
		user.DefaultDisplayUserID = user.CurrentDisplayUserID
	}
	if user.CurrentDisplayUserID == "" {
		user.CurrentDisplayUserID = user.DefaultDisplayUserID
	}
	user.AppCode = appcode.Normalize(user.AppCode)
	if user.CurrentDisplayUserIDKind == "" {
		user.CurrentDisplayUserIDKind = userdomain.DisplayUserIDKindDefault
	}
	if user.Status == "" {
		user.Status = userdomain.StatusActive
	}
	if user.OnboardingStatus == "" {
		// 测试种子默认模拟三方刚创建但未完成注册资料的用户。
		if user.ProfileCompleted {
			user.OnboardingStatus = userdomain.OnboardingStatusCompleted
		} else {
			user.OnboardingStatus = userdomain.OnboardingStatusProfileRequired
		}
	}
	nowMs := user.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if user.CreatedAtMs == 0 {
		user.CreatedAtMs = nowMs
	}
	user.UpdatedAtMs = nowMs

	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO users (
			app_code, user_id, default_display_user_id, current_display_user_id, current_display_user_id_kind,
			current_display_user_id_expires_at_ms, username, gender, country, region_id, invite_code,
			register_ip, register_user_agent, country_by_ip, register_device_id, register_device,
			os_version, avatar, profile_bg_img, contact_info, withdraw_usdt_trc20_address, birth_date, app_version, build_number, source, install_channel,
			campaign, platform, language, timezone, profile_completed, profile_completed_at_ms,
			onboarding_status, status, created_at_ms, updated_at_ms
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			app_code = VALUES(app_code),
			default_display_user_id = VALUES(default_display_user_id),
			current_display_user_id = VALUES(current_display_user_id),
			current_display_user_id_kind = VALUES(current_display_user_id_kind),
			current_display_user_id_expires_at_ms = VALUES(current_display_user_id_expires_at_ms),
			username = VALUES(username),
			gender = VALUES(gender),
			country = VALUES(country),
			region_id = VALUES(region_id),
			invite_code = VALUES(invite_code),
			register_ip = VALUES(register_ip),
			register_user_agent = VALUES(register_user_agent),
			country_by_ip = VALUES(country_by_ip),
			register_device_id = VALUES(register_device_id),
			register_device = VALUES(register_device),
			os_version = VALUES(os_version),
			avatar = VALUES(avatar),
			profile_bg_img = VALUES(profile_bg_img),
			contact_info = VALUES(contact_info),
			withdraw_usdt_trc20_address = VALUES(withdraw_usdt_trc20_address),
			birth_date = VALUES(birth_date),
			app_version = VALUES(app_version),
			build_number = VALUES(build_number),
			source = VALUES(source),
			install_channel = VALUES(install_channel),
			campaign = VALUES(campaign),
			platform = VALUES(platform),
			language = VALUES(language),
			timezone = VALUES(timezone),
			profile_completed = VALUES(profile_completed),
			profile_completed_at_ms = VALUES(profile_completed_at_ms),
			onboarding_status = VALUES(onboarding_status),
			status = VALUES(status),
			updated_at_ms = VALUES(updated_at_ms)
	`, user.AppCode, user.UserID, user.DefaultDisplayUserID, user.CurrentDisplayUserID, string(user.CurrentDisplayUserIDKind), nullableInt64(user.CurrentDisplayUserIDExpiresAtMs), nullableString(user.Username), nullableString(user.Gender), nullableString(user.Country), nullableInt64(user.RegionID), nullableString(user.InviteCode), nullableString(user.RegisterIP), nullableString(user.RegisterUserAgent), nullableString(user.CountryByIP), nullableString(user.RegisterDeviceID), nullableString(user.RegisterDevice), nullableString(user.RegisterOSVersion), nullableString(user.Avatar), strings.TrimSpace(user.ProfileBgImg), strings.TrimSpace(user.ContactInfo), strings.TrimSpace(user.WithdrawUSDTTRC20Address), nullableString(user.BirthDate), nullableString(user.RegisterAppVersion), nullableString(user.RegisterBuildNumber), nullableString(user.RegisterSource), nullableString(user.RegisterInstallChannel), nullableString(user.RegisterCampaign), nullableString(user.RegisterPlatform), nullableString(user.Language), nullableString(user.Timezone), user.ProfileCompleted, user.ProfileCompletedAtMs, string(user.OnboardingStatus), string(user.Status), user.CreatedAtMs, user.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed user %d failed: %v", user.UserID, err)
	}

	_, err = r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO user_display_user_ids (app_code, display_user_id, user_id, display_user_id_kind, status, assigned_at_ms, activated_at_ms, created_at_ms, updated_at_ms)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			user_id = VALUES(user_id),
			display_user_id_kind = VALUES(display_user_id_kind),
			status = VALUES(status),
			activated_at_ms = VALUES(activated_at_ms),
			updated_at_ms = VALUES(updated_at_ms)
	`, user.AppCode, user.DefaultDisplayUserID, user.UserID, string(userdomain.DisplayUserIDKindDefault), string(userdomain.DisplayUserIDStatusActive), user.CreatedAtMs, user.CreatedAtMs, user.CreatedAtMs, user.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed display_user_id %s failed: %v", user.DefaultDisplayUserID, err)
	}
}

// PutCountry seeds or overwrites a country row for user-service tests.
// 国家创建、启用、禁用属于 admin-server 后台能力；user-service 测试只直接准备主数据状态。
func (r *Repository) PutCountry(country userdomain.Country) userdomain.Country {
	r.t.Helper()

	country.CountryCode = userdomain.NormalizeCountryCode(country.CountryCode)
	country.ISOAlpha3 = userdomain.NormalizeISOAlpha3(country.ISOAlpha3)
	country.ISONumeric = userdomain.NormalizeISONumeric(country.ISONumeric)
	country.PhoneCountryCode = userdomain.NormalizePhoneCountryCode(country.PhoneCountryCode)
	country.CountryName = strings.TrimSpace(country.CountryName)
	country.CountryDisplayName = strings.TrimSpace(country.CountryDisplayName)
	country.Flag = strings.TrimSpace(country.Flag)
	if country.CountryName == "" {
		country.CountryName = country.CountryCode + " Name"
	}
	if country.CountryDisplayName == "" {
		country.CountryDisplayName = country.CountryCode + " Display"
	}
	nowMs := country.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if country.CreatedAtMs == 0 {
		country.CreatedAtMs = nowMs
	}
	country.UpdatedAtMs = nowMs

	result, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO countries (
			country_id, country_name, country_code, iso_alpha3, iso_numeric, country_display_name,
			phone_country_code, flag, enabled, sort_order,
			created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			country_name = VALUES(country_name),
			iso_alpha3 = VALUES(iso_alpha3),
			iso_numeric = VALUES(iso_numeric),
			country_display_name = VALUES(country_display_name),
			phone_country_code = VALUES(phone_country_code),
			flag = VALUES(flag),
			enabled = VALUES(enabled),
			sort_order = VALUES(sort_order),
			updated_by_user_id = VALUES(updated_by_user_id),
			updated_at_ms = VALUES(updated_at_ms)
	`, nullableInt64(country.CountryID), country.CountryName, country.CountryCode, nullableString(country.ISOAlpha3), nullableString(country.ISONumeric), country.CountryDisplayName, nullableString(country.PhoneCountryCode), country.Flag, country.Enabled, country.SortOrder, nullableInt64(country.CreatedByUserID), nullableInt64(country.UpdatedByUserID), country.CreatedAtMs, country.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed country %s failed: %v", country.CountryCode, err)
	}
	if country.CountryID == 0 {
		country.CountryID, err = result.LastInsertId()
		if err != nil {
			r.t.Fatalf("read seeded country %s id failed: %v", country.CountryCode, err)
		}
	}
	return country
}

// PutRegion seeds a region, its active country mappings, and rebuild tasks for user-service tests.
// 区域创建属于 admin-server 后台能力；user-service 测试只直接准备区域主数据状态。
func (r *Repository) PutRegion(region userdomain.Region) userdomain.Region {
	r.t.Helper()

	region.RegionCode = userdomain.NormalizeRegionCode(region.RegionCode)
	region.AppCode = appcode.Normalize(region.AppCode)
	region.Name = strings.TrimSpace(region.Name)
	if region.Name == "" {
		region.Name = region.RegionCode + " Region"
	}
	if region.Status == "" {
		region.Status = userdomain.RegionStatusActive
	}
	nowMs := region.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if region.CreatedAtMs == 0 {
		region.CreatedAtMs = nowMs
	}
	region.UpdatedAtMs = nowMs

	tx, err := r.schema.DB.BeginTx(context.Background(), nil)
	if err != nil {
		r.t.Fatalf("begin seed region %s failed: %v", region.RegionCode, err)
	}
	defer tx.Rollback()

	result, err := tx.ExecContext(context.Background(), `
		INSERT INTO regions (app_code, region_id, region_code, name, status, sort_order, created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			region_code = VALUES(region_code),
			name = VALUES(name),
			status = VALUES(status),
			sort_order = VALUES(sort_order),
			updated_by_user_id = VALUES(updated_by_user_id),
			updated_at_ms = VALUES(updated_at_ms)
	`, region.AppCode, nullableInt64(region.RegionID), region.RegionCode, region.Name, region.Status, region.SortOrder, nullableInt64(region.CreatedByUserID), nullableInt64(region.UpdatedByUserID), region.CreatedAtMs, region.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed region %s failed: %v", region.RegionCode, err)
	}
	if region.RegionID == 0 {
		region.RegionID, err = result.LastInsertId()
		if err != nil {
			r.t.Fatalf("read seeded region %s id failed: %v", region.RegionCode, err)
		}
	}

	countries := make([]string, 0, len(region.Countries))
	for _, input := range region.Countries {
		country := userdomain.NormalizeCountryCode(input)
		countries = append(countries, country)
		if _, err := tx.ExecContext(context.Background(), `
			UPDATE region_countries
			SET status = ?, updated_at_ms = ?
			WHERE app_code = ? AND country_code = ? AND status = ?
		`, "inactive", region.UpdatedAtMs, region.AppCode, country, userdomain.RegionStatusActive); err != nil {
			r.t.Fatalf("deactivate existing region country %s failed: %v", country, err)
		}
		if _, err := tx.ExecContext(context.Background(), `
			INSERT INTO region_countries (app_code, region_id, country_code, status, created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		`, region.AppCode, region.RegionID, country, userdomain.RegionStatusActive, nullableInt64(region.CreatedByUserID), nullableInt64(region.UpdatedByUserID), region.CreatedAtMs, region.UpdatedAtMs); err != nil {
			r.t.Fatalf("seed region %s country %s failed: %v", region.RegionCode, country, err)
		}
		if _, err := tx.ExecContext(context.Background(), `
			INSERT INTO user_region_rebuild_tasks (app_code, region_id, country_code, target_region_id, mapping_revision, status, cursor_user_id, created_at_ms, updated_at_ms)
			VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
		`, region.AppCode, nullableInt64(region.RegionID), country, nullableInt64(region.RegionID), region.UpdatedAtMs, userdomain.RegionRebuildTaskStatusPending, region.CreatedAtMs, region.UpdatedAtMs); err != nil {
			r.t.Fatalf("seed region %s rebuild task for %s failed: %v", region.RegionCode, country, err)
		}
	}
	if err := tx.Commit(); err != nil {
		r.t.Fatalf("commit seed region %s failed: %v", region.RegionCode, err)
	}
	region.Countries = countries
	return region
}

// SetCountryEnabled directly prepares country enabled state for user-service tests.
func (r *Repository) SetCountryEnabled(countryID int64, enabled bool) {
	r.t.Helper()

	result, err := r.schema.DB.ExecContext(context.Background(), `
		UPDATE countries
		SET enabled = ?, updated_at_ms = ?
		WHERE country_id = ?
	`, enabled, time.Now().UnixMilli(), countryID)
	if err != nil {
		r.t.Fatalf("set country %d enabled=%t failed: %v", countryID, enabled, err)
	}
	if affected, err := result.RowsAffected(); err != nil {
		r.t.Fatalf("read country %d affected rows failed: %v", countryID, err)
	} else if affected == 0 {
		r.t.Fatalf("country %d not found", countryID)
	}
}

// PutHostProfile seeds a host profile fact for host-domain tests.
func (r *Repository) PutHostProfile(profile hostdomain.HostProfile) {
	r.t.Helper()

	if profile.Status == "" {
		profile.Status = hostdomain.HostStatusActive
	}
	if profile.Source == "" {
		profile.Source = "test"
	}
	nowMs := profile.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if profile.CreatedAtMs == 0 {
		profile.CreatedAtMs = nowMs
	}
	if profile.FirstBecameHostAtMs == 0 {
		profile.FirstBecameHostAtMs = profile.CreatedAtMs
	}
	profile.UpdatedAtMs = nowMs

	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO host_profiles (
			user_id, status, current_agency_id, current_membership_id, source,
			first_became_host_at_ms, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			status = VALUES(status),
			current_agency_id = VALUES(current_agency_id),
			current_membership_id = VALUES(current_membership_id),
			source = VALUES(source),
			updated_at_ms = VALUES(updated_at_ms)
	`, profile.UserID, profile.Status, nullableInt64(profile.CurrentAgencyID), nullableInt64(profile.CurrentMembershipID), profile.Source, profile.FirstBecameHostAtMs, profile.CreatedAtMs, profile.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed host profile %d failed: %v", profile.UserID, err)
	}
}

// PutBDProfile seeds ordinary BD rows into bd_profiles and BD Leader rows into bd_leader_profiles.
func (r *Repository) PutBDProfile(profile hostdomain.BDProfile) {
	r.t.Helper()

	if profile.Role == "" {
		profile.Role = hostdomain.BDRoleBD
	}
	if profile.Status == "" {
		profile.Status = hostdomain.BDStatusActive
	}
	nowMs := profile.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if profile.CreatedAtMs == 0 {
		profile.CreatedAtMs = nowMs
	}
	profile.UpdatedAtMs = nowMs

	if profile.Role == hostdomain.BDRoleLeader {
		_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO bd_leader_profiles (
			user_id, status, created_by_user_id, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			status = VALUES(status),
			updated_at_ms = VALUES(updated_at_ms)
	`, profile.UserID, profile.Status, profile.CreatedByUserID, profile.CreatedAtMs, profile.UpdatedAtMs)
		if err != nil {
			r.t.Fatalf("seed bd leader profile %d failed: %v", profile.UserID, err)
		}
		return
	}

	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO bd_profiles (
			user_id, role, parent_leader_user_id, parent_owner_user_id, status,
			created_by_user_id, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			role = VALUES(role),
			parent_leader_user_id = VALUES(parent_leader_user_id),
			parent_owner_user_id = VALUES(parent_owner_user_id),
			status = VALUES(status),
			updated_at_ms = VALUES(updated_at_ms)
	`, profile.UserID, profile.Role, nullableInt64(profile.ParentLeaderUserID), nullableInt64(profile.ParentOwnerUserID), profile.Status, profile.CreatedByUserID, profile.CreatedAtMs, profile.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed bd profile %d failed: %v", profile.UserID, err)
	}
}

// PutManagerProfile seeds manager identity rows into manager_profiles.
func (r *Repository) PutManagerProfile(profile hostdomain.ManagerProfile) {
	r.t.Helper()

	if profile.Status == "" {
		profile.Status = hostdomain.ManagerStatusActive
	}
	nowMs := profile.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if profile.CreatedAtMs == 0 {
		profile.CreatedAtMs = nowMs
	}
	profile.UpdatedAtMs = nowMs

	_, err := r.schema.DB.ExecContext(context.Background(), `
			INSERT INTO manager_profiles (
				user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle, can_grant_badge,
				can_grant_vip, can_update_user_level, can_add_bd_leader, can_manage_cross_region, can_add_admin, can_add_superadmin, can_block_user, can_transfer_user_country,
				created_by_admin_id, created_at_ms, updated_at_ms
			) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
			ON DUPLICATE KEY UPDATE
				status = VALUES(status),
				contact_info = VALUES(contact_info),
				can_grant_avatar_frame = VALUES(can_grant_avatar_frame),
				can_grant_vehicle = VALUES(can_grant_vehicle),
				can_grant_badge = VALUES(can_grant_badge),
				can_grant_vip = VALUES(can_grant_vip),
				can_update_user_level = VALUES(can_update_user_level),
			can_add_bd_leader = VALUES(can_add_bd_leader),
			can_manage_cross_region = VALUES(can_manage_cross_region),
			can_add_admin = VALUES(can_add_admin),
			can_add_superadmin = VALUES(can_add_superadmin),
			can_block_user = VALUES(can_block_user),
			can_transfer_user_country = VALUES(can_transfer_user_country),
			updated_at_ms = VALUES(updated_at_ms)
		`, profile.UserID, profile.Status, profile.Contact, defaultTrue(profile.CanGrantAvatarFrame),
		defaultTrue(profile.CanGrantVehicle), defaultTrue(profile.CanGrantBadge), defaultTrue(profile.CanGrantVIP),
		defaultTrue(profile.CanUpdateUserLevel), defaultTrue(profile.CanAddBDLeader), profile.CanManageCrossRegion, defaultTrue(profile.CanAddAdmin),
		defaultTrue(profile.CanAddSuperadmin), defaultTrue(profile.CanBlockUser), defaultTrue(profile.CanTransferCountry),
		profile.CreatedByAdminID, profile.CreatedAtMs, profile.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed manager profile %d failed: %v", profile.UserID, err)
	}
}

func (r *Repository) SetManagerCrossRegion(userID int64, enabled bool) {
	r.t.Helper()

	_, err := r.schema.DB.ExecContext(context.Background(), `
		UPDATE manager_profiles
		SET can_manage_cross_region = ?
		WHERE user_id = ?
	`, enabled, userID)
	if err != nil {
		r.t.Fatalf("set manager region BD Leader permission %d failed: %v", userID, err)
	}
}

func (r *Repository) SetManagerGrantVIP(userID int64, enabled bool) {
	r.t.Helper()

	_, err := r.schema.DB.ExecContext(context.Background(), `
		UPDATE manager_profiles
		SET can_grant_vip = ?
		WHERE user_id = ?
	`, enabled, userID)
	if err != nil {
		r.t.Fatalf("set manager grant vip %d failed: %v", userID, err)
	}
}

// PutAgency seeds an active Agency fact for host-domain tests.
func (r *Repository) PutAgency(agency hostdomain.Agency) {
	r.t.Helper()

	if agency.Status == "" {
		agency.Status = hostdomain.AgencyStatusActive
	}
	if agency.Name == "" {
		agency.Name = "Test Agency"
	}
	nowMs := agency.UpdatedAtMs
	if nowMs == 0 {
		nowMs = time.Now().UnixMilli()
	}
	if agency.CreatedAtMs == 0 {
		agency.CreatedAtMs = nowMs
	}
	agency.UpdatedAtMs = nowMs

	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO agencies (
			agency_id, owner_user_id, parent_bd_user_id, name, avatar, status,
			join_enabled, max_hosts, created_by_user_id, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			owner_user_id = VALUES(owner_user_id),
			parent_bd_user_id = VALUES(parent_bd_user_id),
			name = VALUES(name),
			avatar = VALUES(avatar),
			status = VALUES(status),
			join_enabled = VALUES(join_enabled),
			max_hosts = VALUES(max_hosts),
			updated_at_ms = VALUES(updated_at_ms)
	`, agency.AgencyID, agency.OwnerUserID, agency.ParentBDUserID, agency.Name, agency.Avatar, agency.Status, agency.JoinEnabled, agency.MaxHosts, agency.CreatedByUserID, agency.CreatedAtMs, agency.UpdatedAtMs)
	if err != nil {
		r.t.Fatalf("seed agency %d failed: %v", agency.AgencyID, err)
	}
}

func nullableString(value string) any {
	if strings.TrimSpace(value) == "" {
		return nil
	}
	return value
}

func nullableInt64(value int64) any {
	if value <= 0 {
		return nil
	}
	return value
}

func defaultTrue(value bool) bool {
	if !value {
		return true
	}
	return value
}
