package host

import (
	"context"
	"fmt"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	hostdomain "hyapp/services/user-service/internal/domain/host"
	hostservice "hyapp/services/user-service/internal/service/host"
)

// CreateBDLeader 创建后台直属 BD 负责人；admin 鉴权在 admin-server，这里只保证事实一致性。
func (r *Repository) CreateBDLeader(ctx context.Context, command hostservice.CreateBDLeaderCommand) (hostdomain.BDProfile, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.BDProfile{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeCreateBDLeader, hostdomain.ResultTypeBDProfile); err != nil {
			return hostdomain.BDProfile{}, err
		}
		return queryBDLeaderProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	regionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	if err := requireActiveRegion(ctx, tx, regionID); err != nil {
		return hostdomain.BDProfile{}, err
	}
	// admin 和经理入口都是强制开通：已有 active Leader 直接复用，disabled Leader 恢复 active。
	// 开通 Leader 后再补齐普通 BD 身份；已有 active BD 时绝不覆盖原 parent_leader_user_id。
	profile, leaderEvent, err := ensureActiveBDLeaderProfile(ctx, tx, command.TargetUserID, regionID, command.AdminUserID, command.NowMs)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	if _, bdEvent, err := ensureSelfBDProfileForLeader(ctx, tx, command.TargetUserID, regionID, command.AdminUserID, command.NowMs); err != nil {
		return hostdomain.BDProfile{}, err
	} else if bdEvent != "" {
		if err := insertHostOutbox(ctx, tx, command.EventID+1, bdEvent, "bd_profile", command.TargetUserID, command.NowMs); err != nil {
			return hostdomain.BDProfile{}, err
		}
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeCreateBDLeader, ResultType: hostdomain.ResultTypeBDProfile, ResultID: profile.UserID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.BDProfile{}, mapHostDuplicateError(err)
	}
	if leaderEvent != "" {
		if err := insertHostOutbox(ctx, tx, command.EventID, leaderEvent, "bd_profile", profile.UserID, command.NowMs); err != nil {
			return hostdomain.BDProfile{}, err
		}
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.BDProfile{}, err
	}
	return profile, nil
}

