import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { FilterQuery, Model, Types } from 'mongoose';
import { assertAllowedMediaFile, MEDIA_MAX_SIZE_BYTES } from '../../common/media/allowed-media';
import { buildPaginatedResponse } from '../../common/utils/pagination.util';
import { resolveManagedFileUrl, resolveManagedFileUrls } from '../../common/utils/public-url.util';
import { resolveMongoSortDirection } from '../../common/utils/sort.util';
import { escapeRegex } from '../../common/utils/regex.util';
import { UserRole } from '../../common/enums/user-role.enum';
import { ManagedStorageService } from '../../infrastructure/storage/managed-storage.service';
import { AuditService } from '../audit/audit.service';
import {
  SuperAdminCase,
  SuperAdminCaseDocument,
  SuperAdminCasePriority,
  SuperAdminCaseStatus,
} from '../superadmin/schemas/superadmin-case.schema';
import { UsersRepository } from '../users/users.repository';
import { CreateInstrumentDto } from './dto/create-instrument.dto';
import { InstrumentQueryDto } from './dto/instrument-query.dto';
import { CreateRepairShopDto } from './dto/create-repair-shop.dto';
import { MarketplaceHomeQueryDto } from './dto/marketplace-home-query.dto';
import { RepairShopQueryDto } from './dto/repair-shop-query.dto';
import { UpdateShopProfileDto } from './dto/update-shop-profile.dto';
import { UpdateInstrumentDto } from './dto/update-instrument.dto';
import { UpdateMarketplaceStatusDto } from './dto/update-marketplace-status.dto';
import { UpdateRepairShopDto } from './dto/update-repair-shop.dto';
import { MarketplaceListingCategory } from './enums/marketplace-listing-category.enum';
import { MarketplaceListingCondition } from './enums/marketplace-listing-condition.enum';
import { MarketplaceRepository } from './marketplace.repository';
import { InstrumentDocument } from './schemas/instrument.schema';
import { RepairShopDocument } from './schemas/repair-shop.schema';

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

@Injectable()
export class MarketplaceService {
  constructor(
    private readonly marketplaceRepository: MarketplaceRepository,
    private readonly usersRepository: UsersRepository,
    private readonly storageService: ManagedStorageService,
    private readonly auditService: AuditService,
    @InjectModel(SuperAdminCase.name)
    private readonly superAdminCaseModel: Model<SuperAdminCaseDocument>,
  ) {}

  async getHome(query: MarketplaceHomeQueryDto) {
    const listingsLimit = query.listingsLimit ?? 6;
    const instrumentsLimit = query.instrumentsLimit ?? 6;
    const repairShopsLimit = query.repairShopsLimit ?? 4;
    const onlyActive = query.onlyActive ?? true;

    const listingQuery = {
      page: 1,
      limit: listingsLimit,
      isActive: onlyActive,
    } as InstrumentQueryDto;
    const instrumentQuery = {
      page: 1,
      limit: instrumentsLimit,
      isActive: onlyActive,
    } as InstrumentQueryDto;
    const repairShopQuery = {
      page: 1,
      limit: repairShopsLimit,
      isActive: onlyActive,
    } as RepairShopQueryDto;

    const activeListingsFilter = onlyActive ? ({ isActive: true } as FilterQuery<InstrumentDocument>) : {};
    const activeRepairShopsFilter = onlyActive
      ? ({ isActive: true } as FilterQuery<RepairShopDocument>)
      : {};
    const activeMusicalInstrumentsFilter = this.buildInstrumentFilter(
      { isActive: onlyActive ? true : undefined } as InstrumentQueryDto,
      { onlyMusicalInstruments: true },
    );

    const [listings, instruments, repairShops, featuredShops, listingCategories, totalListings, totalInstruments, totalRepairShops] =
      await Promise.all([
        this.getPublicListings(listingQuery),
        this.getPublicInstruments(instrumentQuery),
        this.getPublicRepairShops(repairShopQuery),
        this.getFeaturedShops(8),
        this.getListingCategorySummaries(onlyActive),
        this.marketplaceRepository.countPublic(activeListingsFilter),
        this.marketplaceRepository.countPublic(activeMusicalInstrumentsFilter),
        this.marketplaceRepository.countRepairShopsPublic(activeRepairShopsFilter),
      ]);

    return {
      categories: [
        {
          key: 'listings',
          title: 'Marketplace',
          endpoint: '/marketplace/listings',
        },
        {
          key: 'musical_instruments',
          title: 'Musical instruments',
          endpoint: '/marketplace/instruments',
        },
        {
          key: 'repair_shops',
          title: 'Repair shops',
          endpoint: '/marketplace/repair-shops',
        },
      ],
      summary: {
        activeListings: totalListings,
        activeMusicalInstruments: totalInstruments,
        activeRepairShops: totalRepairShops,
      },
      filters: {
        listingCategories,
      },
      featuredShops,
      sections: {
        listings: {
          title: 'Marketplace',
          endpoint: '/marketplace/listings',
          ...listings,
        },
        musicalInstruments: {
          title: 'Musical instruments',
          endpoint: '/marketplace/instruments',
          ...instruments,
        },
        repairShops: {
          title: 'Repair shops',
          endpoint: '/marketplace/repair-shops',
          ...repairShops,
        },
      },
    };
  }

