import {
  Body,
  Controller,
  Get,
  Param,
  Post,
  Query,
  UseGuards,
} from '@nestjs/common';
import { type QueryBus } from '@nestjs/cqrs';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiParam } from '@nestjs/swagger';
import { JwtAuthGuard } from '@modules/auth';
import { EndpointRateLimit, EndpointRateLimitGuard } from '@modules/shared';
import { RequireQuota, QuotaGuard } from '@modules/subscriptions';
import { type BatchValuationDto as BatchValuationQueryDto } from '../../application/queries/batch-valuation/batch-valuation.handler';
import { BatchValuationQuery } from '../../application/queries/batch-valuation/batch-valuation.query';
import { type DistrictStatsDto } from '../../application/queries/get-district-stats/get-district-stats.handler';
import { GetDistrictStatsQuery } from '../../application/queries/get-district-stats/get-district-stats.query';
import { type HeatmapDto } from '../../application/queries/get-heatmap/get-heatmap.handler';
import { GetHeatmapQuery } from '../../application/queries/get-heatmap/get-heatmap.query';
import { type MarketReportDto } from '../../application/queries/get-market-report/get-market-report.handler';
import { GetMarketReportQuery } from '../../application/queries/get-market-report/get-market-report.query';
import { GetNeighborhoodScoreQuery } from '../../application/queries/get-neighborhood-score/get-neighborhood-score.query';
import { type PriceTrendDto } from '../../application/queries/get-price-trend/get-price-trend.handler';
import { GetPriceTrendQuery } from '../../application/queries/get-price-trend/get-price-trend.query';
import { type ValuationDto } from '../../application/queries/get-valuation/get-valuation.handler';
import { GetValuationQuery } from '../../application/queries/get-valuation/get-valuation.query';
import { type ValuationComparisonDto as ValuationComparisonResultDto } from '../../application/queries/valuation-comparison/valuation-comparison.handler';
import { ValuationComparisonQuery } from '../../application/queries/valuation-comparison/valuation-comparison.query';
import { type ValuationHistoryDto as ValuationHistoryResultDto } from '../../application/queries/valuation-history/valuation-history.handler';
import { ValuationHistoryQuery } from '../../application/queries/valuation-history/valuation-history.query';
import { type NeighborhoodScoreResult } from '../../domain/services/neighborhood-score.service';
import { type BatchValuationDto } from '../dto/batch-valuation.dto';
import { type GetDistrictStatsDto } from '../dto/get-district-stats.dto';
import { type GetHeatmapDto } from '../dto/get-heatmap.dto';
import { type GetMarketReportDto } from '../dto/get-market-report.dto';
import { type GetPriceTrendDto } from '../dto/get-price-trend.dto';
import { type GetValuationDto } from '../dto/get-valuation.dto';
import { type ValuationComparisonDto } from '../dto/valuation-comparison.dto';
import { type ValuationHistoryDto } from '../dto/valuation-history.dto';