// CreateBD 创建普通 BD；父级 Leader 可选，填入时必须是同区域有效 Leader。
func (r *Repository) CreateBD(ctx context.Context, command hostservice.CreateBDCommand) (hostdomain.BDProfile, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.BDProfile{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeCreateBD, hostdomain.ResultTypeBDProfile); err != nil {
			return hostdomain.BDProfile{}, err
		}
		return queryBDProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	regionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	if command.ParentLeaderUserID > 0 {
		// 独立 BD 不需要父级行锁；只有传入 Leader 时才锁定独立 Leader 表并校验启用状态。
		// Leader 的 RegionID 来自其 users 当前区域投影，不再相信 bd_leader_profiles 的历史快照。
		leader, err := queryBDLeaderProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.ParentLeaderUserID)
		if err != nil {
			return hostdomain.BDProfile{}, err
		}
		if leader.Status != hostdomain.BDStatusActive {
			return hostdomain.BDProfile{}, xerr.New(xerr.PermissionDenied, "parent leader is not active")
		}
		if regionID != leader.RegionID {
			return hostdomain.BDProfile{}, xerr.New(xerr.PermissionDenied, "target region does not match leader region")
		}
	}
	if _, ok, err := queryBDProfileByUserMaybe(ctx, tx, command.TargetUserID, true); err != nil {
		return hostdomain.BDProfile{}, err
	} else if ok {
		return hostdomain.BDProfile{}, xerr.New(xerr.Conflict, "target already has bd profile")
	}

	profile := hostdomain.BDProfile{
		UserID:             command.TargetUserID,
		Role:               hostdomain.BDRoleBD,
		RegionID:           regionID,
		ParentLeaderUserID: command.ParentLeaderUserID,
		Status:             hostdomain.BDStatusActive,
		CreatedByUserID:    command.AdminUserID,
		CreatedAtMs:        command.NowMs,
		UpdatedAtMs:        command.NowMs,
	}
	if err := insertBDProfile(ctx, tx, profile); err != nil {
		return hostdomain.BDProfile{}, mapHostDuplicateError(err)
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeCreateBD, ResultType: hostdomain.ResultTypeBDProfile, ResultID: profile.UserID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.BDProfile{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "BDCreated", "bd_profile", profile.UserID, command.NowMs); err != nil {
		return hostdomain.BDProfile{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.BDProfile{}, err
	}
	return profile, nil
}

// SetBDStatus 按调用方明确传入的角色只改对应身份状态，不迁移角色、区域和上级负责人。
func (r *Repository) SetBDStatus(ctx context.Context, command hostservice.SetBDStatusCommand) (hostdomain.BDProfile, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.BDProfile{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeSetBDStatus, hostdomain.ResultTypeBDProfile); err != nil {
			return hostdomain.BDProfile{}, err
		}
		if command.Role == hostdomain.BDRoleLeader {
			return queryBDLeaderProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
		}
		return queryBDProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	var before hostdomain.BDProfile
	switch command.Role {
	case hostdomain.BDRoleBD:
		// 普通 BD 和 Leader 已经拆表；状态路由传 bd 时只锁普通 BD 行。
		before, err = queryBDProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.TargetUserID)
	case hostdomain.BDRoleLeader:
		// 状态路由传 bd_leader 时只锁 Leader 行，避免同用户的普通 BD 钱包权限被误停。
		before, err = queryBDLeaderProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.TargetUserID)
	default:
		err = xerr.New(xerr.InvalidArgument, "bd role is invalid")
	}
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	after := before
	after.Status = command.Status
	after.UpdatedAtMs = command.NowMs
	if command.Role == hostdomain.BDRoleLeader {
		if err := updateBDLeaderProfileStatus(ctx, tx, after); err != nil {
			return hostdomain.BDProfile{}, err
		}
	} else {
		if err := updateBDProfileStatus(ctx, tx, after); err != nil {
			return hostdomain.BDProfile{}, err
		}
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeSetBDStatus, ResultType: hostdomain.ResultTypeBDProfile, ResultID: after.UserID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.BDProfile{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "BDStatusChanged", "bd_profile", after.UserID, command.NowMs); err != nil {
		return hostdomain.BDProfile{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.BDProfile{}, err
	}
	return after, nil
}

// CreateCoinSeller 创建币商身份；币商可以叠加在 Agency/BD/BD Leader 用户上。
func (r *Repository) CreateCoinSeller(ctx context.Context, command hostservice.CreateCoinSellerCommand) (hostdomain.CoinSellerProfile, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeCreateCoinSeller, hostdomain.ResultTypeCoinSellerProfile); err != nil {
			return hostdomain.CoinSellerProfile{}, err
		}
		return queryCoinSellerProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	if _, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE"); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	if _, ok, err := queryCoinSellerProfileByUserMaybe(ctx, tx, command.TargetUserID, true); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	} else if ok {
		return hostdomain.CoinSellerProfile{}, xerr.New(xerr.Conflict, "target already has coin_seller profile")
	}

	profile := hostdomain.CoinSellerProfile{
		UserID:                  command.TargetUserID,
		Status:                  hostdomain.CoinSellerStatusActive,
		MerchantAssetType:       hostdomain.CoinSellerMerchantAssetType,
		CanManageSubCoinSellers: boolValueDefaultTrue(command.CanManageSubCoinSellers),
		CreatedByUserID:         command.AdminUserID,
		CreatedAtMs:             command.NowMs,
		UpdatedAtMs:             command.NowMs,
	}
	if err := insertCoinSellerProfile(ctx, tx, profile); err != nil {
		return hostdomain.CoinSellerProfile{}, mapHostDuplicateError(err)
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeCreateCoinSeller, ResultType: hostdomain.ResultTypeCoinSellerProfile, ResultID: profile.UserID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.CoinSellerProfile{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "CoinSellerCreated", "coin_seller_profile", profile.UserID, command.NowMs); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	return profile, nil
}

