import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  HttpStatus,
  ParseIntPipe,
  ValidationPipe,
  UsePipes,
  HttpCode,
  UseGuards,
  Req,
  Res,
} from '@nestjs/common';
import { Response } from 'express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiParam,
  ApiBody,
  ApiQuery,
  ApiBearerAuth,
  ApiSecurity,
} from '@nestjs/swagger';
import { ProgramAccessService } from './program-access.service';
import { AddProgramAccessUsersDto } from './dto/add-program-access-users.dto';
import { UpdateProgramAccessUserDto } from './dto/update-program-access-user.dto';
import { QueryProgramAccessUsersDto } from './dto/query-program-access-users.dto';
import {
  ProgramAccessUserResponseDto,
  ProgramAccessUsersListResponseDto,
} from './dto/program-access-user-response.dto';
import { BulkUpdateProgramAccessDto } from './dto/bulk-update-program-access.dto';
import { BulkRemoveProgramAccessDto } from './dto/bulk-remove-program-access.dto';
import { ResponseService } from 'src/common/response-handling/response-handler';
import { AppLoggerService } from 'src/common/services/logger.service';
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 { SWAGGER_API_RESPONSE, programAccessMessages, programAccessControllerErrors } from 'src/common/constants/strings-constants';
import { handleControllerError } from 'src/common/utils/controller-response-handling';

@ApiTags('program-access')
@Controller('program/:programId/access-users')
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles('admin', 'mahatria', 'shoba')
@ApiBearerAuth('Authorization')
@ApiSecurity('userIdAuth')
@ApiSecurity('activeRoleAuth')
export class ProgramAccessController {
  constructor(
    private readonly programAccessService: ProgramAccessService,
    private readonly responseService: ResponseService,
    private readonly logger: AppLoggerService,
  ) {}