@ApiTags('analytics')
@Controller('analytics')
export class AnalyticsController {
  constructor(
    private readonly queryBus: QueryBus,
  ) {}

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('market-report')
  @ApiOperation({ summary: 'Get market report for a city' })
  @ApiResponse({ status: 200, description: 'Market report retrieved' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getMarketReport(@Query() dto: GetMarketReportDto): Promise<MarketReportDto> {
    return this.queryBus.execute(
      new GetMarketReportQuery(dto.city, dto.period, dto.propertyType),
    );
  }

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('price-trend')
  @ApiOperation({ summary: 'Get price trend for a district' })
  @ApiResponse({ status: 200, description: 'Price trend data retrieved' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getPriceTrend(@Query() dto: GetPriceTrendDto): Promise<PriceTrendDto> {
    return this.queryBus.execute(
      new GetPriceTrendQuery(dto.district, dto.city, dto.propertyType, dto.periods),
    );
  }

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('heatmap')
  @ApiOperation({ summary: 'Get price heatmap for a city' })
  @ApiResponse({ status: 200, description: 'Heatmap data retrieved' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getHeatmap(@Query() dto: GetHeatmapDto): Promise<HeatmapDto> {
    return this.queryBus.execute(
      new GetHeatmapQuery(dto.city, dto.period),
    );
  }

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('district-stats')
  @ApiOperation({ summary: 'Get statistics by district' })
  @ApiResponse({ status: 200, description: 'District statistics retrieved' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getDistrictStats(@Query() dto: GetDistrictStatsDto): Promise<DistrictStatsDto> {
    return this.queryBus.execute(
      new GetDistrictStatsQuery(dto.city, dto.period),
    );
  }

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('valuation')
  @ApiOperation({ summary: 'Get automated property valuation (AVM)' })
  @ApiResponse({ status: 200, description: 'Valuation estimate retrieved' })
  @ApiResponse({ status: 400, description: 'Invalid parameters — provide propertyId or (lat, lng, areaM2)' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getValuation(@Query() dto: GetValuationDto): Promise<ValuationDto> {
    return this.queryBus.execute(
      new GetValuationQuery(dto.propertyId, dto.latitude, dto.longitude, dto.areaM2, dto.propertyType),
    );
  }

  @ApiBearerAuth('JWT')
  @EndpointRateLimit({ limit: 10, windowSeconds: 60, keyStrategy: 'user' })
  @UseGuards(EndpointRateLimitGuard, JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Post('valuation/batch')
  @ApiOperation({ summary: 'Batch valuation for multiple properties (max 50)' })
  @ApiResponse({ status: 200, description: 'Batch valuation results retrieved' })
  @ApiResponse({ status: 400, description: 'Invalid parameters' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  @ApiResponse({ status: 429, description: 'Rate limit exceeded' })
  async batchValuation(@Body() dto: BatchValuationDto): Promise<BatchValuationQueryDto> {
    return this.queryBus.execute(
      new BatchValuationQuery(dto.propertyIds),
    );
  }

  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Get('valuation/history/:propertyId')
  @ApiOperation({ summary: 'Get valuation history for a property (chart data)' })
  @ApiParam({ name: 'propertyId', description: 'Property ID' })
  @ApiResponse({ status: 200, description: 'Valuation history retrieved' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  async getValuationHistory(
    @Param('propertyId') propertyId: string,
    @Query() dto: ValuationHistoryDto,
  ): Promise<ValuationHistoryResultDto> {
    return this.queryBus.execute(
      new ValuationHistoryQuery(propertyId, dto.limit ?? 50),
    );
  }

  @ApiBearerAuth('JWT')
  @EndpointRateLimit({ limit: 10, windowSeconds: 60, keyStrategy: 'user' })
  @UseGuards(EndpointRateLimitGuard, JwtAuthGuard, QuotaGuard)
  @RequireQuota('analytics_queries')
  @Post('valuation/compare')
  @ApiOperation({ summary: 'Compare valuations for 2-5 properties side by side' })
  @ApiResponse({ status: 200, description: 'Valuation comparison retrieved' })
  @ApiResponse({ status: 400, description: 'Invalid parameters — provide 2-5 property IDs' })
  @ApiResponse({ status: 403, description: 'Quota exceeded' })
  @ApiResponse({ status: 429, description: 'Rate limit exceeded' })
  async compareValuations(@Body() dto: ValuationComparisonDto): Promise<ValuationComparisonResultDto> {
    return this.queryBus.execute(
      new ValuationComparisonQuery(dto.propertyIds),
    );
  }

  @ApiOperation({ summary: 'Get neighborhood score for a district' })
  @ApiParam({ name: 'district', description: 'District name', example: 'Quận 1' })
  @ApiResponse({ status: 200, description: 'Neighborhood score retrieved' })
  @Get('neighborhoods/:district/score')
  async getNeighborhoodScore(
    @Param('district') district: string,
    @Query('city') city: string = 'Hồ Chí Minh',
  ): Promise<NeighborhoodScoreResult> {
    return this.queryBus.execute(
      new GetNeighborhoodScoreQuery(district, city),
    );
  }
}