// SetCoinSellerStatus 启用或停用币商身份，不改币商钱包余额。
func (r *Repository) SetCoinSellerStatus(ctx context.Context, command hostservice.SetCoinSellerStatusCommand) (hostdomain.CoinSellerProfile, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeSetCoinSellerStatus, hostdomain.ResultTypeCoinSellerProfile); err != nil {
			return hostdomain.CoinSellerProfile{}, err
		}
		return queryCoinSellerProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	before, err := queryCoinSellerProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.TargetUserID)
	if err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	after := before
	after.Status = command.Status
	if command.CanManageSubCoinSellers != nil {
		after.CanManageSubCoinSellers = *command.CanManageSubCoinSellers
	}
	after.UpdatedAtMs = command.NowMs
	if err := updateCoinSellerProfileStatus(ctx, tx, after); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeSetCoinSellerStatus, ResultType: hostdomain.ResultTypeCoinSellerProfile, ResultID: after.UserID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.CoinSellerProfile{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "CoinSellerStatusChanged", "coin_seller_profile", after.UserID, command.NowMs); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.CoinSellerProfile{}, err
	}
	return after, nil
}

// CreateAgency 后台直建 Agency；父级 BD 可选，填入时必须是同区域有效 BD。
func (r *Repository) CreateAgency(ctx context.Context, command hostservice.CreateAgencyCommand) (hostdomain.CreateAgencyResult, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeCreateAgency, hostdomain.ResultTypeAgency); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
		return createAgencyResultByAgencyID(ctx, tx, existing.ResultID)
	}

	regionID, err := r.userRegion(ctx, tx, command.OwnerUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if command.ParentBDUserID > 0 {
		// 独立 Agency 不需要父级行锁；传入父级时可挂普通 BD 或 Leader 自身，但必须是有效身份且同区域。
		// 父级 RegionID 来自其 users 当前区域投影，避免角色表旧快照影响新 Agency 归属。
		parentBD, err := queryActiveAgencyInviterProfile(ctx, tx, command.ParentBDUserID)
		if err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
		if regionID != parentBD.RegionID {
			return hostdomain.CreateAgencyResult{}, xerr.New(xerr.PermissionDenied, "owner region does not match parent bd region")
		}
	}
	if _, ok, err := queryActiveAgencyByOwner(ctx, tx, command.OwnerUserID, true); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "owner already has active agency")
	}
	// 后台直建 Agency 是对角色归属的最终确认；目标先前收到的 Agency 邀请已经不可再接受。
	// 必须在同一事务关闭 pending 消息，否则消息中心会长期展示一个点击后必然冲突的旧邀请。
	if pending, ok, err := queryPendingRoleInvitationByTarget(ctx, tx, hostdomain.InvitationTypeAgency, command.OwnerUserID, true); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		pending.Status = hostdomain.InvitationStatusCancelled
		pending.ProcessedByUserID = command.AdminUserID
		pending.ProcessReason = "superseded by admin agency creation"
		pending.ProcessedAtMs = command.NowMs
		pending.UpdatedAtMs = command.NowMs
		if err := updateRoleInvitationProcessed(ctx, tx, pending); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
	}
	activeMembership, hasActiveMembership, err := queryActiveMembershipByHost(ctx, tx, command.OwnerUserID, true)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}

	agencyName, agencyAvatar, err := defaultAgencyProfileFromOwner(ctx, tx, command.OwnerUserID, command.Name)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	// max_hosts 是历史兼容字段，当前业务不再限制主播数量，新建 Agency 固化为 0。
	agency := hostdomain.Agency{AgencyID: command.AgencyID, OwnerUserID: command.OwnerUserID, RegionID: regionID, ParentBDUserID: command.ParentBDUserID, Name: agencyName, Avatar: agencyAvatar, Status: hostdomain.AgencyStatusActive, JoinEnabled: command.JoinEnabled, MaxHosts: 0, CreatedByUserID: command.AdminUserID, CreatedAtMs: command.NowMs, UpdatedAtMs: command.NowMs}
	if err := insertAgency(ctx, tx, agency); err != nil {
		return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
	}
	var hostProfile hostdomain.HostProfile
	var membership hostdomain.AgencyMembership
	var hostProfileChanged bool
	var membershipCreated bool
	if hasActiveMembership {
		// owner 已经是其他 Agency 的 active Host 时，只开启 Agency owner 身份；
		// Host 当前归属继续指向原 membership，避免覆盖既有 Agency 关系。
		hostProfile, hostProfileChanged, err = ensureHostProfileForExistingMembership(ctx, tx, command.OwnerUserID, regionID, activeMembership, hostdomain.HostSourceAdminCreateAgency, command.NowMs)
		if err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
		membership = activeMembership
	} else {
		hostProfile, hostProfileChanged, err = ensureHostProfileForMembership(ctx, tx, command.OwnerUserID, regionID, command.MembershipID, agency.AgencyID, hostdomain.HostSourceAdminCreateAgency, command.NowMs)
		if err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
		membership = hostdomain.AgencyMembership{MembershipID: command.MembershipID, AgencyID: agency.AgencyID, HostUserID: command.OwnerUserID, RegionID: regionID, MembershipType: hostdomain.MembershipTypeOwner, Status: hostdomain.MembershipStatusActive, JoinedAtMs: command.NowMs, CreatedAtMs: command.NowMs, UpdatedAtMs: command.NowMs}
		if err := insertAgencyMembership(ctx, tx, membership); err != nil {
			return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
		}
		membershipCreated = true
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeCreateAgency, ResultType: hostdomain.ResultTypeAgency, ResultID: agency.AgencyID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
	}
	if hostProfileChanged {
		if err := insertHostOutbox(ctx, tx, command.EventID, "HostCreated", "host_profile", hostProfile.UserID, command.NowMs); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
	}
	if err := insertHostOutbox(ctx, tx, command.EventID+1, "AgencyCreated", "agency", agency.AgencyID, command.NowMs); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if membershipCreated {
		if err := insertHostOutbox(ctx, tx, command.EventID+2, "AgencyMembershipChanged", "agency_membership", membership.MembershipID, command.NowMs); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	return hostdomain.CreateAgencyResult{Agency: agency, HostProfile: hostProfile, Membership: membership}, nil
}

