package host

import (
	"context"

	"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 queryBDProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	if err := requireActiveRegion(ctx, tx, command.RegionID); err != nil {
		return hostdomain.BDProfile{}, err
	}
	currentRegionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	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")
	}
	if currentRegionID != command.RegionID {
		if err := updateUserRegion(ctx, tx, command.TargetUserID, command.RegionID, command.NowMs); err != nil {
			return hostdomain.BDProfile{}, err
		}
	}

	profile := hostdomain.BDProfile{
		UserID:          command.TargetUserID,
		Role:            hostdomain.BDRoleLeader,
		RegionID:        command.RegionID,
		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.CommandTypeCreateBDLeader, 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
}

// CreateBD 创建普通 BD，并把它固定到一个有效 BD 负责人名下。
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)
	}

	leader, err := queryBDProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.ParentLeaderUserID)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	if leader.Status != hostdomain.BDStatusActive || leader.Role != hostdomain.BDRoleLeader {
		return hostdomain.BDProfile{}, xerr.New(xerr.PermissionDenied, "parent leader is not active")
	}
	regionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	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 只改 BD 状态，不迁移角色、区域和上级负责人。
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
		}
		return queryBDProfile(ctx, tx, "WHERE user_id = ?", existing.ResultID)
	}

	before, err := queryBDProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.TargetUserID)
	if err != nil {
		return hostdomain.BDProfile{}, err
	}
	after := before
	after.Status = command.Status
	after.UpdatedAtMs = command.NowMs
	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,
		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
	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，并原子创建 owner 的 Host 身份和拥有者成员关系。
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)
	}

	parentBD, err := queryBDProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.ParentBDUserID)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	if parentBD.Status != hostdomain.BDStatusActive {
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.PermissionDenied, "parent bd is not active")
	}
	regionID, err := r.userRegion(ctx, tx, command.OwnerUserID, "FOR UPDATE")
	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")
	}
	if _, ok, err := queryActiveMembershipByHost(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 membership")
	}
	if _, ok, err := queryHostProfileByUserMaybe(ctx, tx, command.OwnerUserID, true); err != nil {
		return hostdomain.CreateAgencyResult{}, err
	} else if ok {
		// 后台直接创建 Agency 会同时创建 owner 的 Host 身份；已存在 Host 身份时拒绝，避免后台把已有主播静默改挂到新 Agency。
		return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "owner already has host profile")
	}

	agency := hostdomain.Agency{AgencyID: command.AgencyID, OwnerUserID: command.OwnerUserID, RegionID: regionID, ParentBDUserID: command.ParentBDUserID, Name: command.Name, Status: hostdomain.AgencyStatusActive, JoinEnabled: command.JoinEnabled, MaxHosts: command.MaxHosts, CreatedByUserID: command.AdminUserID, CreatedAtMs: command.NowMs, UpdatedAtMs: command.NowMs}
	if err := insertAgency(ctx, tx, agency); err != nil {
		return hostdomain.CreateAgencyResult{}, mapHostDuplicateError(err)
	}
	hostProfile, created, 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)
	}
	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 created {
		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 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
}

// 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
	})
}

// 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
	})
}

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
	}
	membership, err := queryAgencyMembership(ctx, q, "WHERE agency_id = ? AND host_user_id = ? AND membership_type = ? AND status = ?", agency.AgencyID, agency.OwnerUserID, hostdomain.MembershipTypeOwner, hostdomain.MembershipStatusActive)
	if err != nil {
		return hostdomain.CreateAgencyResult{}, err
	}
	return hostdomain.CreateAgencyResult{Agency: agency, HostProfile: hostProfile, Membership: membership}, nil
}
