import { readFileSync } from 'fs';
import { join } from 'path';
import { CATALOGO_VISTAS, vistasDelUsuario } from './catalogo-vistas';

/** Definición de cada vista en los .sql: nombre → texto desde CREATE hasta el ; final. */
function vistasSql(): Map<string, string> {
  const sql = ['aplicar.sql', 'negocio.sql']
    .map((f) => readFileSync(join(__dirname, '../../db/vistas', f), 'utf8'))
    .join('\n');
  const mapa = new Map<string, string>();
  const re = /CREATE OR REPLACE VIEW soporte_ro\.(v_[a-z_]+)[\s\S]*?;\s*$/gm;
  for (const m of sql.matchAll(re)) mapa.set(m[1], m[0]);
  return mapa;
}

/** Privilegios del filtro de filas: los de puede()/puede_alguno() después del último WHERE. */
function privilegiosDelFiltro(definicion: string): Set<string> {
  // UNION ALL (v_documentos): cada rama tiene su WHERE.
  const ramas = definicion.split(/\bUNION ALL\b/);
  const codigos = new Set<string>();
  for (const rama of ramas) {
    const where = rama.slice(rama.lastIndexOf('\nWHERE '));
    for (const m of where.matchAll(/puede(?:_alguno)?\(([^)]*)\)/g)) {
      for (const c of m[1].matchAll(/'([A-Z_]+)'/g)) codigos.add(c[1]);
    }
  }
  return codigos;
}

describe('catálogo de vistas', () => {
  const definiciones = vistasSql();

  it('cada vista del catálogo existe en los .sql', () => {
    const faltan = CATALOGO_VISTAS.map((v) => v.vista).filter((v) => !definiciones.has(v));
    expect(faltan).toEqual([]);
  });

  it('cada vista de negocio.sql está en el catálogo', () => {
    const negocio = readFileSync(join(__dirname, '../../db/vistas/negocio.sql'), 'utf8');
    const enSql = [...negocio.matchAll(/CREATE OR REPLACE VIEW soporte_ro\.(v_[a-z_]+)/g)].map((m) => m[1]);
    const enCatalogo = new Set(CATALOGO_VISTAS.map((v) => v.vista));
    expect(enSql.filter((v) => !enCatalogo.has(v))).toEqual([]);
  });

  it.each(CATALOGO_VISTAS.map((v) => [v.vista, v] as const))(
    'los privilegios de %s son los del filtro de la vista',
    (nombre, v) => {
      const delFiltro = privilegiosDelFiltro(definiciones.get(nombre) ?? '');
      const declarados = new Set([...v.privilegios, ...(v.opcionales ?? [])]);
      // v_alertas_abiertas filtra por el privilegio que trae cada alerta, no por uno fijo.
      expect([...delFiltro].filter((p) => !declarados.has(p))).toEqual([]);
      expect(v.privilegios.filter((p) => !delFiltro.has(p))).toEqual([]);
    },
  );

  it('separa las vistas permitidas de las que no', () => {
    const { permitidas, sinPermiso } = vistasDelUsuario(new Set(['VEN_FAC_FACTURA_VER']));
    const nombres = permitidas.map((v) => v.vista);
    expect(nombres).toEqual(expect.arrayContaining(['v_ventas', 'v_ventas_lineas', 'v_empresa', 'v_documentos']));
    expect(nombres).not.toContain('v_stock');
    expect(sinPermiso.map((v) => v.vista)).toContain('v_stock');
  });
});
