import {
  BadRequestException,
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Query,
  Req,
  Res,
  UnauthorizedException,
} from '@nestjs/common';
import type { Request, Response } from 'express';

import { CreateCdcRgcDecisionDto } from './dto/create-cdc-rgc-decision.dto';
import { CreateRgcDecisionDto } from './dto/create-rgc-decision.dto';
import { RgcDecisionQueryDto } from './dto/rgc-decision-query.dto';
import { UpdateRgcDecisionDto } from './dto/update-rgc-decision.dto';
import { RgcDecisionService } from './rgc-decision.service';

type AuthenticatedRequest = Request & {
  user?: {
    id?: number | string;
    userId?: number | string;
    sub?: number | string;
  };
};

@Controller('rgc-decisions')
export class RgcDecisionController {
  constructor(private readonly rgcDecisionService: RgcDecisionService) {}

  // ==========================================
  // 1. STATIC LOOKUP & SPECIFIC GET ROUTES
  // ==========================================
  @Get('lookups/categories')
  getCategories() {
    return this.rgcDecisionService.getCategories();
  }

  @Get('lookups/ministries')
  getMinistries() {
    return this.rgcDecisionService.getMinistries();
  }

  @Get('lookups/plenaries')
  getPlenaries() {
    return this.rgcDecisionService.getPlenaries();
  }

  @Get('lookups/issues')
  getIssues() {
    return this.rgcDecisionService.getIssues();
  }

  @Get('cdc-gpsf')
  findAllForCdcGpsf(
    @Query() query: RgcDecisionQueryDto,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.findAllForCdcGpsf(
      query,
      this.getOptionalUserId(request),
    );
  }

  @Get('scorecard')
  getScorecard(
    @Query('plenaryId') plenaryId: string | undefined,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.getScorecard(
      this.toOptionalPositiveInteger(plenaryId, 'plenaryId'),
      this.getOptionalUserId(request),
    );
  }

  @Get('export/csv')
  async exportCsv(
    @Query('plenaryId') plenaryId: string | undefined,
    @Req() request: AuthenticatedRequest,
    @Res() response: Response,
  ): Promise<void> {
    const safePlenaryId = this.toOptionalPositiveInteger(
      plenaryId,
      'plenaryId',
    );

    if (!safePlenaryId) {
      throw new BadRequestException(
        'plenaryId query parameter is required for CSV export.',
      );
    }

    const file = await this.rgcDecisionService.exportCsv(
      safePlenaryId,
      this.getOptionalUserId(request),
    );

    response.setHeader('Content-Type', 'text/csv; charset=utf-8');
    response.setHeader(
      'Content-Disposition',
      `attachment; filename="${file.fileName}"`,
    );

    response.status(HttpStatus.OK).send(file.content);
  }

  @Get('plenary/:plenaryId/ministries')
  getPlenaryMinistries(
    @Param('plenaryId', ParseIntPipe) plenaryId: number,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.getPlenaryMinistries(
      plenaryId,
      this.getOptionalUserId(request),
    );
  }

  @Get()
  findAll(
    @Query() query: RgcDecisionQueryDto,
    @Req() request: AuthenticatedRequest,
  ) {
    const userId =
      query.ministryOnly === true
        ? this.getRequiredUserId(request)
        : this.getOptionalUserId(request);

    return this.rgcDecisionService.findAll(query, userId);
  }

  // ==========================================
  // 2. POST ROUTES
  // ==========================================
  @Post()
  create(
    @Body() dto: CreateRgcDecisionDto,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.createForMinistry(
      dto,
      this.getRequiredUserId(request),
    );
  }

  @Post('ministry')
  createForMinistry(
    @Body() dto: CreateRgcDecisionDto,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.createForMinistry(
      dto,
      this.getRequiredUserId(request),
    );
  }

  @Post('cdc-gpsf')
  createForCdcGpsf(
    @Body() dto: CreateCdcRgcDecisionDto,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.createForCdcGpsf(
      dto,
      this.getRequiredUserId(request),
    );
  }

  @Post(':id/submit-to-cdc')
  @HttpCode(HttpStatus.OK)
  submitToCdc(
    @Param('id', ParseIntPipe) decisionId: number,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.submitToCdc(
      decisionId,
      this.getRequiredUserId(request),
    );
  }

  // ==========================================
  // 3. DYNAMIC PARAM ID ROUTES (ต้องដាក់នៅក្រោមគេបង្អស់)
  // ==========================================
  @Get(':id')
  findOne(
    @Param('id', ParseIntPipe) decisionId: number,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.findOne(
      decisionId,
      this.getOptionalUserId(request),
    );
  }

  @Patch(':id')
  update(
    @Param('id', ParseIntPipe) decisionId: number,
    @Body() dto: UpdateRgcDecisionDto,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.update(
      decisionId,
      dto,
      this.getRequiredUserId(request),
    );
  }

  @Delete(':id')
  @HttpCode(HttpStatus.OK)
  remove(
    @Param('id', ParseIntPipe) decisionId: number,
    @Req() request: AuthenticatedRequest,
  ) {
    return this.rgcDecisionService.remove(
      decisionId,
      this.getRequiredUserId(request),
    );
  }

  // ==========================================
  // HELPER METHODS
  // ==========================================
  private getRequiredUserId(request: AuthenticatedRequest): number {
    const userId = this.getOptionalUserId(request);

    if (!userId) {
      throw new UnauthorizedException(
        'x-user-id header is required. Please login again.',
      );
    }

    return userId;
  }

  private getOptionalUserId(request: AuthenticatedRequest): number | undefined {
    const headerUserId = request.headers['x-user-id'];

    const rawUserId =
      request.user?.id ??
      request.user?.userId ??
      request.user?.sub ??
      (Array.isArray(headerUserId) ? headerUserId[0] : headerUserId);

    if (typeof rawUserId !== 'string' && typeof rawUserId !== 'number') {
      return undefined;
    }

    const userId = Number(rawUserId);

    if (!Number.isInteger(userId) || userId < 1) {
      return undefined;
    }

    return userId;
  }

  private toRequiredPositiveInteger(value: unknown, fieldName: string): number {
    const parsed = this.toOptionalPositiveInteger(value, fieldName);

    if (!parsed) {
      throw new BadRequestException(`${fieldName} must be a positive integer.`);
    }

    return parsed;
  }

  private toOptionalPositiveInteger(
    value: unknown,
    fieldName: string,
  ): number | undefined {
    if (value === undefined || value === null || value === '') {
      return undefined;
    }

    const numberValue = Number(value);

    if (!Number.isInteger(numberValue) || numberValue < 1) {
      throw new BadRequestException(`${fieldName} must be a positive integer.`);
    }

    return numberValue;
  }
}
