import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Query,
  Body,
  HttpCode,
  HttpStatus,
  Logger,
  UseGuards,
  Req,
  ParseUUIDPipe,
  ParseIntPipe,
  ValidationPipe,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { RoleService } from './role.service';
import { CreateRoleDto } from './dto/create-role.dto';
import { RoleResponseDto } from './dto/role-response.dto';
import { UpdateRoleDto } from './dto/update-role.dto';
import { PaginatedResponseDto } from 'common/utils/paginated-response.dto';
import { PermissionWithStateDto } from '../permissions/dto/permission-state.dto';
import { UserResponseDto } from 'modules/user/dto/user-response.dto';
import { UserPermissionOverrideDto } from './dto/user-permission-override.dto';
import { EffectivePermissionDto } from './dto/effective-permission.dto';
import { JwtAuthGuard } from 'common/guards/jwt-auth.guard';
import { RolesGuard } from 'common/guards/roles.guard';
import { PermissionsGuard } from 'common/guards/ppermissions.guard';
import { Roles } from 'common/decorators/role.decorator';
import { UserRole } from 'common/enum/UserRole.enum';
import { SubPermissionEnum } from 'common/enum/Permissions.enum';
import { Permissions } from 'common/decorators/permissions.decorator';

@ApiTags('Roles')
@ApiBearerAuth()
@Controller({ path: 'roles', version: '1' })
@UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
export class RoleController {
  private readonly logger = new Logger(RoleController.name);
  constructor(private readonly roleService: RoleService) {}