// AdminAddAgencyHost 后台直接添加普通 Host 成员；它复用 Agency 成员关系事实，不走 App 申请/邀请收件箱。
func (r *Repository) AdminAddAgencyHost(ctx context.Context, command hostservice.AdminAddAgencyHostCommand) (hostdomain.CreateAgencyResult, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeAdminAddAgencyHost, hostdomain.ResultTypeMembership); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
		return agencyHostResultByMembershipID(ctx, tx, existing.ResultID)
	}

	// Agency 是被添加成员的归属 owner；锁住它可以和状态切换、删除 Agency、批量结束成员串行。
	agency, err := queryAgency(ctx, tx, "WHERE agency_id = ? FOR UPDATE", command.AgencyID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if agency.Status != hostdomain.AgencyStatusActive {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "agency is not active")
	}

	// 目标用户区域是 user-service 当前事实；直接添加不相信 admin 传入区域，避免跨区挂错团队。
	targetRegionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if targetRegionID != agency.RegionID {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.RegionMismatch, "target region does not match agency")
	}
	if _, ok, err := queryActiveMembershipByHost(ctx, tx, command.TargetUserID, true); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "target already has active agency membership")
	}
	if _, ok, err := queryActiveAgencyByOwner(ctx, tx, command.TargetUserID, true); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "target already owns active agency")
	}

	hostProfile, hostProfileChanged, err := ensureHostProfileForMembership(ctx, tx, command.TargetUserID, targetRegionID, command.MembershipID, agency.AgencyID, hostdomain.HostSourceAdminAddHost, command.NowMs)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	membership := hostdomain.AgencyMembership{
		MembershipID:   command.MembershipID,
		AgencyID:       agency.AgencyID,
		HostUserID:     command.TargetUserID,
		RegionID:       targetRegionID,
		MembershipType: hostdomain.MembershipTypeMember,
		Status:         hostdomain.MembershipStatusActive,
		JoinedAtMs:     command.NowMs,
		CreatedAtMs:    command.NowMs,
		UpdatedAtMs:    command.NowMs,
	}
	if err := insertAgencyMembership(ctx, tx, membership); err != nil {
		return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeAdminAddAgencyHost, ResultType: hostdomain.ResultTypeMembership, ResultID: membership.MembershipID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
	}
	if hostProfileChanged {
		if err := insertHostOutbox(ctx, tx, command.EventID, "HostCreated", "host_profile", hostProfile.UserID, command.NowMs); err != nil {
			return hostdomain.CreateAgencyResult{}, err
		}
	}
	if err := insertHostOutbox(ctx, tx, command.EventID+1, "AgencyMembershipChanged", "agency_membership", membership.MembershipID, command.NowMs); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	return hostdomain.CreateAgencyResult{Agency: agency, HostProfile: hostProfile, Membership: membership}, nil
}

