/* ============================================================
 * Copyright (c) 2026 RAVAPI Soluções. www.ravapi.com
 * Identidade e permissões
 *
 * O módulo não define usuários por conta própria — recebe um
 * IdentityProvider. No modo SaaS, a implementação é local (tabela
 * recibos.users). No modo embarcado, o sistema de freelancers mapeia
 * seus próprios perfis para estes quatro papéis, e o cliente
 * administra usuários num lugar só.
 * ============================================================ */

import { SemPermissao } from './errors.ts';

export type Papel = 'PROPRIETARIO' | 'SUPERVISOR' | 'EMISSOR' | 'AUDITOR';

export interface UsuarioRef {
  id: string;
  nome: string;
  email: string;
  papel: Papel;
  /** ids das séries em que pode emitir. Vazio = nenhuma. */
  seriesPermitidas: string[];
}

export type Acao =
  | 'emitir' | 'cancelar' | 'ler' | 'exportar'
  | 'gerir_cadastros' | 'gerir_usuarios' | 'gerir_series' | 'ver_licenca';

/**
 * Segregação de funções. Quem emite não cancela: cancelar é a única
 * operação de escrita que existe sobre documento selado, e concentrar as
 * duas na mesma pessoa é o que o contador do cliente reclama primeiro.
 */
const MATRIZ: Record<Papel, Acao[]> = {
  PROPRIETARIO: ['emitir','cancelar','ler','exportar','gerir_cadastros','gerir_usuarios','gerir_series','ver_licenca'],
  SUPERVISOR:   ['emitir','cancelar','ler','exportar','gerir_cadastros'],
  EMISSOR:      ['emitir','ler','gerir_cadastros'],
  AUDITOR:      ['ler','exportar'],
};

export function podeFazer(papel: Papel, acao: Acao): boolean {
  return MATRIZ[papel]?.includes(acao) ?? false;
}

/**
 * Permissão de emissão é por série, não geral: quem lança entrada de
 * dinheiro não precisa ser quem lança saída.
 * Proprietário emite em qualquer série — é quem cria as séries.
 */
export function podeEmitirNaSerie(u: UsuarioRef, seriesId: string): boolean {
  if (!podeFazer(u.papel, 'emitir')) return false;
  if (u.papel === 'PROPRIETARIO') return true;
  return u.seriesPermitidas.includes(seriesId);
}

export function exigir(u: UsuarioRef, acao: Acao): void {
  if (!podeFazer(u.papel, acao)) {
    throw new SemPermissao(acao, `O papel ${u.papel} não pode ${acao.replace(/_/g, ' ')}.`);
  }
}

export function exigirSerie(u: UsuarioRef, seriesId: string): void {
  if (!podeEmitirNaSerie(u, seriesId)) {
    throw new SemPermissao('emitir', `${u.nome} não tem permissão para emitir nesta série.`);
  }
}

export interface IdentityProvider {
  usuarioAtual(): UsuarioRef;
  listarMembros(): Promise<UsuarioRef[]>;
  /** true quando o módulo administra usuários (modo SaaS). */
  administraUsuarios(): boolean;
}
