import { Controller, Get, Post, Query, UseGuards, } from '@nestjs/common'; import { CommandBus, QueryBus } from '@nestjs/cqrs'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, } from '@nestjs/swagger'; import { Roles, JwtAuthGuard, RolesGuard } from '@modules/auth'; import { EndpointRateLimit, EndpointRateLimitGuard } from '@modules/shared'; import { ReindexAllCommand } from '../../application/commands/reindex-all/reindex-all.command'; import { type ReindexResult } from '../../application/commands/reindex-all/reindex-all.handler'; import { GeoSearchQuery } from '../../application/queries/geo-search/geo-search.query'; import { SearchPropertiesQuery } from '../../application/queries/search-properties/search-properties.query'; import { type SearchResult } from '../../domain/repositories/search.repository'; import { GeoSearchDto } from '../dto/geo-search.dto'; import { SearchPropertiesDto } from '../dto/search-properties.dto'; @ApiTags('search') @Controller('search') export class SearchController { constructor( private readonly commandBus: CommandBus, private readonly queryBus: QueryBus, ) {} @EndpointRateLimit({ limit: 30, windowSeconds: 60, keyStrategy: 'user' }) @UseGuards(EndpointRateLimitGuard) @Get() @ApiOperation({ summary: 'Search properties', description: 'Public full-text and faceted property search' }) @ApiResponse({ status: 200, description: 'Search results returned successfully' }) async search(@Query() dto: SearchPropertiesDto): Promise { return this.queryBus.execute( new SearchPropertiesQuery( dto.q, dto.propertyType, dto.transactionType, dto.priceMin, dto.priceMax, dto.areaMin, dto.areaMax, dto.bedrooms, dto.district, dto.city, dto.sortBy, dto.page, dto.perPage, dto.featured, dto.ward, ), ); } @EndpointRateLimit({ limit: 30, windowSeconds: 60, keyStrategy: 'user' }) @UseGuards(EndpointRateLimitGuard) @Get('geo') @ApiOperation({ summary: 'Geo search properties', description: 'Public geographic radius property search' }) @ApiResponse({ status: 200, description: 'Geo search results returned successfully' }) async geoSearch(@Query() dto: GeoSearchDto): Promise { return this.queryBus.execute( new GeoSearchQuery( dto.lat, dto.lng, dto.radiusKm, dto.propertyType, dto.transactionType, dto.priceMin, dto.priceMax, dto.sortBy, dto.page, dto.perPage, ), ); } @UseGuards(JwtAuthGuard, RolesGuard) @Roles('ADMIN') @Post('reindex') @ApiBearerAuth('JWT') @ApiOperation({ summary: 'Reindex all properties', description: 'Admin-only endpoint to trigger a full reindex' }) @ApiResponse({ status: 201, description: 'Reindex completed successfully' }) @ApiResponse({ status: 401, description: 'Unauthorized — missing or invalid JWT' }) @ApiResponse({ status: 403, description: 'Forbidden — requires ADMIN role' }) async reindex(): Promise { return this.commandBus.execute(new ReindexAllCommand()); } }