import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  UseInterceptors,
  Inject,
} from '@nestjs/common';
import {
  CacheInterceptor,
  CacheKey,
  CacheTTL,
  CACHE_MANAGER,
} from '@nestjs/cache-manager';
import * as cacheManager from 'cache-manager';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { Roles } from '../../common/decorators';
import { PrismaService } from '../../database/prisma.service';
import { PaginationDto } from '../../common/dto/pagination.dto';
import {
  ApiResponse,
  createSuccessResponse,
  createPaginatedResponse,
  PaginatedData,
} from '../../common/types/api-response.type';
import { plainToInstance } from 'class-transformer';
import { UserResponseDto } from '../users/dto/user.dto';
import {
  PermissionResponseDto,
  RolePermissionResponseDto,
  RoleResponseDto,
  UserRoleResponseDto,
} from './dto/admin.dto';

@ApiTags('Admin')
@ApiBearerAuth()
@Controller('admin')
@Roles('admin')
export class AdminController {
  constructor(
    private readonly prisma: PrismaService,
    @Inject(CACHE_MANAGER) private cacheManager: cacheManager.Cache,
  ) {}

  // ================== Users Management ==================

  @Get('users')
  @ApiOperation({ summary: 'Get all users (admin)' })
  async getAllUsers(
    @Query() pagination: PaginationDto,
  ): Promise<ApiResponse<PaginatedData<UserResponseDto>>> {
    const { skip, take, orderBy } = pagination;

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        skip,
        take,
        orderBy,
        include: {
          roles: {
            include: {
              role: true,
            },
          },
        },
      }),
      this.prisma.user.count(),
    ]);

    const dtos = plainToInstance(
      UserResponseDto,
      users,
    ) as unknown as UserResponseDto[];

    return createPaginatedResponse(
      dtos,
      total,
      pagination.page || 1,
      pagination.limit || 10,
    );
  }

  @Put('users/:id/toggle-active')
  @ApiOperation({ summary: 'Toggle user active status' })
  async toggleUserActive(
    @Param('id') id: string,
  ): Promise<ApiResponse<UserResponseDto>> {
    const user = await this.prisma.user.findUnique({ where: { id } });

    const updated = await this.prisma.user.update({
      where: { id },
      data: { isActive: !user?.isActive },
    });

    return createSuccessResponse(
      plainToInstance(UserResponseDto, updated),
      'User status updated',
    );
  }

  @Post('users/:userId/roles/:roleId')
  @ApiOperation({ summary: 'Assign role to user' })
  async assignRole(
    @Param('userId') userId: string,
    @Param('roleId') roleId: string,
  ): Promise<ApiResponse<UserRoleResponseDto>> {
    const userRole = await this.prisma.userRole.create({
      data: { userId, roleId },
    });
    return createSuccessResponse(
      plainToInstance(UserRoleResponseDto, userRole),
      'Role assigned to user',
    );
  }

  @Delete('users/:userId/roles/:roleId')
  @ApiOperation({ summary: 'Remove role from user' })
  async removeRole(
    @Param('userId') userId: string,
    @Param('roleId') roleId: string,
  ): Promise<ApiResponse<null>> {
    await this.prisma.userRole.deleteMany({
      where: { userId, roleId },
    });
    return createSuccessResponse(null, 'Role removed from user');
  }

  // ================== Roles Management ==================

  @Get('roles')
  @UseInterceptors(CacheInterceptor)
  @CacheKey('roles_list')
  @CacheTTL(60 * 1000)
  @ApiOperation({ summary: 'Get all roles' })
  async getAllRoles(): Promise<ApiResponse<RoleResponseDto[]>> {
    const roles = await this.prisma.role.findMany({
      include: {
        permissions: {
          include: {
            permission: true,
          },
        },
        _count: {
          select: { users: true },
        },
      },
    });

    // Transform Prisma structure to DTO structure
    const transformedRoles = roles.map((role) => ({
      ...role,
      permissions: role.permissions.map((rp) => rp.permission),
    }));

    return createSuccessResponse(
      plainToInstance(
        RoleResponseDto,
        transformedRoles,
      ) as unknown as RoleResponseDto[],
    );
  }

  @Post('roles')
  @ApiOperation({ summary: 'Create a new role' })
  async createRole(
    @Body() data: { name: string; description?: string },
  ): Promise<ApiResponse<RoleResponseDto>> {
    const role = await this.prisma.role.create({ data });
    await this.cacheManager.del('roles_list');
    return createSuccessResponse(
      plainToInstance(RoleResponseDto, role),
      'Role created',
      201,
    );
  }

  @Put('roles/:id')
  @ApiOperation({ summary: 'Update a role' })
  async updateRole(
    @Param('id') id: string,
    @Body() data: { name?: string; description?: string },
  ): Promise<ApiResponse<RoleResponseDto>> {
    const role = await this.prisma.role.update({ where: { id }, data });
    await this.cacheManager.del('roles_list');
    return createSuccessResponse(
      plainToInstance(RoleResponseDto, role),
      'Role updated',
    );
  }

  @Delete('roles/:id')
  @ApiOperation({ summary: 'Delete a role' })
  async deleteRole(@Param('id') id: string): Promise<ApiResponse<null>> {
    await this.prisma.role.delete({ where: { id } });
    await this.cacheManager.del('roles_list');
    return createSuccessResponse(null, 'Role deleted');
  }

  // ================== Permissions Management ==================

  @Get('permissions')
  @UseInterceptors(CacheInterceptor)
  @CacheKey('permissions_list')
  @CacheTTL(60 * 1000)
  @ApiOperation({ summary: 'Get all permissions' })
  async getAllPermissions(): Promise<ApiResponse<PermissionResponseDto[]>> {
    const permissions = await this.prisma.permission.findMany();
    return createSuccessResponse(
      plainToInstance(
        PermissionResponseDto,
        permissions,
      ) as unknown as PermissionResponseDto[],
    );
  }

  @Post('permissions')
  @ApiOperation({ summary: 'Create a new permission' })
  async createPermission(
    @Body()
    data: {
      name: string;
      description?: string;
      resource: string;
      action: string;
    },
  ): Promise<ApiResponse<PermissionResponseDto>> {
    const permission = await this.prisma.permission.create({ data });
    await this.cacheManager.del('permissions_list');
    return createSuccessResponse(
      plainToInstance(PermissionResponseDto, permission),
      'Permission created',
      201,
    );
  }

  @Post('roles/:roleId/permissions/:permissionId')
  @ApiOperation({ summary: 'Assign permission to role' })
  async assignPermission(
    @Param('roleId') roleId: string,
    @Param('permissionId') permissionId: string,
  ): Promise<ApiResponse<RolePermissionResponseDto>> {
    const rolePermission = await this.prisma.rolePermission.create({
      data: { roleId, permissionId },
    });
    // Invalidate roles_list because permissions are nested in roles
    await this.cacheManager.del('roles_list');
    return createSuccessResponse(
      plainToInstance(RolePermissionResponseDto, rolePermission),
      'Permission assigned to role',
    );
  }

  @Delete('roles/:roleId/permissions/:permissionId')
  @ApiOperation({ summary: 'Remove permission from role' })
  async removePermission(
    @Param('roleId') roleId: string,
    @Param('permissionId') permissionId: string,
  ): Promise<ApiResponse<null>> {
    await this.prisma.rolePermission.deleteMany({
      where: { roleId, permissionId },
    });
    // Invalidate roles_list because permissions are nested in roles
    await this.cacheManager.del('roles_list');
    return createSuccessResponse(null, 'Permission removed from role');
  }
}
