import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  UseGuards,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { RolesGuard, Roles } from '@it0/common';
import { Role } from '../../../domain/entities/role.entity';
import { User } from '../../../domain/entities/user.entity';
import * as crypto from 'crypto';

@Controller('api/v1/auth/roles')
@UseGuards(RolesGuard)
@Roles('admin', 'platform_admin', 'platform_super_admin')
export class RoleController {
  constructor(
    @InjectRepository(Role)
    private readonly roleRepository: Repository<Role>,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  @Get()
  async listRoles() {
    const roles = await this.roleRepository.find({ order: { createdAt: 'DESC' } });
    const users = await this.userRepository.find();

    const data = roles.map((r) => {
      const userCount = users.filter((u) => u.roles?.includes(r.name)).length;
      return {
        id: r.id,
        name: r.name,
        description: r.description ?? '',
        permissionCount: r.permissions?.length ?? 0,
        userCount,
        isSystem: ['admin', 'operator', 'viewer'].includes(r.name),
        createdAt: r.createdAt.toISOString(),
      };
    });

    return { data, total: data.length };
  }

  @Post()
  async createRole(@Body() body: { name: string; description?: string }) {
    const role = this.roleRepository.create({
      id: crypto.randomUUID(),
      tenantId: 'default',
      name: body.name,
      description: body.description ?? '',
      permissions: [],
    });

    const saved = await this.roleRepository.save(role);
    return {
      id: saved.id,
      name: saved.name,
      description: saved.description ?? '',
      permissionCount: 0,
      userCount: 0,
      isSystem: false,
      createdAt: saved.createdAt.toISOString(),
    };
  }

  @Put(':id')
  async updateRole(
    @Param('id') id: string,
    @Body() body: { name?: string; description?: string },
  ) {
    const role = await this.roleRepository.findOne({ where: { id } });
    if (!role) throw new NotFoundException(`Role "${id}" not found`);

    if (body.name !== undefined) role.name = body.name;
    if (body.description !== undefined) role.description = body.description;

    const saved = await this.roleRepository.save(role);
    return {
      id: saved.id,
      name: saved.name,
      description: saved.description ?? '',
      permissionCount: saved.permissions?.length ?? 0,
      isSystem: ['admin', 'operator', 'viewer'].includes(saved.name),
      createdAt: saved.createdAt.toISOString(),
    };
  }

  @Delete(':id')
  async deleteRole(@Param('id') id: string) {
    const role = await this.roleRepository.findOne({ where: { id } });
    if (!role) throw new NotFoundException(`Role "${id}" not found`);
    await this.roleRepository.remove(role);
    return { success: true };
  }

  @Get(':roleId/permissions')
  async getRolePermissions(@Param('roleId') roleId: string) {
    const role = await this.roleRepository.findOne({ where: { id: roleId } });
    if (!role) throw new NotFoundException(`Role "${roleId}" not found`);

    // Map permission strings to Permission objects
    const data = (role.permissions ?? []).map((key, idx) => {
      const [resource, action] = key.includes(':') ? key.split(':') : ['general', key];
      return {
        id: `${roleId}-perm-${idx}`,
        key,
        resource,
        action,
        description: `${action} access to ${resource}`,
      };
    });

    return { data };
  }

  @Post(':roleId/permissions')
  async assignPermission(
    @Param('roleId') roleId: string,
    @Body() body: { permissionId: string },
  ) {
    const role = await this.roleRepository.findOne({ where: { id: roleId } });
    if (!role) throw new NotFoundException(`Role "${roleId}" not found`);

    // permissionId is the permission key string (e.g., "servers:read")
    if (!role.permissions.includes(body.permissionId)) {
      role.permissions = [...role.permissions, body.permissionId];
      await this.roleRepository.save(role);
    }

    return { success: true };
  }

  @Delete(':roleId/permissions')
  async revokePermission(
    @Param('roleId') roleId: string,
    @Body() body: { permissionId: string },
  ) {
    const role = await this.roleRepository.findOne({ where: { id: roleId } });
    if (!role) throw new NotFoundException(`Role "${roleId}" not found`);

    role.permissions = role.permissions.filter((p) => p !== body.permissionId);
    await this.roleRepository.save(role);

    return { success: true };
  }
}