  /**
   * Add users to program access map (bulk)
   */
  @Post()
  @HttpCode(HttpStatus.CREATED)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Add users to program access map' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiBody({ type: AddProgramAccessUsersDto })
  @ApiResponse({
    status: HttpStatus.CREATED,
    description: 'Users added to program access map successfully',
    type: [ProgramAccessUserResponseDto],
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: SWAGGER_API_RESPONSE.BAD_REQUEST })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: SWAGGER_API_RESPONSE.NOT_FOUND })
  @ApiResponse({
    status: HttpStatus.CONFLICT,
    description: 'Active access already exists for some users',
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async addProgramAccessUsers(
    @Param('programId', ParseIntPipe) programId: number,
    @Body() dto: AddProgramAccessUsersDto,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.ADD_USERS_REQUEST_RECEIVED(programId, dto.userIds.length));
    try {
      const userId = req.user?.id;
      const data = await this.programAccessService.addProgramAccessUsers(programId, dto, userId);

      return this.responseService.success(
        res,
        programAccessMessages.ADD_USERS_SUCCESS,
        data,
        HttpStatus.CREATED,
      );
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_ADDING_USERS_TO_MAP(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Get all users with access to a program
   */
  @Get()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get all users with access to a program' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiQuery({ name: 'accessScope', required: false, description: 'Filter by access scope' })
  @ApiQuery({ name: 'state', required: false, description: 'Filter by state' })
  @ApiQuery({ name: 'includeDeleted', required: false, type: Boolean, description: 'Include soft-deleted records' })
  @ApiQuery({ name: 'limit', required: false, type: Number, description: 'Number of records to return' })
  @ApiQuery({ name: 'offset', required: false, type: Number, description: 'Number of records to skip' })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Program access users retrieved successfully',
    type: ProgramAccessUsersListResponseDto,
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: SWAGGER_API_RESPONSE.BAD_REQUEST })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async getProgramAccessUsers(
    @Param('programId', ParseIntPipe) programId: number,
    @Query(new ValidationPipe({ transform: true })) query: QueryProgramAccessUsersDto,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.GET_ACCESS_USERS_REQUEST_RECEIVED(programId));
    try {
      const data = await this.programAccessService.getProgramAccessUsers(programId, query);

      return this.responseService.success(
        res,
        programAccessMessages.ACCESS_USERS_RETRIEVED,
        data,
        HttpStatus.OK,
      );
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_GETTING_USERS(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Bulk update program access for multiple users
   */
  @Patch('bulk')
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Bulk update program access for multiple users' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiBody({ type: BulkUpdateProgramAccessDto })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Bulk update completed',
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: SWAGGER_API_RESPONSE.BAD_REQUEST })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async bulkUpdateProgramAccessUsers(
    @Param('programId', ParseIntPipe) programId: number,
    @Body() dto: BulkUpdateProgramAccessDto,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.BULK_UPDATE_REQUEST_RECEIVED(programId, dto.userIds.length));
    try {
      const updatedBy = req.user?.id;
      const data = await this.programAccessService.bulkUpdateProgramAccessUsers(
        programId,
        dto,
        updatedBy,
      );

      return this.responseService.success(
        res,
        programAccessMessages.BULK_UPDATE_SUCCESS,
        data,
        HttpStatus.OK,
      );
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_BULK_UPDATING(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Bulk remove program access for multiple users
   */
  @Delete('bulk')
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Bulk remove program access for multiple users with optional cascade' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiBody({ type: BulkRemoveProgramAccessDto })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Bulk removal completed',
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: SWAGGER_API_RESPONSE.BAD_REQUEST })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async bulkRemoveProgramAccessUsers(
    @Param('programId', ParseIntPipe) programId: number,
    @Body() dto: BulkRemoveProgramAccessDto,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.BULK_REMOVE_REQUEST_RECEIVED(programId, dto.userIds.length));
    try {
      const deletedBy = req.user?.id;
      const data = await this.programAccessService.bulkRemoveProgramAccessUsers(
        programId,
        dto,
        deletedBy,
      );

      return this.responseService.success(res, programAccessMessages.BULK_REMOVE_SUCCESS, data, HttpStatus.OK);
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_BULK_REMOVING(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Update program access for a specific user
   */
  @Patch(':userId')
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Update program access for a specific user' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiBody({ type: UpdateProgramAccessUserDto })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Program access updated successfully',
    type: ProgramAccessUserResponseDto,
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: SWAGGER_API_RESPONSE.BAD_REQUEST })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Access mapping not found' })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async updateProgramAccessUser(
    @Param('programId', ParseIntPipe) programId: number,
    @Param('userId', ParseIntPipe) userId: number,
    @Body() dto: UpdateProgramAccessUserDto,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.UPDATE_USER_REQUEST_RECEIVED(programId, userId));
    try {
      const updatedBy = req.user?.id;
      const data = await this.programAccessService.updateProgramAccessUser(
        programId,
        userId,
        dto,
        updatedBy,
      );

      return this.responseService.success(
        res,
        programAccessMessages.UPDATE_USER_SUCCESS,
        data,
        HttpStatus.OK,
      );
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_UPDATING_USER(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Remove program access for a specific user (soft delete)
   */
  @Delete(':userId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Remove program access for a specific user (soft delete)' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Program access removed successfully',
  })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Access mapping not found' })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async removeProgramAccessUser(
    @Param('programId', ParseIntPipe) programId: number,
    @Param('userId', ParseIntPipe) userId: number,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.REMOVE_USER_REQUEST_RECEIVED(programId, userId));
    try {
      const deletedBy = req.user?.id;
      const data = await this.programAccessService.removeProgramAccessUser(
        programId,
        userId,
        deletedBy,
      );

      return this.responseService.success(res, programAccessMessages.REMOVE_USER_SUCCESS, data, HttpStatus.OK);
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_REMOVING_USER(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }

  /**
   * Check if a user has access to a program
   */
  @Get('check/:userId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Check if a user has access to a program' })
  @ApiParam({ name: 'programId', type: Number, description: 'Program ID' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Access check completed',
  })
  @ApiResponse({
    status: HttpStatus.INTERNAL_SERVER_ERROR,
    description: SWAGGER_API_RESPONSE.INTERNAL_SERVER_ERROR,
  })
  async checkUserAccess(
    @Param('programId', ParseIntPipe) programId: number,
    @Param('userId', ParseIntPipe) userId: number,
    @Res() res: Response,
  ) {
    this.logger.log(programAccessMessages.CHECK_ACCESS_REQUEST_RECEIVED(programId, userId));
    try {
      const hasAccess = await this.programAccessService.hasUserAccessToProgram(programId, userId);
      const canRegister = await this.programAccessService.canUserRegisterForProgram(
        programId,
        userId,
      );

      const data = {
        hasAccess,
        canRegister,
        programId,
        userId,
      };

      return this.responseService.success(res, programAccessMessages.ACCESS_CHECK_SUCCESS, data, HttpStatus.OK);
    } catch (error) {
      this.logger.error(programAccessControllerErrors.ERROR_CHECKING_USER_ACCESS(error.message), error.stack);
      return handleControllerError(res, error);
    }
  }
}
