import {
  Body,
  Controller,
  Get,
  Param,
  Patch,
  Post,
  Query,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { type CommandBus, type QueryBus } from '@nestjs/cqrs';
import { FileInterceptor } from '@nestjs/platform-express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiConsumes,
  ApiQuery,
  ApiParam,
} from '@nestjs/swagger';
import { type JwtPayload } from '@modules/auth/infrastructure/services/token.service';
import { CurrentUser } from '@modules/auth/presentation/decorators/current-user.decorator';
import { Roles } from '@modules/auth/presentation/decorators/roles.decorator';
import { JwtAuthGuard } from '@modules/auth/presentation/guards/jwt-auth.guard';
import { RolesGuard } from '@modules/auth/presentation/guards/roles.guard';
import { FileValidationPipe, type UploadedFile as ValidatedFile } from '@modules/shared/infrastructure/pipes/file-validation.pipe';
import { RequireQuota } from '@modules/subscriptions/presentation/decorators/require-quota.decorator';
import { QuotaGuard } from '@modules/subscriptions/presentation/guards/quota.guard';
import { CreateListingCommand } from '../../application/commands/create-listing/create-listing.command';
import { type CreateListingResult } from '../../application/commands/create-listing/create-listing.handler';
import { ModerateListingCommand } from '../../application/commands/moderate-listing/moderate-listing.command';
import { UpdateListingStatusCommand } from '../../application/commands/update-listing-status/update-listing-status.command';
import { UploadMediaCommand } from '../../application/commands/upload-media/upload-media.command';
import { GetListingQuery } from '../../application/queries/get-listing/get-listing.query';
import { GetPendingModerationQuery } from '../../application/queries/get-pending-moderation/get-pending-moderation.query';
import { SearchListingsQuery } from '../../application/queries/search-listings/search-listings.query';
import { type ListingDetailData, type ListingSearchItem } from '../../domain/repositories/listing-read.dto';
import { type PaginatedResult } from '../../domain/repositories/listing.repository';
import { type CreateListingDto } from '../dto/create-listing.dto';
import { type ModerateListingDto } from '../dto/moderate-listing.dto';
import { type SearchListingsDto } from '../dto/search-listings.dto';
import { type UpdateListingStatusDto } from '../dto/update-listing-status.dto';

@ApiTags('listings')
@Controller('listings')
export class ListingsController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
  ) {}

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Create a new property listing' })
  @ApiResponse({ status: 201, description: 'Listing created successfully' })
  @ApiResponse({ status: 400, description: 'Validation error' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('listings_created')
  @Post()
  async createListing(
    @Body() dto: CreateListingDto,
    @CurrentUser() user: JwtPayload,
  ): Promise<CreateListingResult> {
    return this.commandBus.execute(
      new CreateListingCommand(
        user.sub,
        dto.transactionType,
        dto.priceVND,
        dto.propertyType,
        dto.title,
        dto.description,
        dto.address,
        dto.ward,
        dto.district,
        dto.city,
        dto.latitude,
        dto.longitude,
        dto.areaM2,
        dto.usableAreaM2,
        dto.bedrooms,
        dto.bathrooms,
        dto.floors,
        dto.floor,
        dto.totalFloors,
        dto.direction,
        dto.yearBuilt,
        dto.legalStatus,
        dto.amenities,
        dto.nearbyPOIs,
        dto.metroDistanceM,
        dto.projectName,
        dto.agentId,
        dto.rentPriceMonthly,
        dto.commissionPct,
      ),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Get listings pending moderation (admin only)' })
  @ApiResponse({ status: 200, description: 'Paginated list of pending listings' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Forbidden — admin role required' })
  @ApiQuery({ name: 'page', required: false, type: Number, example: 1, description: 'Page number' })
  @ApiQuery({ name: 'limit', required: false, type: Number, example: 20, description: 'Items per page' })
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('ADMIN')
  @Get('pending')
  async getPendingModeration(
    @Query('page') page?: number,
    @Query('limit') limit?: number,
  ): Promise<PaginatedResult<ListingSearchItem>> {
    return this.queryBus.execute(
      new GetPendingModerationQuery(page ?? 1, limit ?? 20),
    );
  }

  @ApiOperation({ summary: 'Get listing details by ID' })
  @ApiParam({ name: 'id', description: 'Listing UUID', example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' })
  @ApiResponse({ status: 200, description: 'Listing details returned' })
  @ApiResponse({ status: 404, description: 'Listing not found' })
  @Get(':id')
  async getListing(@Param('id') id: string): Promise<ListingDetailData> {
    return this.queryBus.execute(new GetListingQuery(id));
  }

  @ApiOperation({ summary: 'Search and filter property listings' })
  @ApiResponse({ status: 200, description: 'Paginated search results' })
  @Get()
  async searchListings(@Query() dto: SearchListingsDto): Promise<PaginatedResult<ListingSearchItem>> {
    return this.queryBus.execute(
      new SearchListingsQuery(
        dto.status,
        dto.transactionType,
        dto.propertyType,
        dto.city,
        dto.district,
        dto.minPrice,
        dto.maxPrice,
        dto.minArea,
        dto.maxArea,
        dto.bedrooms,
        dto.page,
        dto.limit,
      ),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Update listing status' })
  @ApiParam({ name: 'id', description: 'Listing UUID' })
  @ApiResponse({ status: 200, description: 'Status updated successfully' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 404, description: 'Listing not found' })
  @UseGuards(JwtAuthGuard)
  @Patch(':id/status')
  async updateStatus(
    @Param('id') id: string,
    @Body() dto: UpdateListingStatusDto,
    @CurrentUser() user: JwtPayload,
  ): Promise<{ status: string }> {
    return this.commandBus.execute(
      new UpdateListingStatusCommand(id, dto.status, user.sub, dto.moderationNotes),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Upload media (photo/video) for a listing' })
  @ApiConsumes('multipart/form-data')
  @ApiParam({ name: 'id', description: 'Listing UUID' })
  @ApiResponse({ status: 201, description: 'Media uploaded successfully' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @UseGuards(JwtAuthGuard)
  @UseInterceptors(FileInterceptor('file'))
  @Post(':id/media')
  async uploadMedia(
    @Param('id') id: string,
    @UploadedFile(new FileValidationPipe({
      maxSizeBytes: 10 * 1024 * 1024, // 10 MB
      allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp', 'video/mp4'],
    }))
    file: ValidatedFile,
    @CurrentUser() user: JwtPayload,
    @Body('caption') caption?: string,
  ): Promise<{ mediaId: string; url: string }> {
    return this.commandBus.execute(
      new UploadMediaCommand(id, user.sub, file, caption),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Moderate a listing (admin only)' })
  @ApiParam({ name: 'id', description: 'Listing UUID' })
  @ApiResponse({ status: 200, description: 'Listing moderated successfully' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Forbidden — admin role required' })
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('ADMIN')
  @Patch(':id/moderate')
  async moderateListing(
    @Param('id') id: string,
    @Body() dto: ModerateListingDto,
    @CurrentUser() user: JwtPayload,
  ): Promise<{ status: string }> {
    return this.commandBus.execute(
      new ModerateListingCommand(id, user.sub, dto.action, dto.moderationScore, dto.notes),
    );
  }
}
