import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ClsService } from 'nestjs-cls';
import {
  RolePermissionEntity,
  AccessLevel,
  ModuleName,
} from '../../entities/role-permission.entity';
import { CLS_USER_ROLE_ID, CLS_TENANT_ID } from '../cls/cls.constants';

/**
 * Service-layer permission check.
 *
 * The standard PermissionGuard handles "can this endpoint run at all"
 * (binary allow / deny via @Permissions decorator). It can't answer
 * mid-flight questions like "should I include floor columns in the
 * Excel I'm building?" — that's a per-feature toggle inside a
 * controller method that's already passed the guard.
 *
 * This service exposes a small `hasPermission(module, level)` helper
 * for those cases. Reads the caller's role from CLS (set by
 * JwtStrategy) and looks up the active permission rows. No Redis
 * cache here — kept simple; the call site is rare (file build/import).
 *
 * The numeric ordering of AccessLevel matters: any level >= the
 * required level grants. NONE = 0, VIEW = 1, CREATE = 2, EDIT = 3, DELETE = 4.
 */
const LEVEL_RANK: Record<AccessLevel, number> = {
  [AccessLevel.NONE]: 0,
  [AccessLevel.VIEW]: 1,
  [AccessLevel.CREATE]: 2,
  [AccessLevel.EDIT]: 3,
  [AccessLevel.DELETE]: 4,
};

@Injectable()
export class PermissionContextService {
  constructor(
    @InjectRepository(RolePermissionEntity)
    private readonly rolePermRepo: Repository<RolePermissionEntity>,
    private readonly cls: ClsService,
  ) {}

  async hasPermission(module: ModuleName, requiredLevel: AccessLevel): Promise<boolean> {
    const roleId = this.cls.get<string | null>(CLS_USER_ROLE_ID);
    const tenantId = this.cls.get<string | null>(CLS_TENANT_ID);
    if (!roleId || !tenantId) return false;

    // NOTE: TypeORM 1.0 mis-generates SQL when a partial `select` is
    // combined with a relation filter (`role: { id }`) — the inner
    // DISTINCT subquery drops the id column and Postgres throws
    // `column "RolePermissionEntity_id" does not exist`. Drop the
    // partial select; the row is tiny so over-fetching is fine.
    const row = await this.rolePermRepo.findOne({
      where: {
        role: { id: roleId },
        tenant_id: tenantId,
        module,
        is_deleted: false,
      },
    });
    if (!row) return false;

    return LEVEL_RANK[row.access_level] >= LEVEL_RANK[requiredLevel];
  }
}
