import {
  Controller,
  Get,
  Post,
  Body,
  Query,
  HttpStatus,
  UseGuards,
  ParseIntPipe,
  Req,
  Res,
  ValidationPipe,
} from '@nestjs/common'
import {
  ApiBearerAuth,
  ApiBody,
  ApiOperation,
  ApiQuery,
  ApiResponse,
  ApiSecurity,
  ApiTags,
} from '@nestjs/swagger'
import { Response } from 'express'
import { CombinedAuthGuard } from 'src/auth/combined-auth.guard'
import { RolesGuard } from 'src/common/guards/roles.guard'
import { Roles } from 'src/common/decorators/roles.decorator'
import { ROLE_VALUES } from 'src/common/constants/strings-constants'
import { AppLoggerService } from 'src/common/services/logger.service'
import { ResponseService } from 'src/common/response-handling/response-handler'
import { handleControllerError } from 'src/common/utils/controller-response-handling'
import { ReportsService } from './reports.service'
import { GenerateReportDto } from './dto/generate-report.dto'

@ApiTags('Reports')
@ApiBearerAuth('Authorization')
@ApiSecurity('userIdAuth')
@ApiSecurity('activeRoleAuth')
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles(
  ROLE_VALUES.ADMIN,
  ROLE_VALUES.VIEWER,
  ROLE_VALUES.MAHATRIA,
  ROLE_VALUES.RM,
  ROLE_VALUES.FINANCE_MANAGER,
  ROLE_VALUES.RELATIONAL_MANAGER,
  ROLE_VALUES.COORDINATOR,
  ROLE_VALUES.OPERATIONAL_MANAGER,
  ROLE_VALUES.RM_SUPPORT,
)
@Controller('reports')
export class ReportsController {
  constructor(
    private readonly reportsService: ReportsService,
    private readonly responseService: ResponseService,
    private readonly logger: AppLoggerService,
  ) {}

  @Get('fields')
  @ApiOperation({
    summary: 'Get available report fields for a program',
    description: 'Returns the list of fields available for custom report generation based on the program configuration. Fields are filtered by program flags (e.g. HDB/MSD type, residential, grouped, waitlist).',
  })
  @ApiQuery({ name: 'programId', required: true, type: Number, description: 'ID of the program to fetch fields for' })
  @ApiResponse({
    status: 200,
    description: 'List of available report fields',
    schema: {
      example: {
        success: true,
        data: [
          { key: 'regId', label: 'Registration ID', group: 'Registration', order: 1 },
          { key: 'fullName', label: 'Full Name', group: 'Registration', order: 2 },
          { key: 'email', label: 'Email', group: 'Registration', order: 3 },
        ],
        error: null,
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Invalid programId' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 404, description: 'Program not found' })
  async getFields(
    @Query('programId', ParseIntPipe) programId: number,
    @Res() res: Response,
  ): Promise<void> {
    try {
      const data = await this.reportsService.getAvailableFields(programId)
      await this.responseService.success(res, 'Report fields fetched successfully', data, HttpStatus.OK)
    } catch (error) {
      await handleControllerError(res, error)
    }
  }

  @Post('generate')
  @ApiOperation({
    summary: 'Generate a custom report',
    description: `Generates a report for the selected fields and filters.
- **format: json** — returns \`{ data, tableHeaders, total }\` in the response body.
- **format: excel** — uploads the report to S3 and returns a signed \`downloadUrl\`.

Use \`GET /reports/fields?programId=<id>\` first to get the valid field keys for a program.`,
  })
  @ApiBody({ type: GenerateReportDto })
  @ApiResponse({
    status: 200,
    description: 'Report generated (JSON format)',
    schema: {
      example: {
        success: true,
        data: {
          tableHeaders: [
            { key: 'regId', alias: 'regId', label: 'Registration ID', order: 1, type: 'string', sortable: false, filterable: false },
            { key: 'fullName', alias: 'fullName', label: 'Full Name', order: 2, type: 'string', sortable: false, filterable: false },
          ],
          data: [
            { regId: 'REG-001', fullName: 'John Doe' },
          ],
          total: 1,
        },
        error: null,
      },
    },
  })
  @ApiResponse({
    status: 200,
    description: 'Report generated (Excel format) — returns a signed S3 download URL',
    schema: {
      example: {
        success: true,
        data: {
          downloadUrl: 'https://s3.amazonaws.com/bucket/exports/excel/report.xlsx?X-Amz-Signature=...',
        },
        error: null,
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Invalid fields or filters' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 404, description: 'Program not found (when programId filter is provided)' })
  async generate(
    @Body(new ValidationPipe({ transform: true, whitelist: true })) dto: GenerateReportDto,
    @Req() req: any,
    @Res() res: Response,
  ): Promise<void> {
    this.logger.log('Generate report request', dto)
    try {
      const user = req.user
      const result = await this.reportsService.generateReport(dto, {
        userRoles: user?.roles || [],
        userId: user?.id ?? null,
      })

      if (dto.format === 'excel') {
        const fileName = dto.fileName ?? 'report'
        const downloadUrl = await this.reportsService.buildExcel(result.data, dto.fields, fileName)
        await this.responseService.success(res, 'Report generated successfully', { downloadUrl }, HttpStatus.OK)
        return
      }

      await this.responseService.success(res, 'Report generated successfully', result, HttpStatus.OK)
    } catch (error) {
      await handleControllerError(res, error)
    }
  }
}
