import {
  BadRequestException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectConnection } from '@nestjs/mongoose';
import { ConfigService } from '@nestjs/config';
import { Connection, Types } from 'mongoose';
import { ModerationStatus } from '../../common/enums/moderation-status.enum';
import { PostVisibility } from '../../common/enums/post-visibility.enum';
import { ExperienceLevel } from '../../common/enums/experience-level.enum';
import { MusicRole } from '../../common/enums/music-role.enum';
import { assertAllowedMediaFile, MEDIA_MAX_SIZE_BYTES } from '../../common/media/allowed-media';
import { hashValue } from '../../common/utils/hash.util';
import { buildPaginatedResponse } from '../../common/utils/pagination.util';
import { resolveManagedFileUrl } from '../../common/utils/public-url.util';
import {
  buildProfileShareUrl as buildCanonicalProfileShareUrl,
  resolveShareBaseUrl,
} from '../../common/utils/share-url.util';
import { resolveMongoSortDirection } from '../../common/utils/sort.util';
import { escapeRegex } from '../../common/utils/regex.util';
import { ManagedStorageService } from '../../infrastructure/storage/managed-storage.service';
import { FeedVersionService } from '../../infrastructure/cache/feed-version.service';
import { AuditService } from '../audit/audit.service';
import { UserRole } from '../../common/enums/user-role.enum';
import { CreateUserDto } from './dto/create-user.dto';
import { AdminDisableUserDto } from './dto/admin-disable-user.dto';
import {
  ArtistDashboardRecentActivityItem,
  ArtistDashboardResponse,
  ArtistDashboardTopContentItem,
} from './dto/artist-dashboard-response.dto';
import { CreateAdminDto } from './dto/create-admin.dto';
import { MusicSetupDto } from './dto/music-setup.dto';
import { ProfileSetupDto } from './dto/profile-setup.dto';
import { ShareFriendsQueryDto } from './dto/share-friends-query.dto';
import { TalentDiscoverQueryDto } from './dto/talent-discover-query.dto';
import { UpdateUserRoleDto } from './dto/update-user-role.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UserQueryDto } from './dto/user-query.dto';
import { UsersRepository } from './users.repository';
import { UserDocument } from './schemas/user.schema';

type UploadedImageFile = {
  mimetype?: string;
  size: number;
  buffer: Buffer;
  originalname?: string;
};

type UserPostStats = {
  postsCount: number;
  viewCount: number;
  playCount: number;
  likesCount: number;
  commentsCount: number;
  savesCount: number;
  sharesCount: number;
};

@Injectable()
export class UsersService {
  constructor(
    @InjectConnection() private readonly connection: Connection,
    private readonly usersRepository: UsersRepository,
    private readonly auditService: AuditService,
    private readonly configService: ConfigService,
    private readonly storageService: ManagedStorageService,
    private readonly feedVersionService: FeedVersionService,
  ) {}

  async create(dto: CreateUserDto & { password: string; role?: UserRole }): Promise<UserDocument> {
    const existing = await this.usersRepository.findOne({
      $or: [{ email: dto.email.toLowerCase() }, { username: dto.username.toLowerCase() }],
    });

    if (existing) {
      throw new BadRequestException('Email or username already exists');
    }

    const roles = dto.musicRoles ?? [];

    return this.usersRepository.create({
      ...dto,
      email: dto.email.toLowerCase(),
      username: dto.username.toLowerCase(),
      stageName: dto.stageName ?? '',
      bio: dto.bio ?? '',
      avatar: dto.avatar ?? '',
      coverImage: dto.coverImage ?? '',
      location: dto.location ?? '',
      latitude: dto.latitude,
      longitude: dto.longitude,
      isPrivate: dto.isPrivate ?? false,
      isVerified: dto.isVerified ?? false,
      isEmailVerified: dto.isEmailVerified ?? false,
      musicRoles: roles,
      experienceLevel: dto.experienceLevel ?? ExperienceLevel.BEGINNER,
      musicGenres: dto.musicGenres ?? [],
      favoriteInstruments: dto.favoriteInstruments ?? [],
      favoriteMaqamat: dto.favoriteMaqamat ?? [],
      preferredMood: dto.preferredMood ?? '',
      role: dto.role ?? UserRole.USER,
      isDisabled: false,
      disabledReason: '',
      authProvider: 'local',
    });
  }

  async createWithOptionalAvatarFile(
    dto: CreateUserDto & { password: string; role?: UserRole },
    avatarFile?: UploadedImageFile,
  ): Promise<UserDocument> {
    let uploadedAvatarUrl = '';

    try {
      if (avatarFile) {
        uploadedAvatarUrl = await this.saveAvatarFile(avatarFile);
      }

      return await this.create({
        ...dto,
        ...(uploadedAvatarUrl ? { avatar: uploadedAvatarUrl } : {}),
      });
    } catch (error) {
      if (uploadedAvatarUrl) {
        await this.deleteManagedUpload(uploadedAvatarUrl);
      }
      throw error;
    }
  }

  async rollbackSignupCreatedUser(userId: string, avatarUrl?: string): Promise<void> {
    await this.usersRepository.deleteById(userId);
    if (avatarUrl) {
      await this.deleteManagedUpload(avatarUrl);
    }
  }

  async createAdminBySuperAdmin(
    superAdminIdentifier: string,
    dto: CreateAdminDto,
  ): Promise<UserDocument> {
    if (dto.password !== dto.confirmPassword) {
      throw new BadRequestException('Password confirmation does not match');
    }

    const saltRounds = this.configService.get<number>('security.bcryptSaltRounds', { infer: true });
    const passwordHash = await hashValue(dto.password, saltRounds);

    const admin = await this.create({
      name: dto.name,
      username: dto.username,
      email: dto.email,
      password: passwordHash,
      role: UserRole.ADMIN,
      isVerified: true,
    });

    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'admin_create',
      'user',
      admin.id,
      { role: UserRole.ADMIN },
    );