// CloseAgency 关闭 Agency 本体和入会开关，不回写历史 membership。
func (r *Repository) CloseAgency(ctx context.Context, command hostservice.CloseAgencyCommand) (hostdomain.Agency, error) {
	return r.updateAgencyAdmin(ctx, command.CommandID, hostdomain.CommandTypeCloseAgency, command.AgencyID, command.EventID, command.NowMs, func(agency hostdomain.Agency) hostdomain.Agency {
		agency.Status = hostdomain.AgencyStatusClosed
		agency.JoinEnabled = false
		return agency
	})
}

// DeleteAgency 删除 Agency 身份；历史 Agency、membership 和 host_profile 都保留，只解除当前有效归属。
func (r *Repository) DeleteAgency(ctx context.Context, command hostservice.DeleteAgencyCommand) (hostdomain.Agency, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.Agency{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeDeleteAgency, hostdomain.ResultTypeAgency); err != nil {
			return hostdomain.Agency{}, err
		}
		return queryAgency(ctx, tx, "WHERE agency_id = ?", existing.ResultID)
	}

	agency, err := queryAgency(ctx, tx, "WHERE agency_id = ? FOR UPDATE", command.AgencyID)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	if agency.Status == hostdomain.AgencyStatusDeleted {
		return hostdomain.Agency{}, xerr.New(xerr.Conflict, "agency already deleted")
	}

	activeMemberships, err := queryAgencyMemberships(ctx, tx, fmt.Sprintf(`
		SELECT %s
		FROM agency_memberships
		WHERE app_code = ? AND agency_id = ? AND status = ?
		FOR UPDATE
	`, agencyMembershipColumns), appcode.FromContext(ctx), agency.AgencyID, hostdomain.MembershipStatusActive)
	if err != nil {
		return hostdomain.Agency{}, err
	}

	endReason := command.Reason
	if endReason == "" {
		endReason = "delete agency"
	}
	for _, membership := range activeMemberships {
		hostProfile, err := queryHostProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", membership.HostUserID)
		if err != nil {
			return hostdomain.Agency{}, err
		}
		membership.Status = hostdomain.MembershipStatusEnded
		membership.EndedAtMs = command.NowMs
		membership.EndedByUserID = command.AdminUserID
		membership.EndedReason = endReason
		membership.UpdatedAtMs = command.NowMs
		if err := endAgencyMembership(ctx, tx, membership); err != nil {
			return hostdomain.Agency{}, err
		}
		if hostProfile.CurrentMembershipID == membership.MembershipID {
			// 删除 Agency 只释放当前归属指针，主播身份、来源和首次成为主播时间都保留。
			hostProfile.CurrentAgencyID = 0
			hostProfile.CurrentMembershipID = 0
			hostProfile.UpdatedAtMs = command.NowMs
			if err := updateHostCurrentMembership(ctx, tx, hostProfile); err != nil {
				return hostdomain.Agency{}, err
			}
		}
	}

	agency.Status = hostdomain.AgencyStatusDeleted
	agency.JoinEnabled = false
	agency.UpdatedAtMs = command.NowMs
	if err := updateAgencyAdminState(ctx, tx, agency); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeDeleteAgency, ResultType: hostdomain.ResultTypeAgency, ResultID: agency.AgencyID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.Agency{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "AgencyDeleted", "agency", agency.AgencyID, command.NowMs); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.Agency{}, err
	}
	return agency, nil
}

