import {
  Controller,
  Post,
  Get,
  Put,
  Param,
  Body,
  UseGuards,
  HttpCode,
  HttpStatus,
  Query,
  Logger,
  ForbiddenException,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiParam,
  ApiQuery,
} from '@nestjs/swagger';
import { ApprovalsService } from './approvals.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { ApproveRequestDto } from './dto/approve-request.dto';
import { RejectRequestDto } from './dto/reject-request.dto';
import { RequestChangesDto } from './dto/request-changes.dto';
import { RevalidateDto } from './dto/revalidate.dto';
import { ApprovalResponseDto } from './dto/approval-response.dto';
import { CorrelationId } from '../../common/decorators/correlation-id.decorator';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { JwtPayload } from '../../common/interfaces/request-context.interface';
import { Department } from '../../database/models/department.model';
import { Inject, BadRequestException } from '@nestjs/common';
import { UuidValidationPipe } from '../../common/pipes/uuid-validation.pipe';

@ApiTags('Approvals')
@Controller('approvals')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
export class ApprovalsController {
  private readonly logger = new Logger(ApprovalsController.name);

  constructor(
    private readonly approvalsService: ApprovalsService,
    @Inject(Department)
    private readonly departmentModel: typeof Department,
  ) {}

  @Post(':requestId/approve')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Approve request (short form)',
    description: 'Approve a license request for a specific department',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Request approved successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid request or no pending approval found',
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async approveShort(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Body() dto: ApproveRequestDto,
    @CurrentUser() user: JwtPayload,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Approving request: ${requestId}`);

    if (!user.departmentCode) {
      throw new ForbiddenException('Department code not found in user context');
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${user.departmentCode}`);
    }

    return this.approvalsService.approve(requestId, department.id, dto, user.sub);
  }

  @Post('requests/:requestId/approve')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Approve request',
    description: 'Approve a license request for a specific department',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Request approved successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid request or no pending approval found',
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async approve(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Body() dto: ApproveRequestDto,
    @CurrentUser() user: JwtPayload,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Approving request: ${requestId}`);

    if (!user.departmentCode) {
      throw new ForbiddenException('Department code not found in user context');
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${user.departmentCode}`);
    }

    return this.approvalsService.approve(requestId, department.id, dto, user.sub);
  }

  @Post(':requestId/reject')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Reject request (short form)',
    description: 'Reject a license request for a specific department',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Request rejected successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid request or no pending approval found',
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async rejectShort(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Body() dto: RejectRequestDto,
    @CurrentUser() user: JwtPayload,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Rejecting request: ${requestId}`);

    if (!user.departmentCode) {
      throw new ForbiddenException('Department code not found in user context');
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${user.departmentCode}`);
    }

    return this.approvalsService.reject(requestId, department.id, dto, user.sub);
  }

  @Post('requests/:requestId/reject')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Reject request',
    description: 'Reject a license request for a specific department',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Request rejected successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid request or no pending approval found',
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async reject(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Body() dto: RejectRequestDto,
    @CurrentUser() user: JwtPayload,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Rejecting request: ${requestId}`);

    if (!user.departmentCode) {
      throw new ForbiddenException('Department code not found in user context');
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${user.departmentCode}`);
    }

    return this.approvalsService.reject(requestId, department.id, dto, user.sub);
  }

  @Post('requests/:requestId/request-changes')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Request changes on request',
    description: 'Request changes from applicant for a license request',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Changes requested successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid request or no pending approval found',
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async requestChanges(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Body() dto: RequestChangesDto,
    @CurrentUser() user: JwtPayload,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Requesting changes for request: ${requestId}`);

    if (!user.departmentCode) {
      throw new ForbiddenException('Department code not found in user context');
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${user.departmentCode}`);
    }

    return this.approvalsService.requestChanges(requestId, department.id, dto, user.sub);
  }

  @Get('pending')
  @ApiOperation({
    summary: 'Get pending approvals for current user',
    description: 'Get paginated list of pending approvals for the current department officer',
  })
  @ApiQuery({
    name: 'page',
    required: false,
    type: Number,
    description: 'Page number (default: 1)',
  })
  @ApiQuery({
    name: 'limit',
    required: false,
    type: Number,
    description: 'Items per page (default: 10)',
  })
  @ApiResponse({
    status: 200,
    description: 'Paginated list of pending approvals',
  })
  async getPendingApprovals(
    @CurrentUser() user: JwtPayload,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @CorrelationId() correlationId?: string,
  ) {
    this.logger.debug(`[${correlationId}] Fetching pending approvals for user: ${user.sub}`);

    if (!user.departmentCode) {
      // Return empty list for non-department users
      return {
        data: [],
        meta: { page: 1, limit: 10, total: 0, totalPages: 0 },
      };
    }

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: user.departmentCode });
    if (!department) {
      return {
        data: [],
        meta: { page: 1, limit: 10, total: 0, totalPages: 0 },
      };
    }

    return this.approvalsService.findPendingByDepartment(department.id, {
      page: parseInt(page || '1', 10),
      limit: parseInt(limit || '10', 10),
    });
  }

  @Get(':approvalId')
  @ApiOperation({
    summary: 'Get approval by ID',
    description: 'Retrieve approval details by approval ID',
  })
  @ApiParam({
    name: 'approvalId',
    description: 'Approval ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Approval details',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 404,
    description: 'Approval not found',
  })
  async findById(
    @Param('approvalId', UuidValidationPipe) approvalId: string,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Fetching approval: ${approvalId}`);
    return this.approvalsService.findById(approvalId);
  }

  @Get('requests/:requestId')
  @ApiOperation({
    summary: 'Get approvals for request',
    description: 'Retrieve all approvals for a license request',
  })
  @ApiParam({
    name: 'requestId',
    description: 'License request ID (UUID)',
  })
  @ApiQuery({
    name: 'includeInvalidated',
    required: false,
    description: 'Include invalidated approvals (default: false)',
    example: 'false',
  })
  @ApiResponse({
    status: 200,
    description: 'List of approvals',
    type: [ApprovalResponseDto],
  })
  @ApiResponse({
    status: 404,
    description: 'Request not found',
  })
  async findByRequestId(
    @Param('requestId', UuidValidationPipe) requestId: string,
    @Query('includeInvalidated') includeInvalidated?: string,
    @CorrelationId() correlationId?: string,
  ): Promise<ApprovalResponseDto[]> {
    this.logger.debug(`[${correlationId}] Fetching approvals for request: ${requestId}`);
    return this.approvalsService.findByRequestId(requestId, includeInvalidated === 'true');
  }

  @Get('department/:departmentCode')
  @ApiOperation({
    summary: 'Get approvals by department',
    description: 'Retrieve approvals for a specific department with pagination',
  })
  @ApiParam({
    name: 'departmentCode',
    description: 'Department code',
  })
  @ApiQuery({
    name: 'page',
    required: false,
    description: 'Page number (default: 1)',
    type: Number,
  })
  @ApiQuery({
    name: 'limit',
    required: false,
    description: 'Items per page (default: 20)',
    type: Number,
  })
  @ApiResponse({
    status: 200,
    description: 'Paginated list of approvals',
  })
  async findByDepartment(
    @Param('departmentCode') departmentCode: string,
    @Query() pagination: any,
    @CorrelationId() correlationId: string,
  ) {
    this.logger.debug(`[${correlationId}] Fetching approvals for department: ${departmentCode}`);

    // Look up department by code to get ID
    const department = await this.departmentModel.query().findOne({ code: departmentCode });
    if (!department) {
      throw new BadRequestException(`Department not found: ${departmentCode}`);
    }

    return this.approvalsService.findByDepartment(department.id, pagination);
  }

  @Put(':approvalId/revalidate')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Revalidate approval',
    description: 'Revalidate an invalidated approval after document updates',
  })
  @ApiParam({
    name: 'approvalId',
    description: 'Approval ID (UUID)',
  })
  @ApiResponse({
    status: 200,
    description: 'Approval revalidated successfully',
    type: ApprovalResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Approval is not in invalidated state',
  })
  @ApiResponse({
    status: 404,
    description: 'Approval not found',
  })
  async revalidate(
    @Param('approvalId', UuidValidationPipe) approvalId: string,
    @Body() dto: RevalidateDto,
    @CorrelationId() correlationId: string,
  ): Promise<ApprovalResponseDto> {
    this.logger.debug(`[${correlationId}] Revalidating approval: ${approvalId}`);
    return this.approvalsService.revalidateApproval(approvalId, dto);
  }
}