    return admin;
  }

  async listAdminsBySuperAdmin(query: UserQueryDto): Promise<{
    items: UserDocument[];
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  }> {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter: Record<string, unknown> = {
      role: UserRole.ADMIN,
    };

    if (query.q) {
      filter.$or = [
        { name: { $regex: escapeRegex(query.q), $options: 'i' } },
        { username: { $regex: escapeRegex(query.q), $options: 'i' } },
        { email: { $regex: escapeRegex(query.q), $options: 'i' } },
      ];
    }

    if (typeof query.isVerified === 'boolean') {
      filter.isVerified = query.isVerified;
    }

    const direction = resolveMongoSortDirection(query.sortOrder);
    const sortField = query.sortBy ?? 'createdAt';
    const sort = { [sortField]: direction } as Record<string, 1 | -1>;

    const [items, total] = await Promise.all([
      this.usersRepository.findMany(filter, skip, limit, sort),
      this.usersRepository.count(filter),
    ]);

    return buildPaginatedResponse(items, {
      page,
      limit,
      total,
      offset: skip,
    });
  }

  async updateAdminBySuperAdmin(
    superAdminIdentifier: string,
    adminUserId: string,
    dto: UpdateUserDto,
  ): Promise<UserDocument> {
    await this.assertTargetIsAdmin(adminUserId);
    const payload = await this.prepareManagedUserUpdatePayload(adminUserId, dto);
    const updated = await this.updateUserAndCleanupReplacedImages(adminUserId, payload);

    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'admin_update',
      'user',
      adminUserId,
      { fields: Object.keys(payload) },
    );

    return updated;
  }

  async deleteAdminBySuperAdmin(superAdminIdentifier: string, adminUserId: string): Promise<void> {
    const admin = await this.assertTargetIsAdmin(adminUserId);
    await this.deleteUserRelatedData(adminUserId, {
      avatarUrl: admin.avatar ?? '',
      coverImageUrl: admin.coverImage ?? '',
    });

    await this.usersRepository.deleteById(adminUserId);

    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'admin_delete',
      'user',
      adminUserId,
    );
  }

  async updateUserRoleBySuperAdmin(
    superAdminIdentifier: string,
    targetUserId: string,
    dto: UpdateUserRoleDto,
  ): Promise<UserDocument> {
    if (dto.role === UserRole.SUPERADMIN) {
      throw new BadRequestException('Cannot assign superadmin role via API');
    }

    await this.assertTargetIsManagedUser(targetUserId);
    const updated = await this.usersRepository.updateById(targetUserId, { role: dto.role });
    if (!updated) {
      throw new NotFoundException('User not found');
    }

    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'user_role_update',
      'user',
      targetUserId,
      { role: dto.role },
    );

    return updated;
  }

  async findByIdOrFail(userId: string): Promise<UserDocument> {
    const user = await this.usersRepository.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }
    return user;
  }

  async findPublicByIdOrFail(userId: string): Promise<UserDocument> {
    const user = await this.findByIdOrFail(userId);
    if (user.isDisabled) {
      throw new NotFoundException('User not found');
    }
    return user;
  }

  async findPublicByIdForViewer(userId: string, viewerUserId: string) {
    const user = await this.findPublicByIdOrFail(userId);
    const isFollowing =
      viewerUserId === userId
        ? false
        : (await this.connection.collection('follows').countDocuments({
            followerId: new Types.ObjectId(viewerUserId),
            followingId: new Types.ObjectId(userId),
          })) > 0;

    return {
      ...user.toObject(),
      isFollowing,
      following: isFollowing,
      isOwnProfile: viewerUserId === userId,
    };
  }

  async getPresence(userId: string) {
    const user = await this.findPublicByIdOrFail(userId);
    return {
      userId: user.id,
      isOnline: user.isOnline ?? false,
      lastSeenAt: user.lastSeenAt ?? null,
    };
  }

  async setPresence(userId: string, isOnline: boolean): Promise<void> {
    if (!Types.ObjectId.isValid(userId)) {
      return;
    }
    await this.usersRepository.setPresence(userId, isOnline, new Date());
  }

  async findByEmailWithPassword(email: string): Promise<UserDocument | null> {
    return this.usersRepository.findOneWithPassword({ email: email.toLowerCase() });
  }

  async findByIdWithPassword(userId: string): Promise<UserDocument | null> {
    return this.usersRepository.findByIdWithPassword(userId);
  }

  async findByEmail(email: string): Promise<UserDocument | null> {
    return this.usersRepository.findOne({ email: email.toLowerCase() });
  }

  async findByUsername(username: string): Promise<UserDocument | null> {
    return this.usersRepository.findOne({ username: username.toLowerCase() });
  }

  async findByGoogleId(googleId: string): Promise<UserDocument | null> {
    return this.usersRepository.findOne({ googleId });
  }

  async linkGoogleAccount(userId: string, googleId: string, avatar?: string): Promise<UserDocument> {
    const currentUser = await this.findByIdOrFail(userId);
    const user = await this.updateUserAndCleanupReplacedImages(
      userId,
      {
        googleId,
        authProvider: 'google',
        ...(avatar ? { avatar } : {}),
      },
      { currentUser },
    );

    return user;
  }

  async updateProfile(
    userId: string,
    dto: UpdateUserDto,
    avatarFile?: UploadedImageFile,
    coverImageFile?: UploadedImageFile,
  ): Promise<UserDocument> {
    const currentUser = await this.findByIdOrFail(userId);
    const payload: Record<string, unknown> = { ...dto };
    const uploadedImageUrls = await this.attachUploadedProfileImages(
      payload,
      avatarFile,
      coverImageFile,
    );

    return this.updateUserAndCleanupReplacedImages(userId, payload, {
      currentUser,
      uploadedImageUrls,
    });
  }

  async updatePassword(userId: string, passwordHash: string): Promise<void> {
    const updated = await this.usersRepository.updateById(userId, { password: passwordHash });
    if (!updated) {
      throw new NotFoundException('User not found');
    }
  }

  async markEmailVerified(userId: string): Promise<void> {
    const updated = await this.usersRepository.updateById(userId, { isEmailVerified: true });
    if (!updated) {
      throw new NotFoundException('User not found');
    }
  }

  async findUserByIdForSuperAdmin(targetUserId: string): Promise<UserDocument> {
    return this.assertTargetIsManagedUser(targetUserId);
  }

  async updateUserBySuperAdmin(
    superAdminIdentifier: string,
    targetUserId: string,
    dto: UpdateUserDto,
  ): Promise<UserDocument> {
    await this.assertTargetIsManagedUser(targetUserId);
    const payload = await this.prepareManagedUserUpdatePayload(targetUserId, dto);
    const user = await this.updateUserAndCleanupReplacedImages(targetUserId, payload);
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'user_update',
      'user',
      targetUserId,
      { fields: Object.keys(payload) },
    );
    return user;
  }

  private async prepareManagedUserUpdatePayload(
    targetUserId: string,
    dto: UpdateUserDto,
  ): Promise<Record<string, unknown>> {
    const payload: Record<string, unknown> = { ...dto };

    if (typeof dto.email === 'string') {
      const normalizedEmail = dto.email.trim().toLowerCase();
      if (!normalizedEmail) {
        throw new BadRequestException('Email is required');
      }

      const existingUser = await this.usersRepository.findOne({
        email: normalizedEmail,
        _id: { $ne: targetUserId },
      });
      if (existingUser) {
        throw new BadRequestException('Email already exists');
      }

      payload.email = normalizedEmail;
    }

    if (typeof dto.username === 'string') {
      const normalizedUsername = dto.username.trim().toLowerCase();
      if (!normalizedUsername) {
        throw new BadRequestException('Username is required');
      }

      const existingUser = await this.usersRepository.findOne({
        username: normalizedUsername,
        _id: { $ne: targetUserId },
      });
      if (existingUser) {
        throw new BadRequestException('Username already exists');
      }

      payload.username = normalizedUsername;
    }

    return payload;
  }

  async updateProfileSetup(
    userId: string,
    dto: ProfileSetupDto,
    avatarFile?: UploadedImageFile,
    coverImageFile?: UploadedImageFile,
  ): Promise<UserDocument> {
    if (!Number.isFinite(dto.latitude) || !Number.isFinite(dto.longitude)) {
      throw new BadRequestException('latitude and longitude are required');
    }

    const currentUser = await this.findByIdOrFail(userId);
    const payload: Record<string, unknown> = { ...dto };
    const uploadedImageUrls = await this.attachUploadedProfileImages(
      payload,
      avatarFile,
      coverImageFile,
    );

    return this.updateUserAndCleanupReplacedImages(userId, payload, {
      currentUser,
      uploadedImageUrls,
    });
  }

  private async saveAvatarFile(avatarFile: UploadedImageFile): Promise<string> {
    return this.saveProfileImageFile(avatarFile, {
      folderSegments: ['users', 'avatar'],
      fileFieldName: 'avatarFile',
      fileNamePrefix: 'avatar',
    });
  }

  private async saveCoverImageFile(coverImageFile: UploadedImageFile): Promise<string> {
    return this.saveProfileImageFile(coverImageFile, {
      folderSegments: ['users', 'cover'],
      fileFieldName: 'coverImageFile',
      fileNamePrefix: 'cover',
    });
  }

  private async saveProfileImageFile(
    imageFile: UploadedImageFile,
    options: {
      folderSegments: string[];
      fileFieldName: 'avatarFile' | 'coverImageFile';
      fileNamePrefix: string;
    },
  ): Promise<string> {
    const extension = assertAllowedMediaFile(imageFile, 'image', {
      invalidMessage: `${options.fileFieldName} must be png, jpg, jpeg, or webp`,
      maxSizeBytes: MEDIA_MAX_SIZE_BYTES.userImage,
      maxSizeMessage: `${options.fileFieldName} size must be 5MB or less`,
    });

    return this.storageService.saveFile({
      folderSegments: options.folderSegments,
      extension,
      buffer: imageFile.buffer,
      contentType: imageFile.mimetype,
      fileNamePrefix: options.fileNamePrefix,
    });
  }

  private async attachUploadedProfileImages(
    payload: Record<string, unknown>,
    avatarFile?: UploadedImageFile,
    coverImageFile?: UploadedImageFile,
  ): Promise<string[]> {
    const uploadedImageUrls: string[] = [];

    if (avatarFile) {
      const uploadedAvatarUrl = await this.saveAvatarFile(avatarFile);
      payload.avatar = uploadedAvatarUrl;
      uploadedImageUrls.push(uploadedAvatarUrl);
    }

    if (coverImageFile) {
      const uploadedCoverImageUrl = await this.saveCoverImageFile(coverImageFile);
      payload.coverImage = uploadedCoverImageUrl;
      uploadedImageUrls.push(uploadedCoverImageUrl);
    }

    return uploadedImageUrls;
  }

  private async updateUserAndCleanupReplacedImages(
    userId: string,
    payload: Record<string, unknown>,
    options?: {
      currentUser?: UserDocument;
      uploadedImageUrls?: string[];
    },
  ): Promise<UserDocument> {
    const currentUser = options?.currentUser ?? (await this.findByIdOrFail(userId));
    const user = await this.usersRepository.updateById(userId, payload);

    if (!user) {
      await Promise.all((options?.uploadedImageUrls ?? []).map((fileUrl) => this.deleteManagedUpload(fileUrl)));
      throw new NotFoundException('User not found');
    }

    await this.cleanupReplacedUserImages(currentUser, payload);
    if (
      payload.isPrivate === true &&
      currentUser.isPrivate !== true
    ) {
      await Promise.all([
        this.feedVersionService.bumpGlobalVersion(),
        this.feedVersionService.bumpUserVersion(userId),
      ]);
    }
    return user;
  }

  private async cleanupReplacedUserImages(
    currentUser: UserDocument,
    payload: Record<string, unknown>,
  ): Promise<void> {
    const imageFields: Array<'avatar' | 'coverImage'> = ['avatar', 'coverImage'];

    await Promise.all(
      imageFields.map(async (field) => {
        const nextValue = payload[field];
        if (typeof nextValue !== 'string') {
          return;
        }

        const currentValue = (currentUser.get(field) as string | undefined) ?? '';
        if (nextValue === currentValue) {
          return;
        }

        await this.deleteManagedUpload(currentValue);
      }),
    );
  }

  async updateMusicSetup(userId: string, dto: MusicSetupDto): Promise<UserDocument> {
    const payload: Record<string, unknown> = { ...dto };

    const user = await this.usersRepository.updateById(userId, payload);
    if (!user) {
      throw new NotFoundException('User not found');
    }
    return user;
  }

  async disableUserBySuperAdmin(
    superAdminIdentifier: string,
    targetUserId: string,
    dto: AdminDisableUserDto,
  ): Promise<UserDocument> {
    await this.assertTargetIsManagedUser(targetUserId);

    const updated = await this.usersRepository.updateById(targetUserId, {
      isDisabled: true,
      disabledAt: new Date(),
      disabledReason: dto.reason ?? '',
      disabledBy: superAdminIdentifier,
    });
    if (!updated) {
      throw new NotFoundException('User not found');
    }
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'user_disable',
      'user',
      targetUserId,
      { reason: dto.reason ?? '' },
    );
    return updated;
  }

  async enableUserBySuperAdmin(superAdminIdentifier: string, targetUserId: string): Promise<UserDocument> {
    await this.assertTargetIsManagedUser(targetUserId);
    const updated = await this.usersRepository.updateById(targetUserId, {
      isDisabled: false,
      disabledAt: null,
      disabledReason: '',
      disabledBy: null,
    });
    if (!updated) {
      throw new NotFoundException('User not found');
    }
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'user_enable',
      'user',
      targetUserId,
    );
    return updated;
  }

  async deleteUserBySuperAdmin(superAdminIdentifier: string, targetUserId: string): Promise<void> {
    const user = await this.assertTargetIsManagedUser(targetUserId);
    await this.deleteUserRelatedData(targetUserId, {
      avatarUrl: user.avatar ?? '',
      coverImageUrl: user.coverImage ?? '',
    });
    await this.usersRepository.deleteById(targetUserId);
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'user_delete',
      'user',
      targetUserId,
    );
  }

  async searchUsers(query: UserQueryDto): Promise<{
    items: UserDocument[];
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  }> {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter = this.buildPublicUserFilter(query);

    const direction = resolveMongoSortDirection(query.sortOrder);
    const sortField = query.sortBy ?? 'createdAt';
    const sort = { [sortField]: direction } as Record<string, 1 | -1>;

    const [items, total] = await Promise.all([
      this.usersRepository.findMany(filter, skip, limit, sort),
      this.usersRepository.count(filter),
    ]);

    return buildPaginatedResponse(items, {
      page,
      limit,
      total,
      offset: skip,
    });
  }

  async discoverTalents(query: TalentDiscoverQueryDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 8;
    const skip = (page - 1) * limit;

    const baseQuery: UserQueryDto = {
      ...query,
      hasAvatar: query.hasAvatarOnly ?? true,
    };
    const filter = this.buildPublicUserFilter(baseQuery, {
      forcePublicTalentsOnly: true,
    });
    const direction = resolveMongoSortDirection(query.sortOrder);
    const sortField = query.sortBy ?? 'followersCount';
    const sort = { [sortField]: direction } as Record<string, 1 | -1>;

    const [items, total, roleBuckets] = await Promise.all([
      this.usersRepository.findMany(filter, skip, limit, sort),
      this.usersRepository.count(filter),
      query.includeRoleBuckets === false
        ? Promise.resolve([])
        : this.buildTalentRoleBuckets(baseQuery),
    ]);

    const result = buildPaginatedResponse(items, {
      page,
      limit,
      total,
      offset: skip,
    });

    return {
      ...result,
      roleBuckets,
      activeRole: query.musicRole ?? null,
    };
  }

  async discoverTalentsForSuperAdmin(query: TalentDiscoverQueryDto) {
    return this.discoverTalents(query);
  }

  async getProfileOverview(userId: string, viewerUserId: string) {
    const user = await this.findPublicByIdOrFail(userId);
    const objectUserId = new Types.ObjectId(userId);
    const postsCollection = this.connection.collection('posts');
    const followsCollection = this.connection.collection('follows');

    const [reelsCount, audioCount, imageCount, textCount, collaborationsCount, followingState] =
      await Promise.all([
        postsCollection.countDocuments({
          authorId: objectUserId,
          postType: 'video',
          isDeleted: false,
          moderationStatus: { $ne: ModerationStatus.HIDDEN },
        }),
        postsCollection.countDocuments({
          authorId: objectUserId,
          postType: 'audio',
          isDeleted: false,
          moderationStatus: { $ne: ModerationStatus.HIDDEN },
        }),
        postsCollection.countDocuments({
          authorId: objectUserId,
          postType: 'image',
          isDeleted: false,
          moderationStatus: { $ne: ModerationStatus.HIDDEN },
        }),
        postsCollection.countDocuments({
          authorId: objectUserId,
          postType: 'text',
          isDeleted: false,
          moderationStatus: { $ne: ModerationStatus.HIDDEN },
        }),
        this.countUserCollaborations(objectUserId),
        viewerUserId === userId
          ? Promise.resolve(false)
          : followsCollection.countDocuments({
              followerId: new Types.ObjectId(viewerUserId),
              followingId: objectUserId,
            }).then((count) => count > 0),
      ]);

    return {
      user,
      profileShareUrl: this.buildProfileShareUrl(user),
      stats: {
        followersCount: user.followersCount ?? 0,
        followingCount: user.followingCount ?? 0,
        postsCount: user.postsCount ?? 0,
        collaborationsCount,
      },
      contentCounts: {
        reels: reelsCount,
        audio: audioCount,
        image: imageCount,
        text: textCount,
        other: imageCount + textCount,
      },
      tabs: [
        { key: 'reels', postType: 'video', count: reelsCount },
        { key: 'audio', postType: 'audio', count: audioCount },
        { key: 'other', count: imageCount + textCount },
      ],
      viewerState: {
        isOwnProfile: viewerUserId === userId,
        following: followingState,
        isFollowing: followingState,
        canMessage: viewerUserId !== userId,
      },
    };
  }

  async getProfileOverviewForSuperAdmin(userId: string) {
    const overview = await this.getProfileOverview(userId, userId);
    return {
      ...overview,
      viewerState: {
        isOwnProfile: false,
        following: false,
        isFollowing: false,
        canMessage: false,
      },
    };
  }

  async getProfileOverviewByUsername(username: string, viewerUserId: string) {
    const normalizedUsername = username.trim().toLowerCase();
    const user = normalizedUsername ? await this.findByUsername(normalizedUsername) : null;
    if (!user || user.isDisabled) {
      throw new NotFoundException('User not found');
    }

    return this.getProfileOverview(this.extractUserId(user), viewerUserId);
  }

  async getFollowCounts(userId: string, viewerUserId: string) {
    if (!Types.ObjectId.isValid(userId) || !Types.ObjectId.isValid(viewerUserId)) {
      throw new BadRequestException('Invalid user id');
    }

    const user = await this.findPublicByIdOrFail(userId);
    const objectUserId = new Types.ObjectId(userId);
    const objectViewerUserId = new Types.ObjectId(viewerUserId);
    const isOwnProfile = userId === viewerUserId;
    const followsCollection = this.connection.collection('follows');
    const followRequestsCollection = this.connection.collection('followrequests');
    const blocksCollection = this.connection.collection('blocks');

    const [
      followersCount,
      followingCount,
      followRequestsCount,
      isFollowingCount,
      pendingRequestCount,
      viewerBlockedTargetCount,
      targetBlockedViewerCount,
    ] = await Promise.all([
      followsCollection.countDocuments({ followingId: objectUserId }),
      followsCollection.countDocuments({ followerId: objectUserId }),
      isOwnProfile
        ? followRequestsCollection.countDocuments({
            targetUserId: objectUserId,
            status: 'pending',
          })
        : Promise.resolve(0),
      isOwnProfile
        ? Promise.resolve(0)
        : followsCollection.countDocuments({
            followerId: objectViewerUserId,
            followingId: objectUserId,
          }),
      isOwnProfile
        ? Promise.resolve(0)
        : followRequestsCollection.countDocuments({
            requesterId: objectViewerUserId,
            targetUserId: objectUserId,
            status: 'pending',
          }),
      isOwnProfile
        ? Promise.resolve(0)
        : blocksCollection.countDocuments({
            blockerId: objectViewerUserId,
            blockedId: objectUserId,
          }),
      isOwnProfile
        ? Promise.resolve(0)
        : blocksCollection.countDocuments({
            blockerId: objectUserId,
            blockedId: objectViewerUserId,
          }),
    ]);

    return {
      userId,
      followersCount,
      followingCount,
      followRequestsCount,
      isPrivate: user.isPrivate ?? false,
      viewerState: {
        isOwnProfile,
        isFollowing: isFollowingCount > 0,
        hasPendingFollowRequest: pendingRequestCount > 0,
        isBlocked: viewerBlockedTargetCount > 0,
        isBlockedByUser: targetBlockedViewerCount > 0,
      },
    };
  }

  async getMyDashboard(currentUserId: string): Promise<ArtistDashboardResponse> {
    if (!Types.ObjectId.isValid(currentUserId)) {
      throw new BadRequestException('Invalid user id');
    }

    const user = await this.findByIdOrFail(currentUserId);
    if (user.isDisabled) {
      throw new NotFoundException('User not found');
    }

    const objectUserId = new Types.ObjectId(currentUserId);
    const now = new Date();
    const weekAgo = new Date(now);
    weekAgo.setDate(weekAgo.getDate() - 7);
    const monthAgo = new Date(now);
    monthAgo.setDate(monthAgo.getDate() - 30);

    const [postStats, collaborationsCount, topContent, recentActivity, newFollowersThisWeek, newFollowersThisMonth] =
      await Promise.all([
        this.aggregateUserPostStats(objectUserId),
        this.countUserCollaborations(objectUserId),
        this.findTopDashboardContent(objectUserId, 5),
        this.findArtistRecentActivity(objectUserId, 10),
        this.connection.collection('follows').countDocuments({
          followingId: objectUserId,
          createdAt: { $gte: weekAgo },
        }),
        this.connection.collection('follows').countDocuments({
          followingId: objectUserId,
          createdAt: { $gte: monthAgo },
        }),
      ]);

    const followersCount = this.toSafeNumber(user.followersCount);
    const followingCount = this.toSafeNumber(user.followingCount);
    const totalEngagement =
      postStats.likesCount + postStats.commentsCount + postStats.savesCount + postStats.sharesCount;
    const totalReach = postStats.viewCount + postStats.playCount;
    const engagementRate = this.roundPercent((totalEngagement / Math.max(totalReach, 1)) * 100);
    const userObject = typeof user.toObject === 'function' ? (user.toObject() as Record<string, any>) : (user as any);

    return {
      profile: {
        _id: currentUserId,
        coverImage: userObject.coverImage ?? '',
        avatar: userObject.avatar ?? '',
        name: userObject.name ?? '',
        stageName: userObject.stageName ?? '',
        bio: userObject.bio ?? '',
        isVerified: userObject.isVerified ?? false,
      },
      stats: {
        followersCount,
        followingCount,
        postsCount: postStats.postsCount,
        collaborationsCount,
        viewCount: postStats.viewCount,
        playCount: postStats.playCount,
        listenCount: postStats.playCount,
        likesCount: postStats.likesCount,
        commentsCount: postStats.commentsCount,
        savesCount: postStats.savesCount,
        sharesCount: postStats.sharesCount,
        engagementRate,
        // TODO: Replace with real artist score logic when product scoring rules exist.
        scorePercentage: 0,
        // TODO: Replace with real monetization calculation when earnings module exists.
        earnings: 0,
      },
      audience: {
        followers: followersCount,
        // TODO: Requires viewer-level analytics/events to calculate non-followers.
        nonFollowers: 0,
        newFollowersThisWeek,
        newFollowersThisMonth,
      },
      engagementChart: [],
      topContent,
      recentActivity,
      meta: {
        generatedAt: now.toISOString(),
        chartAvailable: false,
        earningsAvailable: false,
        audienceAnalyticsAvailable: false,
      },
    };
  }

  async searchUsersForSuperAdmin(query: UserQueryDto): Promise<{
    items: UserDocument[];
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  }> {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter = this.buildAdminUserSearchFilter(query);
    const direction = resolveMongoSortDirection(query.sortOrder);
    const sortField = query.sortBy ?? 'createdAt';
    const sort = { [sortField]: direction } as Record<string, 1 | -1>;

    const [items, total] = await Promise.all([
      this.usersRepository.findMany(filter, skip, limit, sort),
      this.usersRepository.count(filter),
    ]);

    return buildPaginatedResponse(items, {
      page,
      limit,
      total,
      offset: skip,
    });
  }

  async getShareFriends(currentUserId: string, query: ShareFriendsQueryDto) {
    if (query.postId) {
      await this.assertPostShareableForViewer(currentUserId, query.postId);
    }

    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;
    const currentUserObjectId = new Types.ObjectId(currentUserId);
    const blockedIds = await this.findBlockingOrBlockedIds(currentUserId);

    const [result] = await this.connection
      .collection('follows')
      .aggregate<{ items: Record<string, any>[]; total: Array<{ count: number }> }>([
        { $match: { followerId: currentUserObjectId } },
        {
          $lookup: {
            from: 'users',
            localField: 'followingId',
            foreignField: '_id',
            as: 'user',
          },
        },
        { $unwind: '$user' },
        {
          $match: {
            'user._id': { $ne: currentUserObjectId, ...(blockedIds.length ? { $nin: blockedIds } : {}) },
            'user.isDisabled': { $ne: true },
          },
        },
        { $sort: { createdAt: -1 } },
        {
          $facet: {
            items: [
              { $skip: skip },
              { $limit: limit },
              {
                $project: {
                  _id: '$user._id',
                  name: '$user.name',
                  username: '$user.username',
                  stageName: '$user.stageName',
                  avatar: '$user.avatar',
                  isVerified: '$user.isVerified',
                },
              },
            ],
            total: [{ $count: 'count' }],
          },
        },
      ])
      .toArray();

    const total = result?.total?.[0]?.count ?? 0;
    const items = (result?.items ?? []).map((user) => this.formatShareFriend(user));

    return buildPaginatedResponse(items, {
      page,
      limit,
      total,
      offset: skip,
    });
  }

  private async aggregateUserPostStats(authorId: Types.ObjectId): Promise<UserPostStats> {
    const [row] = await this.connection
      .collection('posts')
      .aggregate<UserPostStats>([
        {
          $match: {
            authorId,
            isDeleted: { $ne: true },
            isArchived: { $ne: true },
            moderationStatus: { $ne: ModerationStatus.HIDDEN },
          },
        },
        {
          $group: {
            _id: null,
            postsCount: { $sum: 1 },
            viewCount: { $sum: { $ifNull: ['$viewCount', 0] } },
            playCount: { $sum: { $ifNull: ['$playCount', 0] } },
            likesCount: { $sum: { $ifNull: ['$likesCount', 0] } },
            commentsCount: { $sum: { $ifNull: ['$commentsCount', 0] } },
            savesCount: { $sum: { $ifNull: ['$savesCount', 0] } },
            sharesCount: { $sum: { $ifNull: ['$shareCount', 0] } },
          },
        },
        { $project: { _id: 0 } },
      ])
      .toArray();

    return {
      postsCount: this.toSafeNumber(row?.postsCount),
      viewCount: this.toSafeNumber(row?.viewCount),
      playCount: this.toSafeNumber(row?.playCount),
      likesCount: this.toSafeNumber(row?.likesCount),
      commentsCount: this.toSafeNumber(row?.commentsCount),
      savesCount: this.toSafeNumber(row?.savesCount),
      sharesCount: this.toSafeNumber(row?.sharesCount),
    };
  }

  private async countUserCollaborations(userId: Types.ObjectId): Promise<number> {
    return this.connection.collection('posts').countDocuments({
      isDeleted: { $ne: true },
      isArchived: { $ne: true },
      moderationStatus: { $ne: ModerationStatus.HIDDEN },
      collaboratorIds: userId,
    });
  }

  private buildProfileShareUrl(user: UserDocument | Record<string, any>): string {
    return buildCanonicalProfileShareUrl(this.resolveShareBaseUrl(), user);
  }

  private async assertPostShareableForViewer(viewerUserId: string, postId: string): Promise<void> {
    if (!Types.ObjectId.isValid(postId)) {
      throw new BadRequestException('Invalid post id');
    }

    const post = await this.connection.collection('posts').findOne({
      _id: new Types.ObjectId(postId),
      isDeleted: { $ne: true },
      isArchived: { $ne: true },
      moderationStatus: { $ne: ModerationStatus.HIDDEN },
    });
    if (!post) {
      throw new NotFoundException('Post not found');
    }

    const authorId = post.authorId?.toString?.() ?? '';
    if (!authorId) {
      throw new NotFoundException('Post not found');
    }

    const author = await this.usersRepository.findById(authorId);
    if (!author || author.isDisabled) {
      throw new NotFoundException('Post not found');
    }

    if (authorId === viewerUserId) {
      return;
    }

    const blocked = await this.connection.collection('blocks').countDocuments({
      $or: [
        { blockerId: new Types.ObjectId(viewerUserId), blockedId: new Types.ObjectId(authorId) },
        { blockerId: new Types.ObjectId(authorId), blockedId: new Types.ObjectId(viewerUserId) },
      ],
    });
    if (blocked > 0) {
      throw new ForbiddenException('You cannot access this post');
    }

    if (author.isPrivate) {
      const followsPrivateAuthor = await this.connection.collection('follows').countDocuments({
        followerId: new Types.ObjectId(viewerUserId),
        followingId: new Types.ObjectId(authorId),
      });
      if (followsPrivateAuthor === 0) {
        throw new ForbiddenException('You cannot access this post');
      }
    }

    if (post.visibility === PostVisibility.PRIVATE) {
      throw new ForbiddenException('You cannot access this post');
    }

    if (post.visibility === PostVisibility.FOLLOWERS) {
      const follows = await this.connection.collection('follows').countDocuments({
        followerId: new Types.ObjectId(viewerUserId),
        followingId: new Types.ObjectId(authorId),
      });
      if (follows === 0) {
        throw new ForbiddenException('You cannot access this post');
      }
    }
  }

  private async findBlockingOrBlockedIds(userId: string): Promise<Types.ObjectId[]> {
    const userObjectId = new Types.ObjectId(userId);
    const rows = await this.connection
      .collection('blocks')
      .find({ $or: [{ blockerId: userObjectId }, { blockedId: userObjectId }] })
      .project({ blockerId: 1, blockedId: 1 })
      .toArray();

    return rows.map((row) =>
      row.blockerId?.toString?.() === userId ? row.blockedId : row.blockerId,
    ).filter((id): id is Types.ObjectId => id instanceof Types.ObjectId);
  }

  private formatShareFriend(user: Record<string, any>) {
    const stageName = typeof user.stageName === 'string' ? user.stageName.trim() : '';
    const name = typeof user.name === 'string' ? user.name.trim() : '';
    const username = typeof user.username === 'string' ? user.username.trim() : '';
    return {
      id: user._id?.toString?.() ?? '',
      displayName: stageName || name || username,
      username,
      avatarUrl: this.resolveManagedStringUrl(user.avatar),
      isVerified: !!user.isVerified,
      isFollowing: true,
    };
  }

  private resolveShareBaseUrl(): string {
    return resolveShareBaseUrl(this.configService);
  }

  private extractUserId(user: UserDocument | Record<string, any>): string {
    const candidate = user.id ?? user._id;
    return candidate?.toString?.() ?? String(candidate);
  }

  private async findTopDashboardContent(
    authorId: Types.ObjectId,
    limit: number,
  ): Promise<ArtistDashboardTopContentItem[]> {
    const rows = await this.connection
      .collection('posts')
      .aggregate<Record<string, any>>([
        {
          $match: {
            authorId,
            isDeleted: { $ne: true },
            isArchived: { $ne: true },
            moderationStatus: { $ne: ModerationStatus.HIDDEN },
          },
        },
        {
          $addFields: {
            engagementScore: {
              $add: [
                { $ifNull: ['$likesCount', 0] },
                { $ifNull: ['$commentsCount', 0] },
                { $ifNull: ['$savesCount', 0] },
                { $ifNull: ['$shareCount', 0] },
              ],
            },
          },
        },
        { $sort: { engagementScore: -1, viewCount: -1, playCount: -1, createdAt: -1 } },
        { $limit: Math.max(1, limit) },
        {
          $project: {
            postType: 1,
            thumbnailUrl: 1,
            imageUrls: 1,
            imageItems: 1,
            videoUrl: 1,
            audioUrl: 1,
            content: 1,
            viewCount: 1,
            playCount: 1,
            likesCount: 1,
            commentsCount: 1,
            savesCount: 1,
            engagementScore: 1,
            createdAt: 1,
          },
        },
      ])
      .toArray();

    return rows.map((post) => {
      const thumbnailUrl = this.resolveManagedStringUrl(post.thumbnailUrl);
      const fallbackDisplayUrl = this.resolveTopContentDisplayUrl(post);
      return {
        _id: post._id?.toString?.() ?? '',
        postType: post.postType ?? '',
        thumbnailUrl,
        displayUrl: thumbnailUrl || fallbackDisplayUrl,
        content: post.content ?? '',
        viewCount: this.toSafeNumber(post.viewCount),
        playCount: this.toSafeNumber(post.playCount),
        likesCount: this.toSafeNumber(post.likesCount),
        commentsCount: this.toSafeNumber(post.commentsCount),
        savesCount: this.toSafeNumber(post.savesCount),
        engagementScore: this.toSafeNumber(post.engagementScore),
        createdAt: post.createdAt ?? '',
      };
    });
  }

  private async findArtistRecentActivity(
    userId: Types.ObjectId,
    limit: number,
  ): Promise<ArtistDashboardRecentActivityItem[]> {
    const rows = await this.connection
      .collection('notifications')
      .find({
        $or: [{ recipientId: userId }, { actorId: userId }],
      })
      .sort({ createdAt: -1 })
      .limit(Math.max(1, limit))
      .project({
        type: 1,
        title: 1,
        previewText: 1,
        resourceType: 1,
        referenceId: 1,
        deepLink: 1,
        createdAt: 1,
      })
      .toArray();

    return rows.map((activity) => ({
      _id: activity._id?.toString?.() ?? '',
      type: activity.type ?? '',
      title: activity.title ?? '',
      previewText: activity.previewText ?? '',
      resourceType: activity.resourceType ?? '',
      referenceId: activity.referenceId?.toString?.() ?? null,
      deepLink: activity.deepLink ?? '',
      createdAt: activity.createdAt ?? '',
    }));
  }

  private resolveTopContentDisplayUrl(post: Record<string, any>): string {
    const imageItems = Array.isArray(post.imageItems) ? post.imageItems : [];
    const imageUrls = Array.isArray(post.imageUrls) ? post.imageUrls : [];
    const candidate =
      imageItems.find((item) => typeof item?.url === 'string' && item.url)?.url ??
      imageUrls.find((url) => typeof url === 'string' && url) ??
      post.videoUrl ??
      post.audioUrl ??
      '';

    return this.resolveManagedStringUrl(candidate);
  }

  private resolveManagedStringUrl(value: unknown): string {
    const resolved = resolveManagedFileUrl(typeof value === 'string' ? value : '');
    return typeof resolved === 'string' ? resolved : '';
  }

  private toSafeNumber(value: unknown): number {
    const numeric = Number(value ?? 0);
    return Number.isFinite(numeric) && numeric > 0 ? numeric : 0;
  }

  private roundPercent(value: number): number {
    if (!Number.isFinite(value)) {
      return 0;
    }

    return Math.round(value * 100) / 100;
  }

  private async assertTargetIsAdmin(userId: string): Promise<UserDocument> {
    const user = await this.findByIdOrFail(userId);
    if (user.role !== UserRole.ADMIN) {
      throw new BadRequestException('Target user is not an admin');
    }
    return user;
  }

  private async assertTargetIsManagedUser(userId: string): Promise<UserDocument> {
    const user = await this.findByIdOrFail(userId);
    if (user.role === UserRole.SUPERADMIN) {
      throw new ForbiddenException('Superadmin accounts cannot be managed through this endpoint');
    }
    return user;
  }

  private async deleteUserRelatedData(
    userId: string,
    options?: {
      avatarUrl?: string;
      coverImageUrl?: string;
    },
  ): Promise<void> {
    if (!Types.ObjectId.isValid(userId)) {
      return;
    }

    const objectUserId = new Types.ObjectId(userId);

    const postsCollection = this.connection.collection('posts');
    const commentsCollection = this.connection.collection('comments');
    const likesCollection = this.connection.collection('likes');
    const savesCollection = this.connection.collection('saves');
    const followsCollection = this.connection.collection('follows');
    const notificationsCollection = this.connection.collection('notifications');
    const conversationsCollection = this.connection.collection('conversations');
    const messagesCollection = this.connection.collection('messages');
    const chatBlocksCollection = this.connection.collection('chatblocks');
    const marketplaceCollection = this.connection.collection('instruments');
    const refreshTokensCollection = this.connection.collection('refreshtokens');
    const passwordResetCodesCollection = this.connection.collection('passwordresetcodes');
    const emailVerificationCodesCollection = this.connection.collection('emailverificationcodes');

    const [adminPosts, ownComments, adminConversations, ownMessages, ownInstruments] = await Promise.all([
      postsCollection.find({ authorId: objectUserId }).project({ _id: 1, videoUrl: 1, audioUrl: 1 }).toArray(),
      commentsCollection.find({ authorId: objectUserId }).project({ _id: 1 }).toArray(),
      conversationsCollection.find({ participantIds: objectUserId }).project({ _id: 1 }).toArray(),
      messagesCollection.find({ senderId: objectUserId }).project({ _id: 1, mediaUrl: 1 }).toArray(),
      marketplaceCollection.find({ ownerAdminId: objectUserId }).project({ _id: 1, imageUrls: 1 }).toArray(),
    ]);

    const postIds = adminPosts.map((post) => post._id as Types.ObjectId);
    const ownCommentIds = ownComments.map((comment) => comment._id as Types.ObjectId);
    const conversationIds = adminConversations.map((conversation) => conversation._id as Types.ObjectId);

    const postComments = postIds.length
      ? await commentsCollection.find({ postId: { $in: postIds } }).project({ _id: 1 }).toArray()
      : [];
    const postCommentIds = postComments.map((comment) => comment._id as Types.ObjectId);
    const commentIds = [...ownCommentIds, ...postCommentIds];

    const conversationMessages = conversationIds.length
      ? await messagesCollection
          .find({ conversationId: { $in: conversationIds } })
          .project({ _id: 1, mediaUrl: 1 })
          .toArray()
      : [];

    const localFilesToDelete = new Set<string>();
    if (options?.avatarUrl) {
      localFilesToDelete.add(options.avatarUrl);
    }
    if (options?.coverImageUrl) {
      localFilesToDelete.add(options.coverImageUrl);
    }

    for (const post of adminPosts) {
      if (typeof post.videoUrl === 'string') {
        localFilesToDelete.add(post.videoUrl);
      }
      if (typeof post.audioUrl === 'string') {
        localFilesToDelete.add(post.audioUrl);
      }
    }

    for (const message of [...ownMessages, ...conversationMessages]) {
      if (typeof message.mediaUrl === 'string') {
        localFilesToDelete.add(message.mediaUrl);
      }
    }

    for (const instrument of ownInstruments) {
      if (Array.isArray(instrument.imageUrls)) {
        for (const imageUrl of instrument.imageUrls) {
          if (typeof imageUrl === 'string') {
            localFilesToDelete.add(imageUrl);
          }
        }
      }
    }

    const likesDeleteOr: Record<string, unknown>[] = [{ userId: objectUserId }];
    if (postIds.length) {
      likesDeleteOr.push({ targetType: 'post', targetId: { $in: postIds } });
    }
    if (commentIds.length) {
      likesDeleteOr.push({ targetType: 'comment', targetId: { $in: commentIds } });
    }

    const savesDeleteOr: Record<string, unknown>[] = [{ userId: objectUserId }];
    if (postIds.length) {
      savesDeleteOr.push({ postId: { $in: postIds } });
    }

    await Promise.all([
      likesCollection.deleteMany({ $or: likesDeleteOr }),
      savesCollection.deleteMany({ $or: savesDeleteOr }),
      followsCollection.deleteMany({ $or: [{ followerId: objectUserId }, { followingId: objectUserId }] }),
      notificationsCollection.deleteMany({ $or: [{ recipientId: objectUserId }, { actorId: objectUserId }] }),
      chatBlocksCollection.deleteMany({ $or: [{ blockerId: objectUserId }, { blockedId: objectUserId }] }),
      refreshTokensCollection.deleteMany({ userId: objectUserId }),
      passwordResetCodesCollection.deleteMany({ userId: objectUserId }),
      emailVerificationCodesCollection.deleteMany({ userId: objectUserId }),
      conversationIds.length ? messagesCollection.deleteMany({ conversationId: { $in: conversationIds } }) : null,
      ownMessages.length ? messagesCollection.deleteMany({ senderId: objectUserId }) : null,
      conversationIds.length ? conversationsCollection.deleteMany({ _id: { $in: conversationIds } }) : null,
      commentIds.length ? commentsCollection.deleteMany({ _id: { $in: commentIds } }) : null,
      postIds.length ? postsCollection.deleteMany({ _id: { $in: postIds } }) : null,
      marketplaceCollection.deleteMany({ ownerAdminId: objectUserId }),
    ]);

    await Promise.all([...localFilesToDelete].map((fileUrl) => this.deleteManagedUpload(fileUrl)));
  }

  private async deleteManagedUpload(fileUrl: string): Promise<void> {
    await this.storageService.deleteFile(fileUrl);
  }

  private buildPublicUserFilter(
    query: Pick<UserQueryDto, 'q' | 'isVerified' | 'musicRole' | 'experienceLevel' | 'isPrivate' | 'hasAvatar'>,
    options?: { forcePublicTalentsOnly?: boolean },
  ): Record<string, unknown> {
    const clauses: Record<string, unknown>[] = [{ isDisabled: false }];

    if (query.q?.trim()) {
      clauses.push({
        $or: [
          { name: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { username: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { stageName: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { bio: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
        ],
      });
    }

    if (typeof query.isVerified === 'boolean') {
      clauses.push({ isVerified: query.isVerified });
    }

    if (query.musicRole) {
      clauses.push({ musicRoles: query.musicRole });
    }

    if (query.experienceLevel) {
      clauses.push({ experienceLevel: query.experienceLevel });
    }

    if (typeof query.isPrivate === 'boolean') {
      clauses.push({ isPrivate: query.isPrivate });
    }

    if (typeof query.hasAvatar === 'boolean') {
      clauses.push(
        query.hasAvatar
          ? { avatar: { $ne: '' } }
          : {
              $or: [{ avatar: '' }, { avatar: { $exists: false } }],
            },
      );
    }

    if (options?.forcePublicTalentsOnly) {
      clauses.push({ role: UserRole.USER });
      clauses.push({ isPrivate: false });
      clauses.push({ musicRoles: { $exists: true, $ne: [] } });
    }

    if (clauses.length === 1) {
      return clauses[0];
    }

    return { $and: clauses };
  }

  private async buildTalentRoleBuckets(
    query: Pick<UserQueryDto, 'q' | 'isVerified' | 'experienceLevel' | 'isPrivate' | 'hasAvatar'>,
  ) {
    const roles = Object.values(MusicRole);
    return Promise.all(
      roles.map(async (role) => ({
        role,
        count: await this.usersRepository.count(
          this.buildPublicUserFilter(
            {
              ...query,
              musicRole: role,
            },
            { forcePublicTalentsOnly: true },
          ),
        ),
      })),
    );
  }

  private buildAdminUserSearchFilter(query: UserQueryDto): Record<string, unknown> {
    const clauses: Record<string, unknown>[] = [{ role: { $ne: UserRole.SUPERADMIN } }];

    if (query.q?.trim()) {
      clauses.push({
        $or: [
          { name: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { username: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { email: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { stageName: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
          { bio: { $regex: escapeRegex(query.q.trim()), $options: 'i' } },
        ],
      });
    }

    if (typeof query.isVerified === 'boolean') {
      clauses.push({ isVerified: query.isVerified });
    }

    if (query.musicRole) {
      clauses.push({ musicRoles: query.musicRole });
    }

    if (query.experienceLevel) {
      clauses.push({ experienceLevel: query.experienceLevel });
    }

    if (typeof query.isPrivate === 'boolean') {
      clauses.push({ isPrivate: query.isPrivate });
    }

    if (typeof query.hasAvatar === 'boolean') {
      clauses.push(
        query.hasAvatar
          ? { avatar: { $ne: '' } }
          : {
              $or: [{ avatar: '' }, { avatar: { $exists: false } }],
            },
      );
    }

    if (clauses.length === 1) {
      return clauses[0];
    }

    return { $and: clauses };
  }
}