// SetAgencyStatus 显式切换 Agency 状态；关闭会同步关入会，恢复只恢复组织本体，避免误把申请入口重新放开。
func (r *Repository) SetAgencyStatus(ctx context.Context, command hostservice.SetAgencyStatusCommand) (hostdomain.Agency, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.Agency{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeSetAgencyStatus, hostdomain.ResultTypeAgency); err != nil {
			return hostdomain.Agency{}, err
		}
		return queryAgency(ctx, tx, "WHERE agency_id = ?", existing.ResultID)
	}

	agency, err := queryAgency(ctx, tx, "WHERE agency_id = ? FOR UPDATE", command.AgencyID)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	if agency.Status == hostdomain.AgencyStatusDeleted {
		// deleted 已经解除当前 Agency 归属，是删除事实；重新激活会破坏 host_profile 当前归属和历史 membership 语义。
		return hostdomain.Agency{}, xerr.New(xerr.Conflict, "deleted agency cannot be reopened")
	}

	agency.Status = command.Status
	if command.Status == hostdomain.AgencyStatusClosed {
		// 关闭组织必须同时停止 App 申请入口；重新 active 时保留当前 join_enabled，让管理员显式决定是否开放入会。
		agency.JoinEnabled = false
	}
	agency.UpdatedAtMs = command.NowMs
	if err := updateAgencyAdminState(ctx, tx, agency); err != nil {
		return hostdomain.Agency{}, mapHostDuplicateError(err)
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeSetAgencyStatus, ResultType: hostdomain.ResultTypeAgency, ResultID: agency.AgencyID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.Agency{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "AgencyChanged", "agency", agency.AgencyID, command.NowMs); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.Agency{}, err
	}
	return agency, nil
}

// SetAgencyJoinEnabled 调整 Agency 入会开关。
func (r *Repository) SetAgencyJoinEnabled(ctx context.Context, command hostservice.SetAgencyJoinEnabledCommand) (hostdomain.Agency, error) {
	return r.updateAgencyAdmin(ctx, command.CommandID, hostdomain.CommandTypeSetAgencyJoinEnabled, command.AgencyID, command.EventID, command.NowMs, func(agency hostdomain.Agency) hostdomain.Agency {
		agency.JoinEnabled = command.JoinEnabled
		return agency
	})
}

