/* ============================================================
 * Copyright (c) 2026 RAVAPI Soluções. www.ravapi.com
 * Identidade por sessão — a ponte entre o token e o IdentityProvider
 *
 * Mesmo padrão de src/db/client.ts (comContexto/tenantAtual): um
 * AsyncLocalStorage carrega o usuário autenticado através da requisição
 * inteira, sem precisar de provider com escopo de requisição no Nest.
 *
 * ATENÇÃO: camada de ligação, ainda não coberta por teste automatizado
 * — a lógica que importa (verificarSessao, AuthService.login) está.
 * ============================================================ */

import { AsyncLocalStorage } from 'node:async_hooks';
import { UnauthorizedException } from '@nestjs/common';
import { verificarSessao } from '../core/sessao.ts';
import type { IdentityProvider, UsuarioRef } from '../core/identity.ts';

const armazenamento = new AsyncLocalStorage<UsuarioRef>();

export function comUsuario<T>(usuario: UsuarioRef, fn: () => T): T {
  return armazenamento.run(usuario, fn);
}

/** Lê o usuário autenticado da requisição atual — ver sessaoMiddleware abaixo. */
export class IdentidadeSessao implements IdentityProvider {
  usuarioAtual(): UsuarioRef {
    const u = armazenamento.getStore();
    if (!u) throw new UnauthorizedException('Sem sessão autenticada.');
    return u;
  }
  async listarMembros(): Promise<UsuarioRef[]> {
    throw new Error('Use UsuariosService.listar() — listarMembros() não se aplica ao login local.');
  }
  administraUsuarios() { return true; }
}

/**
 * Decodifica o Bearer token e, se válido, roda o resto da requisição
 * dentro do AsyncLocalStorage — TenantInterceptor lê req.user.tenantId,
 * IdentidadeSessao lê o resto daqui.
 */
export function sessaoMiddleware(segredo: string) {
  return (req: any, _res: any, next: (erro?: unknown) => void) => {
    const auth = req.headers.authorization;
    if (!auth?.startsWith('Bearer ')) return next();

    try {
      const claims = verificarSessao(auth.slice(7), segredo);
      req.user = { tenantId: claims.tenant_id };
      const usuario: UsuarioRef = {
        id: claims.sub, nome: claims.nome, email: claims.email,
        papel: claims.papel as UsuarioRef['papel'], seriesPermitidas: claims.seriesPermitidas ?? [],
      };
      comUsuario(usuario, next);
    } catch {
      // token ruim: segue sem req.user — TenantInterceptor barra com 401
      // mais abaixo, com uma mensagem própria (não é este middleware que erra)
      next();
    }
  };
}