  @Post('create')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.CREATE)
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a new role' })
  @ApiBody({ type: CreateRoleDto })
  @ApiResponse({ type: RoleResponseDto, status: HttpStatus.CREATED, description: 'Role created successfully' })
  async createRole(@Body() createRoleDto: CreateRoleDto): Promise<RoleResponseDto> {
    this.logger.log(`Creating role with data: ${JSON.stringify(createRoleDto)}`);
    return this.roleService.createRole(createRoleDto);
  }

  // Update a role
  @Put(':roleId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.UPDATE)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Update an existing role' })
  @ApiParam({ name: 'roleId', type: String, description: 'ID of the role to update' })
  @ApiBody({ type: UpdateRoleDto })
  @ApiResponse({ type: RoleResponseDto, status: HttpStatus.OK, description: 'Role updated successfully' })
  async updateRole(@Param('roleId') roleId: string, @Body() updateRoleDto: UpdateRoleDto): Promise<RoleResponseDto> {
    this.logger.log(`Updating role with ID: ${roleId} and data: ${JSON.stringify(updateRoleDto)}`);
    return this.roleService.updateRole(roleId, updateRoleDto);
  }

  // Delete a role
  @Delete(':roleId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.DELETE)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Delete a role' })
  @ApiParam({ name: 'roleId', type: String, description: 'ID of the role to delete' })
  @ApiResponse({ status: HttpStatus.NO_CONTENT, description: 'Role deleted successfully' })
  async deleteRole(@Param('roleId') roleId: string): Promise<void> {
    this.logger.log(`Deleting role with ID: ${roleId}`);
    return this.roleService.deleteRole(roleId);
  }

  // Get all roles with pagination
  @Get()
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get all roles with pagination' })
  @ApiQuery({ name: 'page', type: Number, required: false, description: 'Page number for pagination', example: 1 })
  @ApiQuery({ name: 'limit', type: Number, required: false, description: 'Number of roles per page', example: 10 })
  @ApiResponse({ type: [RoleResponseDto], status: HttpStatus.OK, description: 'List of roles' })
  async getAllRoles(
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
    @Req() req: any,
  ): Promise<PaginatedResponseDto<RoleResponseDto>> {
    this.logger.log(`Fetching roles with pagination: page=${page}, limit=${limit}`);
    const clientId = req.user?.clientId; // Assuming clientId is stored in the JWT token
    return this.roleService.findAllRoles(page, limit, clientId);
  }

  // Get a role by ID
  @Get(':roleId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.VIEW_BY_ID)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get a role by ID' })
  @ApiParam({ name: 'roleId', type: String, description: 'ID of the role to retrieve' })
  @ApiResponse({ type: RoleResponseDto, status: HttpStatus.OK, description: 'Role retrieved successfully' })
  async getRoleById(@Param('roleId') roleId: string): Promise<RoleResponseDto> {
    this.logger.log(`Fetching role with ID: ${roleId}`);
    return this.roleService.findRoleById(roleId);
  }

  // Get roles by client ID
  @Get('client/:clientId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.GET_ROLES_BY_CLIENT)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get roles by client ID' })
  @ApiParam({ name: 'clientId', type: String, description: 'ID of the client to retrieve roles for' })
  @ApiResponse({ type: [RoleResponseDto], status: HttpStatus.OK, description: 'List of roles for the client' })
  async getRolesByClientId(@Param('clientId') clientId: string): Promise<RoleResponseDto[]> {
    this.logger.log(`Fetching roles for client with ID: ${clientId}`);
    return this.roleService.getRolesByClientId(clientId);
  }

  // Get a role by name
  @Get('name/:roleName')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.VIEW_BY_NAME)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get a role by name' })
  @ApiParam({ name: 'roleName', type: String, description: 'Name of the role to retrieve' })
  @ApiResponse({ type: RoleResponseDto, status: HttpStatus.OK, description: 'Role retrieved successfully' })
  async getRoleByName(@Param('roleName') roleName: string, @Req() req: any): Promise<RoleResponseDto> {
    this.logger.log(`Fetching role with name: ${roleName}`);
    const clientId = req.user?.clientId; // Assuming clientId is stored in the JWT token
    return this.roleService.findRoleByName(roleName, clientId);
  }

  // Get permissions of a role by role ID
  @Get(':roleId/permissions')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.GET_PERMISSIONS)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get permissions of a role by role ID' })
  @ApiParam({ name: 'roleId', type: String, description: 'ID of the role to retrieve permissions for' })
  @ApiResponse({ type: [PermissionWithStateDto], status: HttpStatus.OK, description: 'List of permissions for the role' })
  async getRolePermissions(@Param('roleId') roleId: string): Promise<PermissionWithStateDto[]> {
    this.logger.log(`Fetching permissions for role with ID: ${roleId}`);
    return this.roleService.getPermissionsByRoleId(roleId);
  }

  // Get sub-permissions of a role by role ID
  @Get(':roleId/subpermissions')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.GET_SUBPERMISSIONS)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get sub-permissions of a role by role ID' })
  @ApiParam({ name: 'roleId', type: String, description: 'ID of the role to retrieve sub-permissions for' })
  @ApiResponse({ type: [PermissionWithStateDto], status: HttpStatus.OK, description: 'List of sub-permissions for the role' })
  async getRoleSubPermissions(@Param('roleId') roleId: string): Promise<PermissionWithStateDto[]> {
    this.logger.log(`Fetching sub-permissions for role with ID: ${roleId}`);
    return this.roleService.getSubPermissionsByRoleId(roleId);
  }

  @Put(':id/permissions/:permissionId/state')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.ASSIGN_PERMISSIONS)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Set permission state for a role' })
  @ApiResponse({ status: 204, description: 'Permission state updated successfully' })
  @ApiParam({ name: 'id', type: String, description: 'Role ID' })
  @ApiParam({ name: 'permissionId', type: String, description: 'Permission ID' })
  @ApiBody({ schema: { type: 'object', properties: { state: { type: 'string', enum: ['ALLOW', 'DENY', 'INHERIT'] } } } })
  async setRolePermissionState(
    @Param('id', ParseUUIDPipe) id: string,
    @Param('permissionId', ParseUUIDPipe) permissionId: string,
    @Body('state') state: string,
  ): Promise<void> {
    return this.roleService.setRolePermissionState(id, permissionId, state);
  }

  @Put(':id/sub-permissions/:subPermissionId/state')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.ASSIGN_PERMISSIONS)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Set sub-permission state for a role' })
  @ApiResponse({ status: 204, description: 'Sub-permission state updated successfully' })
  @ApiParam({ name: 'id', type: String, description: 'Role ID' })
  @ApiParam({ name: 'subPermissionId', type: String, description: 'Sub-permission ID' })
  @ApiBody({ schema: { type: 'object', properties: { state: { type: 'string', enum: ['ALLOW', 'DENY', 'INHERIT'] } } } })
  async setRoleSubPermissionState(
    @Param('id', ParseUUIDPipe) id: string,
    @Param('subPermissionId', ParseUUIDPipe) subPermissionId: string,
    @Body('state') state: string,
  ): Promise<void> {
    return this.roleService.setRoleSubPermissionState(id, subPermissionId, state);
  }

  // User-Role Management Endpoints
  @Get(':id/users')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.GET_USERS_BY_ROLE)
  @ApiOperation({ summary: 'Get users assigned to a role' })
  @ApiResponse({ status: 200, description: 'Role users retrieved successfully', type: [UserResponseDto] })
  @ApiParam({ name: 'id', type: String, description: 'Role ID' })
  async getRoleUsers(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto[]> {
    return this.roleService.getUsersByRoleId(id);
  }

  @Post('assign-by-email')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.ASSIGN_USER)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Assign user to role by email' })
  @ApiResponse({ status: 204, description: 'User assigned to role successfully' })
  @ApiResponse({ status: 404, description: 'User or role not found' })
  @ApiResponse({ status: 409, description: 'User already has the role' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        email: { type: 'string', format: 'email' },
        roleId: { type: 'string', format: 'uuid' },
        clientId: { type: 'string', format: 'uuid' },
      },
      required: ['email', 'roleId'],
    },
  })
  async assignUserToRoleByEmail(@Body() body: { email: string; roleId: string; clientId: string }): Promise<void> {
    const decodedEmail = decodeURIComponent(body.email);
    body.email = decodedEmail;
    this.logger.log(`Assigning user with email: ${body.email} to role with ID: ${body.roleId} for client ID: ${body.clientId}`);
    return this.roleService.assignUserToRoleByEmail(body.email, body.roleId, body.clientId);
  }

  @Delete(':id/users/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.REMOVE_USER)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Remove user from role' })
  @ApiResponse({ status: 204, description: 'User removed from role successfully' })
  @ApiParam({ name: 'id', type: String, description: 'Role ID' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  async removeUserFromRole(@Param('id', ParseUUIDPipe) id: string, @Param('userId', ParseIntPipe) userId: number): Promise<void> {
    this.logger.log(`Removing user with ID: ${userId} from role with ID: ${id}`);
    return this.roleService.removeUserFromRole(userId, id);
  }

  // User Permission Override Endpoints
  @Post('user-permission-override')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.ASSIGN_PERMISSIONS)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Set user permission override' })
  @ApiResponse({ status: 204, description: 'User permission override set successfully' })
  @ApiBody({ type: UserPermissionOverrideDto })
  async setUserPermissionOverride(@Body(ValidationPipe) override: UserPermissionOverrideDto): Promise<void> {
    this.logger.log(`Setting user permission override: ${JSON.stringify(override)}`);
    return this.roleService.setUserPermissionOverride(override);
  }

  @Delete('user-permission-override/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.REMOVE_USER)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Remove user permission override' })
  @ApiResponse({ status: 204, description: 'User permission override removed successfully' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiQuery({ name: 'permissionId', required: false, type: String, description: 'Permission ID' })
  @ApiQuery({ name: 'subPermissionId', required: false, type: String, description: 'Sub-permission ID' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async removeUserPermissionOverride(
    @Param('userId', ParseIntPipe) userId: number,
    @Query('permissionId') permissionId?: string,
    @Query('subPermissionId') subPermissionId?: string,
    @Query('clientId') clientId?: string,
  ): Promise<void> {
    this.logger.log(
      `Removing permission override for user ID: ${userId}, permission ID: ${permissionId}, sub-permission ID: ${subPermissionId}, client ID: ${clientId}`,
    );
    return this.roleService.removeUserPermissionOverride(userId, permissionId, subPermissionId, clientId);
  }

  @Get('user-permission-override/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN)
  // @Permissions(SubPermissionEnum.GET_USERS_BY_ROLE)
  @ApiOperation({ summary: 'Get user permission overrides' })
  @ApiResponse({ status: 200, description: 'User permission overrides retrieved successfully' })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async getUserPermissionOverrides(@Param('userId', ParseIntPipe) userId: number, @Query('clientId') clientId?: string) {
    this.logger.log(`Fetching permission overrides for user ID: ${userId}, client ID: ${clientId}`);
    return this.roleService.getUserPermissionOverrides(userId, clientId);
  }

  // User Permission Checking Endpoints
  @Get('check-permission/:userId/:permissionName')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN, UserRole.USER)
  @ApiOperation({ summary: 'Check if user has specific permission' })
  @ApiResponse({ status: 200, description: 'Permission check result', schema: { type: 'boolean' } })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiParam({ name: 'permissionName', type: String, description: 'Permission name' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async checkUserPermission(
    @Param('userId', ParseIntPipe) userId: number,
    @Param('permissionName') permissionName: string,
    @Query('clientId') clientId?: string,
  ): Promise<boolean> {
    return this.roleService.checkUserPermission(userId, permissionName, clientId);
  }

  @Get('check-sub-permission/:userId/:subPermissionName')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN, UserRole.USER)
  @ApiOperation({ summary: 'Check if user has specific sub-permission' })
  @ApiResponse({ status: 200, description: 'Sub-permission check result', schema: { type: 'boolean' } })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiParam({ name: 'subPermissionName', type: String, description: 'Sub-permission name' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async checkUserSubPermission(
    @Param('userId', ParseIntPipe) userId: number,
    @Param('subPermissionName') subPermissionName: string,
    @Query('clientId') clientId?: string,
  ): Promise<boolean> {
    return this.roleService.checkUserSubPermission(userId, subPermissionName, clientId);
  }

  // User Effective Permissions
  @Get('effective-permissions/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN, UserRole.USER)
  @ApiOperation({ summary: 'Get effective permissions for a user' })
  @ApiResponse({ status: 200, description: 'Effective permissions retrieved successfully', type: [EffectivePermissionDto] })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async getUserEffectivePermissions(
    @Param('userId', ParseIntPipe) userId: number,
    @Query('clientId') clientId?: string,
  ): Promise<EffectivePermissionDto[]> {
    return this.roleService.getUserEffectivePermissions(userId, clientId);
  }

  @Get('user-permissions/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN, UserRole.USER)
  @ApiOperation({ summary: 'Get permissions for a user' })
  @ApiResponse({ status: 200, description: 'User permissions retrieved successfully', type: [EffectivePermissionDto] })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async getUserPermissions(@Param('userId', ParseIntPipe) userId: number, @Query('clientId') clientId?: string): Promise<EffectivePermissionDto[]> {
    return this.roleService.getPermissionsByUserId(userId, clientId);
  }

  @Get('user-sub-permissions/:userId')
  @Roles(UserRole.SUPER_ADMIN, UserRole.CLIENT, UserRole.ADMIN, UserRole.USER)
  @ApiOperation({ summary: 'Get sub-permissions for a user' })
  @ApiResponse({ status: 200, description: 'User sub-permissions retrieved successfully', type: [EffectivePermissionDto] })
  @ApiParam({ name: 'userId', type: Number, description: 'User ID' })
  @ApiQuery({ name: 'clientId', required: false, type: String, description: 'Client ID' })
  async getUserSubPermissions(
    @Param('userId', ParseIntPipe) userId: number,
    @Query('clientId') clientId?: string,
  ): Promise<EffectivePermissionDto[]> {
    return this.roleService.getSubPermissionsByUserId(userId, clientId);
  }
}
