import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { map } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { CurrentAdmin } from '../decorators/current-admin.decorator';
import {
  AssignUserPermissionDto,
  AssignUserRoleDto,
  CreateOAuthClientDto,
  CreateRoleDto,
  UpdateOAuthClientDto,
  UpdateRoleDto
} from '../dto/rbac.dto';
import {
  AdminGuard,
  AdminRequestUser,
  assertAdminAnyPermission,
  assertAdminPermission,
  RbacManageGuard,
  SuperAdminGuard
} from '../guards/admin.guard';

@ApiTags('RBAC и OAuth')
@ApiBearerAuth()
@UseGuards(AdminGuard)
@Controller('admin/rbac')
export class RbacController {
  constructor(private readonly core: CoreGrpcService) {}

  @Get('roles')
  @ApiOperation({ summary: 'Список ролей', description: 'Возвращает роли вместе с назначенными правами.' })
  listRoles() {
    return this.core.rbac.ListRoles({}).pipe(
      map((response) => {
        const payload = response as { roles?: Array<{ permissions?: unknown[] }> };
        return {
          roles: (payload.roles ?? []).map((role) => ({
            ...role,
            permissions: role.permissions ?? []
          }))
        };
      })
    );
  }

  @Get('permissions')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Список прав', description: 'Возвращает все доступные permissions.' })
  listPermissions() {
    return this.core.rbac.ListPermissions({});
  }

  @Get('oauth-scopes')
  @ApiOperation({ summary: 'OAuth scopes', description: 'Возвращает доступные scopes для OAuth-приложений.' })
  listOAuthScopes(@CurrentAdmin() admin: AdminRequestUser) {
    assertAdminAnyPermission(admin, 'canViewOAuth', 'canManageOAuth');
    return this.core.rbac.ListOAuthScopes({});
  }

  @Get('oauth-clients')
  @ApiOperation({ summary: 'OAuth приложения', description: 'Возвращает OAuth-клиенты и доступные scopes.' })
  listOAuthClients(@CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canViewOAuth');
    return this.core.rbac.ListOAuthClients({ actorUserId: admin.id });
  }

  @Post('roles')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Создать роль', description: 'Создаёт новую роль с набором прав.' })
  @ApiBody({ type: CreateRoleDto })
  createRole(@Body() dto: CreateRoleDto, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.CreateRole({ actorUserId: admin.id, ...dto });
  }

  @Patch('roles/:roleSlug')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Обновить роль', description: 'Изменяет название, описание и права роли.' })
  @ApiParam({ name: 'roleSlug', description: 'Slug роли' })
  @ApiBody({ type: UpdateRoleDto })
  updateRole(@Param('roleSlug') roleSlug: string, @Body() dto: UpdateRoleDto, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.UpdateRole({ actorUserId: admin.id, roleSlug, ...dto });
  }

  @Delete('roles/:roleSlug')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Удалить роль', description: 'Удаляет пользовательскую роль. Системные роли удалить нельзя.' })
  @ApiParam({ name: 'roleSlug', description: 'Slug роли' })
  deleteRole(@Param('roleSlug') roleSlug: string, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.DeleteRole({ actorUserId: admin.id, roleSlug });
  }

  @Post('users/:userId/roles')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Назначить роль пользователю' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiBody({ type: AssignUserRoleDto })
  assignRole(@Param('userId') userId: string, @Body() dto: AssignUserRoleDto, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.AssignUserRole({ actorUserId: admin.id, userId, roleSlug: dto.roleSlug });
  }

  @Delete('users/:userId/roles/:roleSlug')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Снять роль с пользователя' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiParam({ name: 'roleSlug', description: 'Slug роли' })
  removeRole(@Param('userId') userId: string, @Param('roleSlug') roleSlug: string, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.RemoveUserRole({ actorUserId: admin.id, userId, roleSlug });
  }

  @Post('users/:userId/permissions')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Назначить право пользователю', description: 'Прямое назначение permission без смены роли.' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiBody({ type: AssignUserPermissionDto })
  assignPermission(@Param('userId') userId: string, @Body() dto: AssignUserPermissionDto, @CurrentAdmin() admin: AdminRequestUser) {
    return this.core.rbac.AssignUserPermission({ actorUserId: admin.id, userId, permissionSlug: dto.permissionSlug });
  }

  @Delete('users/:userId/permissions/:permissionSlug')
  @UseGuards(RbacManageGuard)
  @ApiOperation({ summary: 'Снять право с пользователя' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiParam({ name: 'permissionSlug', description: 'Slug права' })
  removePermission(
    @Param('userId') userId: string,
    @Param('permissionSlug') permissionSlug: string,
    @CurrentAdmin() admin: AdminRequestUser
  ) {
    return this.core.rbac.RemoveUserPermission({ actorUserId: admin.id, userId, permissionSlug });
  }

  @Post('oauth-clients')
  @ApiOperation({ summary: 'Создать OAuth-приложение', description: 'Создаёт OAuth2-клиент и возвращает client secret один раз.' })
  @ApiBody({ type: CreateOAuthClientDto })
  createOAuthClient(@Body() dto: CreateOAuthClientDto, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageOAuth');
    return this.core.rbac.CreateOAuthClient({ actorUserId: admin.id, ...dto });
  }

  @Patch('oauth-clients/:clientId')
  @ApiOperation({ summary: 'Обновить OAuth-приложение', description: 'Изменяет redirect URI, scopes и статус приложения.' })
  @ApiParam({ name: 'clientId', description: 'Client ID приложения' })
  @ApiBody({ type: UpdateOAuthClientDto })
  updateOAuthClient(@Param('clientId') clientId: string, @Body() dto: UpdateOAuthClientDto, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageOAuth');
    return this.core.rbac.UpdateOAuthClient({ actorUserId: admin.id, clientId, ...dto });
  }

  @Delete('oauth-clients/:clientId')
  @ApiOperation({ summary: 'Удалить OAuth-приложение', description: 'Полностью удаляет OAuth2-клиент и связанные данные.' })
  @ApiParam({ name: 'clientId', description: 'Client ID приложения' })
  deleteOAuthClient(@Param('clientId') clientId: string, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageOAuth');
    return this.core.rbac.DeleteOAuthClient({ actorUserId: admin.id, clientId });
  }

  @Post('oauth-clients/:clientId/rotate-secret')
  @ApiOperation({ summary: 'Перевыпустить client secret', description: 'Генерирует новый secret для confidential-клиента.' })
  @ApiParam({ name: 'clientId', description: 'Client ID приложения' })
  rotateSecret(@Param('clientId') clientId: string, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageOAuth');
    return this.core.rbac.RotateOAuthSecret({ actorUserId: admin.id, clientId });
  }
}