  async createListingByAdmin(
    adminUserId: string,
    dto: CreateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    const admin = await this.assertAdminRole(adminUserId);
    this.assertShopConfigured(admin);
    this.assertImageUrlsCount(dto.imageUrls, imageFiles.length);
    const uploadedImageUrls = await this.saveMarketplaceImageFiles(imageFiles, {
      folderSegments: ['marketplace', 'images'],
      fieldName: 'imageFiles',
      fileNamePrefix: 'listing',
    });
    return this.marketplaceRepository.create(adminUserId, {
      ...dto,
      currency: (dto.currency ?? 'SAR').toUpperCase(),
      description: dto.description ?? '',
      imageUrls: [...(dto.imageUrls ?? []), ...uploadedImageUrls],
      isActive: dto.isActive ?? true,
      condition: dto.condition ?? MarketplaceListingCondition.USED,
      instrumentType: dto.instrumentType?.trim() ?? '',
      listingCategory: dto.listingCategory ?? MarketplaceListingCategory.MUSICAL_INSTRUMENT,
    });
  }

  async createListingBySuperAdmin(
    ownerAdminId: string,
    dto: CreateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    await this.assertActiveAdminOwner(ownerAdminId);
    return this.createListingByAdmin(ownerAdminId, dto, imageFiles);
  }

  async createByAdmin(adminUserId: string, dto: CreateInstrumentDto): Promise<InstrumentDocument> {
    return this.createInstrumentByAdmin(adminUserId, dto);
  }

  async createInstrumentByAdmin(
    adminUserId: string,
    dto: CreateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    return this.createListingByAdmin(adminUserId, {
      ...dto,
      listingCategory: MarketplaceListingCategory.MUSICAL_INSTRUMENT,
    }, imageFiles);
  }

  async createInstrumentBySuperAdmin(
    ownerAdminId: string,
    dto: CreateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    await this.assertActiveAdminOwner(ownerAdminId);
    return this.createInstrumentByAdmin(ownerAdminId, dto, imageFiles);
  }

  async updateListingByAdmin(
    adminUserId: string,
    instrumentId: string,
    dto: UpdateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    await this.assertAdminRole(adminUserId);
    this.assertImageUrlsCount(dto.imageUrls, imageFiles.length);
    const existing = await this.marketplaceRepository.findById(instrumentId);
    if (!existing) {
      throw new NotFoundException('Listing not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can update only your listings');
    }

    const uploadedImageUrls = await this.saveMarketplaceImageFiles(imageFiles, {
      folderSegments: ['marketplace', 'images'],
      fieldName: 'imageFiles',
      fileNamePrefix: 'listing',
    });

    const imageUrls =
      typeof dto.imageUrls === 'undefined' && uploadedImageUrls.length === 0
        ? undefined
        : [...(dto.imageUrls ?? []), ...uploadedImageUrls];

    const updated = await this.marketplaceRepository.updateById(instrumentId, {
      ...dto,
      ...(typeof imageUrls !== 'undefined' ? { imageUrls } : {}),
      ...(dto.currency ? { currency: dto.currency.toUpperCase() } : {}),
      ...(typeof dto.instrumentType === 'string' ? { instrumentType: dto.instrumentType.trim() } : {}),
    });
    if (!updated) {
      await Promise.all(uploadedImageUrls.map((fileUrl) => this.deleteManagedUpload(fileUrl)));
      throw new NotFoundException('Listing not found');
    }
    if (typeof imageUrls !== 'undefined') {
      await this.cleanupRemovedManagedUrls(existing.imageUrls, imageUrls);
    }
    return updated;
  }

  async updateByAdmin(
    adminUserId: string,
    instrumentId: string,
    dto: UpdateInstrumentDto,
  ): Promise<InstrumentDocument> {
    return this.updateInstrumentByAdmin(adminUserId, instrumentId, dto);
  }

