import { Injectable } from '@nestjs/common';
import type {
  ObjectCategory,
  PermissionEntry,
  PermissionRight,
} from '@siop/shared';
import { PrismaService } from '../prisma/prisma.service';

const CACHE_TTL_MS = 60_000; // invariant R0 : la matrice est relue au plus toutes les 60 s

interface CacheEntry {
  expiresAt: number;
  permissions: PermissionEntry[];
}

@Injectable()
export class PermissionsService {
  private readonly cache = new Map<string, CacheEntry>();

  constructor(private readonly prisma: PrismaService) {}

  async getForRole(roleId: string): Promise<PermissionEntry[]> {
    const cached = this.cache.get(roleId);
    if (cached && cached.expiresAt > Date.now()) return cached.permissions;

    const rows = await this.prisma.permission.findMany({ where: { roleId } });
    const permissions = rows.map((r) => ({
      objectCategory: r.objectCategory as ObjectCategory,
      canView: r.canView,
      canViewOther: r.canViewOther,
      canCreate: r.canCreate,
      canEdit: r.canEdit,
      canDelete: r.canDelete,
    }));
    this.cache.set(roleId, { expiresAt: Date.now() + CACHE_TTL_MS, permissions });
    return permissions;
  }

  async can(
    roleId: string,
    category: ObjectCategory,
    right: PermissionRight,
  ): Promise<boolean> {
    const permissions = await this.getForRole(roleId);
    const entry = permissions.find((p) => p.objectCategory === category);
    if (!entry) return false;
    switch (right) {
      case 'view':
        return entry.canView;
      case 'viewOther':
        return entry.canViewOther;
      case 'create':
        return entry.canCreate;
      case 'edit':
        return entry.canEdit;
      case 'delete':
        return entry.canDelete;
    }
  }

  /** À appeler après toute modification de la matrice (admin, R1+). */
  invalidate(roleId?: string): void {
    if (roleId) this.cache.delete(roleId);
    else this.cache.clear();
  }
}