// UpdateAgencyProfile 只允许 active Agency owner 修改组织独立资料。
func (r *Repository) UpdateAgencyProfile(ctx context.Context, command hostservice.UpdateAgencyProfileCommand) (hostdomain.Agency, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
		return hostdomain.Agency{}, err
	} else if ok {
		if err := requireCommandResult(existing, hostdomain.CommandTypeUpdateAgencyProfile, hostdomain.ResultTypeAgency); err != nil {
			return hostdomain.Agency{}, err
		}
		return queryAgency(ctx, tx, "WHERE agency_id = ?", existing.ResultID)
	}

	agency, err := queryAgency(ctx, tx, "WHERE agency_id = ? FOR UPDATE", command.AgencyID)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	if agency.Status != hostdomain.AgencyStatusActive || agency.OwnerUserID != command.OperatorUserID {
		// Agency 资料的写权限只属于当前 active owner；普通成员、关闭团队和历史 owner 都不能改。
		return hostdomain.Agency{}, xerr.New(xerr.PermissionDenied, "permission denied")
	}
	if command.Name != nil {
		agency.Name = *command.Name
	}
	if command.Avatar != nil {
		agency.Avatar = *command.Avatar
	}
	agency.UpdatedAtMs = command.NowMs
	if err := updateAgencyProfile(ctx, tx, agency); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: command.CommandID, CommandType: hostdomain.CommandTypeUpdateAgencyProfile, ResultType: hostdomain.ResultTypeAgency, ResultID: agency.AgencyID, CreatedAtMs: command.NowMs}); err != nil {
		return hostdomain.Agency{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, command.EventID, "AgencyChanged", "agency", agency.AgencyID, command.NowMs); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.Agency{}, err
	}
	return agency, nil
}

func (r *Repository) updateAgencyAdmin(ctx context.Context, commandID string, commandType string, agencyID int64, eventID int64, nowMs int64, mutate func(hostdomain.Agency) hostdomain.Agency) (hostdomain.Agency, error) {
	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	defer tx.Rollback()

	if existing, ok, err := commandResult(ctx, tx, commandID); err != nil {
		return hostdomain.Agency{}, err
	} else if ok {
		if err := requireCommandResult(existing, commandType, hostdomain.ResultTypeAgency); err != nil {
			return hostdomain.Agency{}, err
		}
		return queryAgency(ctx, tx, "WHERE agency_id = ?", existing.ResultID)
	}
	before, err := queryAgency(ctx, tx, "WHERE agency_id = ? FOR UPDATE", agencyID)
	if err != nil {
		return hostdomain.Agency{}, err
	}
	after := mutate(before)
	after.UpdatedAtMs = nowMs
	if err := updateAgencyAdminState(ctx, tx, after); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{CommandID: commandID, CommandType: commandType, ResultType: hostdomain.ResultTypeAgency, ResultID: after.AgencyID, CreatedAtMs: nowMs}); err != nil {
		return hostdomain.Agency{}, mapHostDuplicateError(err)
	}
	if err := insertHostOutbox(ctx, tx, eventID, "AgencyChanged", "agency", after.AgencyID, nowMs); err != nil {
		return hostdomain.Agency{}, err
	}
	if err := tx.Commit(); err != nil {
		return hostdomain.Agency{}, err
	}
	return after, nil
}

func createAgencyResultByAgencyID(ctx context.Context, q sqlQueryer, agencyID int64) (hostdomain.CreateAgencyResult, error) {
	agency, err := queryAgency(ctx, q, "WHERE agency_id = ?", agencyID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	hostProfile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", agency.OwnerUserID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	var membership hostdomain.AgencyMembership
	if hostProfile.CurrentMembershipID > 0 {
		// 新规则允许 Agency owner 已经是其他 Agency 的 Host 成员；
		// 幂等回放返回用户当前 Host 归属，而不是强行要求新 Agency 存在 owner membership。
		membership, _ = queryAgencyMembership(ctx, q, "WHERE membership_id = ?", hostProfile.CurrentMembershipID)
	}
	return hostdomain.CreateAgencyResult{Agency: agency, HostProfile: hostProfile, Membership: membership}, nil
}

func agencyHostResultByMembershipID(ctx context.Context, q sqlQueryer, membershipID int64) (hostdomain.CreateAgencyResult, error) {
	membership, err := queryAgencyMembership(ctx, q, "WHERE membership_id = ?", membershipID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	agency, err := queryAgency(ctx, q, "WHERE agency_id = ?", membership.AgencyID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	hostProfile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", membership.HostUserID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	return hostdomain.CreateAgencyResult{Agency: agency, HostProfile: hostProfile, Membership: membership}, nil
}