  async updateInstrumentByAdmin(
    adminUserId: string,
    instrumentId: string,
    dto: UpdateInstrumentDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<InstrumentDocument> {
    const existing = await this.marketplaceRepository.findById(instrumentId);
    if (!existing || !this.isMusicalInstrumentListing(existing.listingCategory)) {
      throw new NotFoundException('Instrument not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can update only your instruments');
    }

    return this.updateListingByAdmin(adminUserId, instrumentId, {
      ...dto,
      listingCategory: MarketplaceListingCategory.MUSICAL_INSTRUMENT,
    }, imageFiles);
  }

  async removeListingByAdmin(adminUserId: string, instrumentId: string): Promise<void> {
    await this.assertAdminRole(adminUserId);
    const existing = await this.marketplaceRepository.findById(instrumentId);
    if (!existing) {
      throw new NotFoundException('Listing not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can delete only your listings');
    }
    await this.marketplaceRepository.deleteById(instrumentId);
    await this.cleanupRemovedManagedUrls(existing.imageUrls);
  }

  async removeByAdmin(adminUserId: string, instrumentId: string): Promise<void> {
    return this.removeInstrumentByAdmin(adminUserId, instrumentId);
  }

  async removeInstrumentByAdmin(adminUserId: string, instrumentId: string): Promise<void> {
    const existing = await this.marketplaceRepository.findById(instrumentId);
    if (!existing || !this.isMusicalInstrumentListing(existing.listingCategory)) {
      throw new NotFoundException('Instrument not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can delete only your instruments');
    }
    await this.marketplaceRepository.deleteById(instrumentId);
    await this.cleanupRemovedManagedUrls(existing.imageUrls);
  }

  async getMyListings(
    adminUserId: string,
    query: InstrumentQueryDto,
    options?: { onlyMusicalInstruments?: boolean },
  ) {
    await this.assertAdminRole(adminUserId);
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const matchingShopOwnerIds = await this.findMatchingShopOwnerIds(query.q);
    const filter = this.buildInstrumentFilter(query, {
      ...options,
      matchingShopOwnerIds,
    });
    filter.ownerAdminId = new Types.ObjectId(adminUserId);
    const sort = this.buildInstrumentSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countPublic(filter),
    ]);

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

  async getMine(adminUserId: string, query: InstrumentQueryDto) {
    return this.getMyInstruments(adminUserId, query);
  }

  async getMyInstruments(adminUserId: string, query: InstrumentQueryDto) {
    return this.getMyListings(adminUserId, query, { onlyMusicalInstruments: true });
  }

