import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Inject,
  Param,
  Post,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery } from '@nestjs/swagger';
import { JwtAuthGuard } from '@modules/auth';
import { RequireQuota, QuotaGuard } from '@modules/subscriptions';
import { DeleteReportCommand } from '../../application/commands/delete-report/delete-report.command';
import { GenerateReportCommand } from '../../application/commands/generate-report/generate-report.command';
import { type GenerateReportResult } from '../../application/commands/generate-report/generate-report.handler';
import { GetReportQuery } from '../../application/queries/get-report/get-report.query';
import { type ListReportsResult } from '../../application/queries/list-reports/list-reports.handler';
import { ListReportsQuery } from '../../application/queries/list-reports/list-reports.query';
import { type ReportEntity } from '../../domain/entities/report.entity';
import { MACRO_DATA_SERVICE, type IMacroDataService } from '../../domain/services/macro-data.service';
import { GenerateReportDto } from '../dto/generate-report.dto';
import { ListReportsDto } from '../dto/list-reports.dto';

interface AuthenticatedRequest {
  user: { sub: string };
}

@ApiTags('reports')
@Controller('reports')
export class ReportsController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
    @Inject(MACRO_DATA_SERVICE) private readonly macroDataService: IMacroDataService,
  ) {}

  @Post('generate')
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, QuotaGuard)
  @RequireQuota('reports_generated')
  @ApiOperation({ summary: 'Tạo báo cáo AI (async)' })
  @ApiResponse({ status: 201, description: 'Report generation started' })
  async generate(
    @Body() dto: GenerateReportDto,
    @Req() req: AuthenticatedRequest,
  ): Promise<GenerateReportResult> {
    return this.commandBus.execute(
      new GenerateReportCommand(req.user.sub, dto.type, dto.title, dto.params),
    );
  }

  @Get()
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Danh sách báo cáo của tôi' })
  @ApiResponse({ status: 200, description: 'List of reports' })
  async list(
    @Query() dto: ListReportsDto,
    @Req() req: AuthenticatedRequest,
  ) {
    const result: ListReportsResult = await this.queryBus.execute(
      new ListReportsQuery(req.user.sub, dto.type, dto.limit, dto.offset),
    );
    return {
      data: result.reports.map((r) => this.toResponse(r)),
      total: result.total,
    };
  }

  @Get('macro-data')
  @ApiOperation({ summary: 'Dữ liệu kinh tế vĩ mô theo tỉnh' })
  @ApiQuery({ name: 'province', required: true, description: 'Province name' })
  @ApiQuery({ name: 'categories', required: false, isArray: true, description: 'Indicator categories to retrieve' })
  @ApiQuery({ name: 'fromYear', required: false, description: 'Start year (default 2020)' })
  @ApiQuery({ name: 'toYear', required: false, description: 'End year (default 2025)' })
  @ApiResponse({ status: 200, description: 'Macro-economic data grouped by indicator' })
  async getMacroData(
    @Query('province') province: string,
    @Query('categories') categories?: string | string[],
    @Query('fromYear') fromYear?: string,
    @Query('toYear') toYear?: string,
  ) {
    const indicators = categories
      ? Array.isArray(categories) ? categories : [categories]
      : undefined;
    const from = fromYear ? parseInt(fromYear, 10) : 2020;
    const to = toYear ? parseInt(toYear, 10) : 2025;

    const rows = await this.macroDataService.getByProvince(province, indicators);

    // Filter by year range and group by indicator
    type MacroPoint = { year: number; value: number; unit: string; yoy_change: number | null };
    const grouped = new Map<string, MacroPoint[]>();
    for (const row of rows) {
      const year = parseInt(row.period, 10);
      if (isNaN(year) || year < from || year > to) continue;
      let series = grouped.get(row.indicator);
      if (!series) {
        series = [];
        grouped.set(row.indicator, series);
      }
      series.push({ year, value: row.value, unit: row.unit, yoy_change: null });
    }

    // Sort each group by year and compute YoY change
    for (const series of grouped.values()) {
      series.sort((a, b) => a.year - b.year);
      for (let i = 1; i < series.length; i++) {
        const prev = series[i - 1]!;
        const curr = series[i]!;
        if (prev.value !== 0) {
          curr.yoy_change = parseFloat((((curr.value - prev.value) / prev.value) * 100).toFixed(2));
        }
      }
    }

    return {
      province,
      data: Object.fromEntries(grouped),
      highlights: [],
    };
  }

  @Get(':id')
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Chi tiết báo cáo' })
  @ApiResponse({ status: 200, description: 'Report details' })
  async getById(
    @Param('id') id: string,
    @Req() req: AuthenticatedRequest,
  ) {
    const report: ReportEntity = await this.queryBus.execute(
      new GetReportQuery(id, req.user.sub),
    );
    return this.toResponse(report);
  }

  @Get(':id/status')
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Trạng thái tạo báo cáo' })
  @ApiResponse({ status: 200, description: 'Report status' })
  async getStatus(
    @Param('id') id: string,
    @Req() req: AuthenticatedRequest,
  ) {
    const report: ReportEntity = await this.queryBus.execute(
      new GetReportQuery(id, req.user.sub),
    );
    return {
      id: report.id,
      status: report.status,
      errorMsg: report.errorMsg,
      pdfUrl: report.pdfUrl,
    };
  }

  @Delete(':id')
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Xóa báo cáo' })
  @ApiResponse({ status: 204, description: 'Report deleted' })
  async delete(
    @Param('id') id: string,
    @Req() req: AuthenticatedRequest,
  ): Promise<void> {
    await this.commandBus.execute(new DeleteReportCommand(id, req.user.sub));
  }

  private toResponse(report: ReportEntity) {
    return {
      id: report.id,
      type: report.type,
      title: report.title,
      params: report.params,
      content: report.content,
      pdfUrl: report.pdfUrl,
      status: report.status,
      errorMsg: report.errorMsg,
      createdAt: report.createdAt.toISOString(),
      updatedAt: report.updatedAt.toISOString(),
    };
  }
}
