import {
    BadRequestException,
    ConflictException,
    Injectable,
    NotFoundException,
  } from '@nestjs/common';
  import { TenantConnectionService } from '../../database/tenant-connection.service';
  import { requireCurrentTenant } from '../../database/tenant-context';
  import { Role } from '../../database/tenant/entities/role.entity';
  import { Permission } from '../../database/tenant/entities/permission.entity';
  import { RolePermission } from '../../database/tenant/entities/role-permission.entity';
  import { CreateRoleDto, UpdateRoleDto } from './dto/create-role.dto';

  @Injectable()
  export class RolesService {
    constructor(private readonly tenantConn: TenantConnectionService) {}

    private async ds() {
      const ctx = requireCurrentTenant();
      return this.tenantConn.getTenantDataSource(ctx.subdomain);
    }

    async listPermissions() {
      const ds = await this.ds();
      return ds
        .getRepository(Permission)
        .find({ order: { module: 'ASC', key: 'ASC' } });
    }

    async list() {
      const ds = await this.ds();
      return ds.getRepository(Role).find({
        relations: { rolePermissions: { permission: true } },
        order: { name: 'ASC' },
      });
    }

    async getOne(id: string) {
      const ds = await this.ds();
      const r = await ds.getRepository(Role).findOne({
        where: { id },
        relations: { rolePermissions: { permission: true } },
      });
      if (!r) throw new NotFoundException('Role not found');
      return r;
    }

    async create(dto: CreateRoleDto) {
      const ds = await this.ds();
      const repo = ds.getRepository(Role);
      const existing = await repo.findOne({ where: { name: dto.name } });
      if (existing) throw new ConflictException('Role name already exists');

      const role = await repo.save(
        repo.create({
          name: dto.name,
          description: dto.description,
          isSystem: false,
        }),
      );

      if (dto.permissionIds && dto.permissionIds.length > 0) {
        await this.setPermissions(role.id, dto.permissionIds);
      }
      return this.getOne(role.id);
    }

    async update(id: string, dto: UpdateRoleDto) {
      const ds = await this.ds();
      const repo = ds.getRepository(Role);
      const role = await repo.findOne({ where: { id } });
      if (!role) throw new NotFoundException('Role not found');
      if (role.isSystem && dto.name && dto.name !== role.name) {
        throw new BadRequestException('Cannot rename a system role');
      }
      if (dto.name !== undefined) role.name = dto.name;
      if (dto.description !== undefined) role.description = dto.description;
      await repo.save(role);

      if (dto.permissionIds) {
        await this.setPermissions(id, dto.permissionIds);
      }
      return this.getOne(id);
    }

    async remove(id: string) {
      const ds = await this.ds();
      const role = await ds.getRepository(Role).findOne({ where: { id } });
      if (!role) throw new NotFoundException('Role not found');
      if (role.isSystem) {
        throw new BadRequestException('Cannot delete a system role');
      }
      await ds.getRepository(Role).delete(id);
      return { ok: true };
    }

    private async setPermissions(roleId: string, permissionIds: string[]) {
      const ds = await this.ds();
      const rpRepo = ds.getRepository(RolePermission);
      await rpRepo.delete({ roleId });
      if (permissionIds.length > 0) {
        await rpRepo.save(
          permissionIds.map((permissionId) =>
            rpRepo.create({ roleId, permissionId }),
          ),
        );
      }
    }
  }
  