  async getPublicListings(
    query: InstrumentQueryDto,
    options?: { onlyMusicalInstruments?: boolean },
  ) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const matchingShopOwnerIds = await this.findMatchingShopOwnerIds(query.q);
    const filter = this.buildInstrumentFilter(query, {
      ...options,
      matchingShopOwnerIds,
    });
    if (typeof query.isActive === 'undefined') {
      filter.isActive = true;
    }
    const sort = this.buildInstrumentSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countPublic(filter),
    ]);

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

  async getPublic(query: InstrumentQueryDto) {
    return this.getPublicInstruments(query);
  }

  async getListingsForSuperAdmin(query: InstrumentQueryDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const matchingShopOwnerIds = await this.findMatchingShopOwnerIds(query.q);
    const filter = this.buildInstrumentFilter(query, {
      matchingShopOwnerIds,
    });
    const sort = this.buildInstrumentSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countPublic(filter),
    ]);

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

  async getPublicInstruments(query: InstrumentQueryDto) {
    return this.getPublicListings(query, { onlyMusicalInstruments: true });
  }

  async findById(instrumentId: string) {
    return this.findListingById(instrumentId);
  }

  async findListingById(instrumentId: string) {
    const item = await this.marketplaceRepository.findById(instrumentId);
    if (!item) {
      throw new NotFoundException('Listing not found');
    }
    const owner = item.ownerAdminId as unknown as { isDisabled?: boolean } | undefined;
    if (owner?.isDisabled) {
      throw new NotFoundException('Listing not found');
    }
    return this.decorateInstrumentItem(item);
  }

  async findInstrumentById(instrumentId: string) {
    const item = await this.findListingById(instrumentId);
    if (!this.isMusicalInstrumentListing(item.listingCategory)) {
      throw new NotFoundException('Instrument not found');
    }
    return item;
  }

  async createRepairShop(
    adminUserId: string,
    dto: CreateRepairShopDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<RepairShopDocument> {
    await this.assertAdminRole(adminUserId);
    await this.assertAdminHasNoRepairShop(adminUserId);
    this.assertShopImagesCount(dto.imageUrls, imageFiles.length);
    this.assertShopCoordinates(dto.latitude, dto.longitude);
    const uploadedImageUrls = await this.saveMarketplaceImageFiles(imageFiles, {
      folderSegments: ['marketplace', 'images'],
      fieldName: 'imageFiles',
      fileNamePrefix: 'repair-shop',
    });
    return this.marketplaceRepository.createRepairShop(adminUserId, {
      ...dto,
      description: dto.description ?? '',
      services: dto.services ?? [],
      phone: dto.phone ?? '',
      whatsapp: dto.whatsapp ?? '',
      imageUrls: [...(dto.imageUrls ?? []), ...uploadedImageUrls],
      location: dto.location ?? '',
      isActive: dto.isActive ?? true,
    });
  }

  async createRepairShopBySuperAdmin(
    ownerAdminId: string,
    dto: CreateRepairShopDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<RepairShopDocument> {
    await this.assertActiveAdminOwner(ownerAdminId);
    return this.createRepairShop(ownerAdminId, dto, imageFiles);
  }

  async updateRepairShop(
    adminUserId: string,
    repairShopId: string,
    dto: UpdateRepairShopDto,
    imageFiles: UploadedImageFile[] = [],
  ): Promise<RepairShopDocument> {
    await this.assertAdminRole(adminUserId);
    this.assertShopImagesCount(dto.imageUrls, imageFiles.length);
    this.assertShopCoordinates(dto.latitude, dto.longitude);
    const existing = await this.marketplaceRepository.findRepairShopById(repairShopId);
    if (!existing) {
      throw new NotFoundException('Repair shop not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can update only your repair shops');
    }

    const uploadedImageUrls = await this.saveMarketplaceImageFiles(imageFiles, {
      folderSegments: ['marketplace', 'images'],
      fieldName: 'imageFiles',
      fileNamePrefix: 'repair-shop',
    });

    const imageUrls =
      typeof dto.imageUrls === 'undefined' && uploadedImageUrls.length === 0
        ? undefined
        : [...(dto.imageUrls ?? []), ...uploadedImageUrls];

    const updated = await this.marketplaceRepository.updateRepairShopById(repairShopId, {
      ...dto,
      ...(typeof imageUrls !== 'undefined' ? { imageUrls } : {}),
    });
    if (!updated) {
      await Promise.all(uploadedImageUrls.map((fileUrl) => this.deleteManagedUpload(fileUrl)));
      throw new NotFoundException('Repair shop not found');
    }
    if (typeof imageUrls !== 'undefined') {
      await this.cleanupRemovedManagedUrls(existing.imageUrls, imageUrls);
    }
    return updated;
  }

  async removeRepairShop(adminUserId: string, repairShopId: string): Promise<void> {
    await this.assertAdminRole(adminUserId);
    const existing = await this.marketplaceRepository.findRepairShopById(repairShopId);
    if (!existing) {
      throw new NotFoundException('Repair shop not found');
    }
    if (existing.ownerAdminId.toString() !== adminUserId) {
      throw new ForbiddenException('You can delete only your repair shops');
    }
    await this.marketplaceRepository.deleteRepairShopById(repairShopId);
    await this.cleanupRemovedManagedUrls(existing.imageUrls);
  }

  async getMyRepairShops(adminUserId: string, query: RepairShopQueryDto) {
    await this.assertAdminRole(adminUserId);
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter = this.buildRepairShopFilter(query);
    filter.ownerAdminId = new Types.ObjectId(adminUserId);
    const sort = this.buildRepairShopSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyRepairShopsPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countRepairShopsPublic(filter),
    ]);

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

  async getPublicRepairShops(query: RepairShopQueryDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter = this.buildRepairShopFilter(query);
    if (typeof query.isActive === 'undefined') {
      filter.isActive = true;
    }
    const sort = this.buildRepairShopSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyRepairShopsPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countRepairShopsPublic(filter),
    ]);

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

  async getRepairShopsForSuperAdmin(query: RepairShopQueryDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 20;
    const skip = (page - 1) * limit;

    const filter = this.buildRepairShopFilter(query);
    const sort = this.buildRepairShopSort(query.sortBy, query.sortOrder);

    const [items, total] = await Promise.all([
      this.marketplaceRepository.findManyRepairShopsPublic(filter, skip, limit, sort),
      this.marketplaceRepository.countRepairShopsPublic(filter),
    ]);

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

  async findRepairShopById(repairShopId: string) {
    const item = await this.marketplaceRepository.findRepairShopById(repairShopId);
    if (!item) {
      throw new NotFoundException('Repair shop not found');
    }
    const owner = item.ownerAdminId as unknown as { isDisabled?: boolean } | undefined;
    if (owner?.isDisabled) {
      throw new NotFoundException('Repair shop not found');
    }
    return this.decorateRepairShopItem(item);
  }

  async updateMyShopProfile(
    adminUserId: string,
    dto: UpdateShopProfileDto,
    shopImageFiles: UploadedImageFile[] = [],
  ) {
    const admin = await this.assertAdminRole(adminUserId);
    this.assertShopImagesCount(dto.shopImageUrls, shopImageFiles.length);
    this.assertShopCoordinates(dto.shopLatitude, dto.shopLongitude);

    const uploadedImageUrls = await this.saveMarketplaceImageFiles(shopImageFiles, {
      folderSegments: ['marketplace', 'images'],
      fieldName: 'shopImageFiles',
      fileNamePrefix: 'shop',
    });

    const shopImageUrls =
      typeof dto.shopImageUrls === 'undefined' && uploadedImageUrls.length === 0
        ? undefined
        : [...(dto.shopImageUrls ?? []), ...uploadedImageUrls];

    const payload: Record<string, unknown> = {
      ...dto,
      shopName: typeof dto.shopName === 'string' ? dto.shopName.trim() : admin.shopName,
      shopDescription:
        typeof dto.shopDescription === 'string' ? dto.shopDescription.trim() : admin.shopDescription,
      shopLocation: typeof dto.shopLocation === 'string' ? dto.shopLocation.trim() : admin.shopLocation,
      ...(typeof shopImageUrls !== 'undefined' ? { shopImageUrls } : {}),
    };

    const updated = await this.usersRepository.updateById(adminUserId, payload);
    if (!updated) {
      await Promise.all(uploadedImageUrls.map((fileUrl) => this.deleteManagedUpload(fileUrl)));
      throw new NotFoundException('Admin not found');
    }

    if (typeof shopImageUrls !== 'undefined') {
      await this.cleanupRemovedManagedUrls(admin.shopImageUrls, shopImageUrls);
    }

    return this.mapShopProfile(updated);
  }

  async updateShopProfileBySuperAdmin(
    ownerAdminId: string,
    dto: UpdateShopProfileDto,
    shopImageFiles: UploadedImageFile[] = [],
  ) {
    await this.assertActiveAdminOwner(ownerAdminId);
    return this.updateMyShopProfile(ownerAdminId, dto, shopImageFiles);
  }

  async getMyShopProfile(adminUserId: string) {
    const admin = await this.assertAdminRole(adminUserId);
    return this.mapShopProfile(admin);
  }

  async getShopProfileByAdminId(adminId: string) {
    const admin = await this.assertAdminRole(adminId);
    if (admin.isDisabled) {
      throw new NotFoundException('Shop not found');
    }
    return this.mapShopProfile(admin);
  }

  async updateListingStatusBySuperAdmin(
    superAdminIdentifier: string,
    listingId: string,
    dto: UpdateMarketplaceStatusDto,
  ) {
    const existing = await this.marketplaceRepository.findById(listingId);
    if (!existing) {
      throw new NotFoundException('Listing not found');
    }

    const updated = await this.marketplaceRepository.updateById(listingId, {
      isActive: dto.isActive,
    });
    if (!updated) {
      throw new NotFoundException('Listing not found');
    }

    const reason = dto.reason?.trim() ?? '';
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      dto.isActive ? 'marketplace_listing_activate' : 'marketplace_listing_deactivate',
      'listing',
      listingId,
      {
        isActive: dto.isActive,
        reason,
      },
    );
    await this.recordMarketplaceCase({
      actor: superAdminIdentifier,
      resourceType: 'listing',
      resourceId: listingId,
      title: this.buildMarketplaceCaseTitle('Listing', existing.title ?? listingId),
      description: reason,
      action: dto.isActive ? 'listing_activated' : 'listing_deactivated',
      note: reason,
      status: dto.isActive ? 'resolved' : 'in_review',
      priority: dto.isActive ? 'normal' : 'high',
    });

    return this.decorateInstrumentItem(updated);
  }

  async removeListingBySuperAdmin(superAdminIdentifier: string, listingId: string) {
    const existing = await this.marketplaceRepository.findById(listingId);
    if (!existing) {
      throw new NotFoundException('Listing not found');
    }
    await this.marketplaceRepository.deleteById(listingId);
    await this.cleanupRemovedManagedUrls(existing.imageUrls);
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'marketplace_listing_delete',
      'listing',
      listingId,
      { title: existing.title ?? '' },
    );
    await this.recordMarketplaceCase({
      actor: superAdminIdentifier,
      resourceType: 'listing',
      resourceId: listingId,
      title: this.buildMarketplaceCaseTitle('Listing', existing.title ?? listingId),
      description: 'Deleted by superadmin',
      action: 'listing_deleted',
      note: 'Deleted by superadmin',
      status: 'resolved',
      priority: 'high',
    });
  }

  async updateRepairShopStatusBySuperAdmin(
    superAdminIdentifier: string,
    repairShopId: string,
    dto: UpdateMarketplaceStatusDto,
  ) {
    const existing = await this.marketplaceRepository.findRepairShopById(repairShopId);
    if (!existing) {
      throw new NotFoundException('Repair shop not found');
    }

    const updated = await this.marketplaceRepository.updateRepairShopById(repairShopId, {
      isActive: dto.isActive,
    });
    if (!updated) {
      throw new NotFoundException('Repair shop not found');
    }

    const reason = dto.reason?.trim() ?? '';
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      dto.isActive ? 'marketplace_repair_shop_activate' : 'marketplace_repair_shop_deactivate',
      'repair_shop',
      repairShopId,
      {
        isActive: dto.isActive,
        reason,
      },
    );
    await this.recordMarketplaceCase({
      actor: superAdminIdentifier,
      resourceType: 'repair_shop',
      resourceId: repairShopId,
      title: this.buildMarketplaceCaseTitle('Repair shop', existing.name ?? repairShopId),
      description: reason,
      action: dto.isActive ? 'repair_shop_activated' : 'repair_shop_deactivated',
      note: reason,
      status: dto.isActive ? 'resolved' : 'in_review',
      priority: dto.isActive ? 'normal' : 'high',
    });

    return this.decorateRepairShopItem(updated);
  }

  async removeRepairShopBySuperAdmin(superAdminIdentifier: string, repairShopId: string) {
    const existing = await this.marketplaceRepository.findRepairShopById(repairShopId);
    if (!existing) {
      throw new NotFoundException('Repair shop not found');
    }
    await this.marketplaceRepository.deleteRepairShopById(repairShopId);
    await this.cleanupRemovedManagedUrls(existing.imageUrls);
    await this.auditService.logSuperAdminAction(
      superAdminIdentifier,
      'marketplace_repair_shop_delete',
      'repair_shop',
      repairShopId,
      { name: existing.name ?? '' },
    );
    await this.recordMarketplaceCase({
      actor: superAdminIdentifier,
      resourceType: 'repair_shop',
      resourceId: repairShopId,
      title: this.buildMarketplaceCaseTitle('Repair shop', existing.name ?? repairShopId),
      description: 'Deleted by superadmin',
      action: 'repair_shop_deleted',
      note: 'Deleted by superadmin',
      status: 'resolved',
      priority: 'high',
    });
  }

  private buildInstrumentFilter(
    query: InstrumentQueryDto,
    options?: {
      onlyMusicalInstruments?: boolean;
      matchingShopOwnerIds?: Types.ObjectId[];
    },
  ): FilterQuery<InstrumentDocument> {
    const clauses: FilterQuery<InstrumentDocument>[] = [];
    if (query.q) {
      const searchClauses: Record<string, unknown>[] = [
        { title: { $regex: escapeRegex(query.q), $options: 'i' } },
        { description: { $regex: escapeRegex(query.q), $options: 'i' } },
        { instrumentType: { $regex: escapeRegex(query.q), $options: 'i' } },
      ];
      if (options?.matchingShopOwnerIds?.length) {
        searchClauses.push({ ownerAdminId: { $in: options.matchingShopOwnerIds } });
      }
      clauses.push({
        $or: searchClauses,
      });
    }

    if (typeof query.minPrice === 'number' || typeof query.maxPrice === 'number') {
      const priceFilter: Record<string, number> = {};
      if (typeof query.minPrice === 'number') {
        priceFilter.$gte = query.minPrice;
      }
      if (typeof query.maxPrice === 'number') {
        priceFilter.$lte = query.maxPrice;
      }
      clauses.push({ price: priceFilter });
    }

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

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

    if (query.instrumentType?.trim()) {
      clauses.push({ instrumentType: { $regex: escapeRegex(query.instrumentType.trim()), $options: 'i' } });
    }

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

    if (options?.onlyMusicalInstruments) {
      clauses.push(this.buildMusicalInstrumentCategoryFilter());
    }

    if (!clauses.length) {
      return {};
    }

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

    return { $and: clauses };
  }

  private buildRepairShopFilter(query: RepairShopQueryDto): FilterQuery<RepairShopDocument> {
    const filter: FilterQuery<RepairShopDocument> = {};

    if (query.q) {
      filter.$or = [
        { name: { $regex: escapeRegex(query.q), $options: 'i' } },
        { description: { $regex: escapeRegex(query.q), $options: 'i' } },
        { location: { $regex: escapeRegex(query.q), $options: 'i' } },
        { services: { $elemMatch: { $regex: escapeRegex(query.q), $options: 'i' } } },
      ];
    }

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

    return filter;
  }

  private buildInstrumentSort(
    sortBy: InstrumentQueryDto['sortBy'],
    sortOrder: InstrumentQueryDto['sortOrder'],
  ): Record<string, 1 | -1> {
    const direction = resolveMongoSortDirection(sortOrder);
    const field = sortBy ?? 'createdAt';
    return { [field]: direction };
  }

  private buildRepairShopSort(
    sortBy: RepairShopQueryDto['sortBy'],
    sortOrder: RepairShopQueryDto['sortOrder'],
  ): Record<string, 1 | -1> {
    const direction = resolveMongoSortDirection(sortOrder);
    const field = sortBy ?? 'createdAt';
    return { [field]: direction };
  }

  private buildMusicalInstrumentCategoryFilter(): FilterQuery<InstrumentDocument> {
    return {
      $or: [
        { listingCategory: MarketplaceListingCategory.MUSICAL_INSTRUMENT },
        { listingCategory: { $exists: false } },
        { listingCategory: null },
      ],
    };
  }

  private isMusicalInstrumentListing(value: unknown): boolean {
    return value === MarketplaceListingCategory.MUSICAL_INSTRUMENT || value == null;
  }

  private buildMarketplaceCaseTitle(prefix: string, subject: string): string {
    const normalized = subject.trim();
    return `${prefix} moderation: ${normalized.slice(0, 72) || 'untitled'}`;
  }

  private async recordMarketplaceCase(params: {
    actor: string;
    resourceType: 'listing' | 'repair_shop';
    resourceId: string;
    title: string;
    description: string;
    action: string;
    note: string;
    status: SuperAdminCaseStatus;
    priority: SuperAdminCasePriority;
  }): Promise<void> {
    const existing = await this.superAdminCaseModel
      .findOne({
        resourceType: params.resourceType,
        resourceId: params.resourceId,
        status: { $in: ['open', 'in_review'] },
      })
      .sort({ updatedAt: -1 })
      .exec();

    if (!existing) {
      await this.superAdminCaseModel.create({
        title: params.title,
        description: params.description,
        caseType: 'marketplace_review',
        resourceType: params.resourceType,
        resourceId: params.resourceId,
        status: params.status,
        priority: params.priority,
        createdBy: params.actor,
        updatedBy: params.actor,
        resolution: params.note,
        events: [
          {
            action: params.action,
            actor: params.actor,
            note: params.note,
            metadata: {},
            createdAt: new Date(),
          },
        ],
      });
      return;
    }

    existing.status = params.status;
    existing.priority = params.priority;
    existing.updatedBy = params.actor;
    existing.title = params.title;
    existing.description = params.description;
    existing.resolution = params.note;
    existing.events.push({
      action: params.action,
      actor: params.actor,
      note: params.note,
      metadata: {},
      createdAt: new Date(),
    });
    await existing.save();
  }

  private async findMatchingShopOwnerIds(query: string | undefined): Promise<Types.ObjectId[]> {
    const search = query?.trim();
    if (!search) {
      return [];
    }

    const rows = await this.usersRepository.findMany(
      {
        role: UserRole.ADMIN,
        isDisabled: false,
        $or: [
          { shopName: { $regex: search, $options: 'i' } },
          { name: { $regex: search, $options: 'i' } },
        ],
      },
      0,
      25,
      { isVerified: -1, followersCount: -1, createdAt: -1 },
    );

    return rows.map((row) => new Types.ObjectId(row.id));
  }

  private async getFeaturedShops(limit: number) {
    const admins = await this.usersRepository.findMany(
      {
        role: UserRole.ADMIN,
        isDisabled: false,
        shopName: { $ne: '' },
        'shopImageUrls.0': { $exists: true },
      },
      0,
      limit,
      { isVerified: -1, followersCount: -1, createdAt: -1 },
    );

    return admins.map((admin) => this.mapShopProfile(admin));
  }

  private async getListingCategorySummaries(onlyActive: boolean) {
    const categories = [
      { key: 'all', listingCategory: null as MarketplaceListingCategory | null },
      { key: MarketplaceListingCategory.MUSICAL_INSTRUMENT, listingCategory: MarketplaceListingCategory.MUSICAL_INSTRUMENT },
      { key: MarketplaceListingCategory.ACCESSORY, listingCategory: MarketplaceListingCategory.ACCESSORY },
      { key: MarketplaceListingCategory.AUDIO_GEAR, listingCategory: MarketplaceListingCategory.AUDIO_GEAR },
      { key: MarketplaceListingCategory.SHEET_MUSIC, listingCategory: MarketplaceListingCategory.SHEET_MUSIC },
      { key: MarketplaceListingCategory.OTHER, listingCategory: MarketplaceListingCategory.OTHER },
    ];

    return Promise.all(
      categories.map(async (category) => {
        const filter = category.listingCategory
          ? this.buildInstrumentFilter(
              {
                listingCategory: category.listingCategory,
                isActive: onlyActive ? true : undefined,
              } as InstrumentQueryDto,
            )
          : (onlyActive ? ({ isActive: true } as FilterQuery<InstrumentDocument>) : {});

        return {
          key: category.key,
          count: await this.marketplaceRepository.countPublic(filter),
        };
      }),
    );
  }

  private async assertAdminRole(userId: string) {
    const user = await this.usersRepository.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }
    if (user.role !== UserRole.ADMIN) {
      throw new ForbiddenException('Admin role required');
    }
    return user;
  }

  private async assertActiveAdminOwner(userId: string) {
    const user = await this.assertAdminRole(userId);
    if (user.isDisabled) {
      throw new ForbiddenException('Disabled admins cannot own marketplace resources');
    }
    return user;
  }

  private async assertAdminHasNoRepairShop(adminUserId: string): Promise<void> {
    const existingShop = await this.marketplaceRepository.findRepairShopByOwnerAdminId(adminUserId);
    if (existingShop) {
      throw new BadRequestException(
        'Each admin can create one marketplace shop only. Update your existing shop instead.',
      );
    }
  }

  private assertImageUrlsCount(imageUrls?: string[], additionalImagesCount = 0): void {
    if ((imageUrls?.length ?? 0) + additionalImagesCount > 5) {
      throw new BadRequestException('You can upload up to 5 images only');
    }
  }

  private assertShopImagesCount(imageUrls?: string[], additionalImagesCount = 0): void {
    if ((imageUrls?.length ?? 0) + additionalImagesCount > 8) {
      throw new BadRequestException('Shop can contain up to 8 images only');
    }
  }

  private assertShopCoordinates(
    latitude: number | undefined,
    longitude: number | undefined,
  ): void {
    const hasLat = typeof latitude === 'number';
    const hasLng = typeof longitude === 'number';
    if (hasLat !== hasLng) {
      throw new BadRequestException('shopLatitude and shopLongitude must be provided together');
    }
  }

  private assertShopConfigured(admin: {
    shopName?: string;
    shopImageUrls?: string[];
    shopDescription?: string;
    shopLocation?: string;
  }): void {
    const hasName = !!admin.shopName?.trim();
    const hasImages = (admin.shopImageUrls?.length ?? 0) > 0;
    const hasDescription = !!admin.shopDescription?.trim();
    const hasLocation = !!admin.shopLocation?.trim();

    if (!hasName || !hasImages || !hasDescription || !hasLocation) {
      throw new BadRequestException(
        'Complete shop profile first (shopName, shopImageUrls, shopDescription, shopLocation)',
      );
    }
  }

  private async saveMarketplaceImageFiles(
    imageFiles: UploadedImageFile[],
    options: {
      folderSegments: string[];
      fieldName: 'imageFiles' | 'shopImageFiles';
      fileNamePrefix: string;
    },
  ): Promise<string[]> {
    const uploads: string[] = [];

    for (const imageFile of imageFiles) {
      uploads.push(await this.saveMarketplaceImageFile(imageFile, options));
    }

    return uploads;
  }

  private async saveMarketplaceImageFile(
    imageFile: UploadedImageFile,
    options: {
      folderSegments: string[];
      fieldName: 'imageFiles' | 'shopImageFiles';
      fileNamePrefix: string;
    },
  ): Promise<string> {
    const extension = assertAllowedMediaFile(imageFile, 'image', {
      invalidMessage: `${options.fieldName} must be png, jpg, jpeg, or webp`,
      maxSizeBytes: MEDIA_MAX_SIZE_BYTES.marketplaceImage,
      maxSizeMessage: `${options.fieldName} size must be 8MB or less`,
    });

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

  private async cleanupRemovedManagedUrls(
    currentUrls: unknown,
    nextUrls?: string[],
  ): Promise<void> {
    const current = Array.isArray(currentUrls)
      ? currentUrls.filter((value): value is string => typeof value === 'string' && !!value.trim())
      : [];
    const next = new Set((nextUrls ?? []).filter((value) => typeof value === 'string' && !!value.trim()));

    const removedUrls = current.filter((fileUrl) => !next.has(fileUrl));
    await Promise.all(removedUrls.map((fileUrl) => this.deleteManagedUpload(fileUrl)));
  }

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

  private mapShopProfile(admin: {
    id?: string;
    _id?: unknown;
    name?: string;
    username?: string;
    email?: string;
    shopName?: string;
    shopDescription?: string;
    shopImageUrls?: string[];
    shopLocation?: string;
    shopLatitude?: number | null;
    shopLongitude?: number | null;
    isDisabled?: boolean;
    createdAt?: Date;
    updatedAt?: Date;
  }) {
    return {
      adminId: (admin as any)._id?.toString?.() ?? admin.id,
      adminName: admin.name ?? '',
      adminUsername: admin.username ?? '',
      adminEmail: admin.email ?? '',
      shopName: admin.shopName ?? '',
      shopDescription: admin.shopDescription ?? '',
      shopImageUrls: resolveManagedFileUrls(admin.shopImageUrls ?? []) as string[],
      shopLocation: admin.shopLocation ?? '',
      shopLatitude: admin.shopLatitude ?? null,
      shopLongitude: admin.shopLongitude ?? null,
      isDisabled: admin.isDisabled ?? false,
      createdAt: admin.createdAt,
      updatedAt: admin.updatedAt,
    };
  }

  private decorateInstrumentRows(items: Array<Record<string, unknown> | InstrumentDocument>): Array<Record<string, unknown>> {
    return items.map((item) => this.decorateInstrumentItem(item));
  }

  private decorateRepairShopRows(
    items: Array<Record<string, unknown> | RepairShopDocument>,
  ): Array<Record<string, unknown>> {
    return items.map((item) => this.decorateRepairShopItem(item));
  }

  private decorateInstrumentItem(item: Record<string, unknown> | InstrumentDocument): Record<string, unknown> {
    const row =
      typeof (item as InstrumentDocument).toObject === 'function'
        ? ((item as InstrumentDocument).toObject() as unknown as Record<string, unknown>)
        : ({ ...item } as Record<string, unknown>);

    row.imageUrls = resolveManagedFileUrls(row.imageUrls);
    if (row.ownerAdminId && typeof row.ownerAdminId === 'object') {
      const owner = { ...(row.ownerAdminId as Record<string, unknown>) };
      owner.avatar = resolveManagedFileUrl(owner.avatar);
      row.ownerAdminId = owner;
      row.shop = {
        adminId: owner._id ?? owner.id ?? '',
        name: owner.shopName ?? owner.name ?? '',
        username: owner.username ?? '',
        avatar: owner.avatar ?? '',
      };
      row.storeName = owner.shopName ?? owner.name ?? '';
    }

    return row;
  }

  private decorateRepairShopItem(item: Record<string, unknown> | RepairShopDocument): Record<string, unknown> {
    const row =
      typeof (item as RepairShopDocument).toObject === 'function'
        ? ((item as RepairShopDocument).toObject() as unknown as Record<string, unknown>)
        : ({ ...item } as Record<string, unknown>);

    row.imageUrls = resolveManagedFileUrls(row.imageUrls);
    if (row.ownerAdminId && typeof row.ownerAdminId === 'object') {
      const owner = { ...(row.ownerAdminId as Record<string, unknown>) };
      owner.avatar = resolveManagedFileUrl(owner.avatar);
      row.ownerAdminId = owner;
      row.shop = {
        adminId: owner._id ?? owner.id ?? '',
        name: owner.shopName ?? owner.name ?? '',
        username: owner.username ?? '',
        avatar: owner.avatar ?? '',
      };
      row.storeName = owner.shopName ?? owner.name ?? '';
    }

    return row;
  }
}
