/**
 * Filtro por rubro (unidad de negocio). Ver docs/plan-rubros-multi-sucursal.md.
 *
 * Una empresa puede operar sucursales de rubros distintos (ej. una despensa y
 * una ferretería). El maestro de proveedores y el catálogo son de la empresa,
 * así que sin este filtro cada sucursal ve los datos de la otra.
 *
 * Regla única, aplicada igual en los tres lugares donde se usa: **sin rubro
 * asignado = visible en todos los rubros**. Es el mismo criterio de
 * `oferta_sucursales`, y es lo que hace que el despliegue sea inocuo: mientras
 * nadie etiquete nada, todo se sigue viendo como antes.
 *
 * Ojo: los `where...PorRubro` sólo arman el filtro. Qué rubros puede ver cada
 * usuario lo resuelven `resolverRubrosPermitidos` + `rubrosAConsultar` (vía
 * `rubrosFiltroUsuario` en utils/alcance-rubro.ts): los listados de productos y
 * proveedores, el reporte de productos y `/auth/me` lo aplican. Pasar el
 * `rubro_id` crudo sin validarlo deja ver otros rubros.
 *
 * Los dos `where...PorRubro` devuelven un fragmento bajo `AND` para poder
 * spreadearse sobre un `where` que ya use `OR`, sin pisarlo.
 */

/**
 * Fragmento de `where` de Prisma para filtrar proveedores por rubro.
 *
 * Va envuelto en `AND` a propósito: los `where` donde se inserta ya usan `OR`
 * en el nivel de arriba para la búsqueda por texto (razón social / RUC), así
 * que devolver otro `OR` lo pisaría al hacer spread y rompería la búsqueda.
 */
export function whereProveedorPorRubro(rubro?: string | string[] | null) {
  if (Array.isArray(rubro)) {
    // Lista vacía = pidió un rubro que no tiene permitido: no devuelve nada.
    if (rubro.length === 0) return { AND: [{ id: { in: [] as string[] } }] };
    if (rubro.length === 1) return whereProveedorPorRubro(rubro[0]);
    return {
      AND: [
        { OR: [{ proveedor_rubros: { none: {} } }, { proveedor_rubros: { some: { rubro_id: { in: rubro } } } }] },
      ],
    };
  }
  if (!rubro) return {};
  return {
    AND: [
      { OR: [{ proveedor_rubros: { none: {} } }, { proveedor_rubros: { some: { rubro_id: rubro } } }] },
    ],
  };
}

/**
 * Fragmento de `where` de Prisma para filtrar categorías por rubro: las del
 * rubro más las compartidas (sin rubro). Es lo que ven los selectores de
 * categoría (stock, productos, ofertas, inventario físico, POS); el árbol del
 * ABM no lo usa porque tiene que conservar al padre de otro rubro cuando una
 * hija entra.
 */
export function whereCategoriaPorRubro(rubro?: string | string[] | null) {
  if (Array.isArray(rubro)) {
    if (rubro.length === 0) return { AND: [{ id: { in: [] as string[] } }] };
    if (rubro.length === 1) return whereCategoriaPorRubro(rubro[0]);
    return { AND: [{ OR: [{ rubro_id: null }, { rubro_id: { in: rubro } }] }] };
  }
  if (!rubro) return {};
  return { AND: [{ OR: [{ rubro_id: null }, { rubro_id: rubro }] }] };
}

/**
 * Fragmento de `where` de Prisma para filtrar productos por rubro.
 *
 * Normalmente el producto hereda el rubro de su categoría: así se etiqueta una
 * decena de categorías en vez de cada producto, y lo que se dé de alta después
 * queda clasificado solo. Los compartidos entre rubros (bolsas, limpieza,
 * pseudo-productos como DESCUENTO OTORGADO) viven en categorías sin rubro.
 *
 * `productos.rubro_id` es el escape para los catálogos migrados sin categorizar
 * —agogo tiene 11.799 productos y ninguna categoría—, donde la herencia no
 * separa nada. Cuando está seteado, gana sobre la categoría.
 */
export function whereProductoPorRubro(rubro?: string | string[] | null) {
  if (Array.isArray(rubro)) {
    // Lista vacía = el usuario pidió un rubro que no tiene permitido: no se
    // devuelve nada, ni siquiera los compartidos.
    if (rubro.length === 0) return { AND: [{ id: { in: [] as string[] } }] };
    // Un solo rubro: misma forma que el caso simple.
    if (rubro.length === 1) return whereProductoPorRubro(rubro[0]);
    const enRubros = { in: rubro };
    return {
      AND: [
        {
          OR: [
            { rubro_id: enRubros },
            {
              rubro_id: null,
              OR: [
                { categoria_id: null },
                { categoria: { rubro_id: null } },
                { categoria: { rubro_id: enRubros } },
              ],
            },
          ],
        },
      ],
    };
  }
  if (!rubro) return {};
  return {
    AND: [
      {
        OR: [
          { rubro_id: rubro },
          {
            rubro_id: null,
            OR: [
              { categoria_id: null },
              { categoria: { rubro_id: null } },
              { categoria: { rubro_id: rubro } },
            ],
          },
        ],
      },
    ],
  };
}

/**
 * Rubros que el usuario tiene permitido ver, derivados del mismo alcance que
 * `alcanceSucursalUsuario` (utils/alcance-sucursal.ts).
 *
 * `null` = sin restricción: puede elegir cualquier rubro o "Todos los rubros".
 * Lo devuelven:
 *  - un usuario con acceso elevado (superadmin / holding / reseller);
 *  - uno sin sucursales asignadas (misma regla que el alcance por sucursal:
 *    estrenar la restricción no puede dejar a nadie sin ver nada);
 *  - uno asignado a alguna sucursal SIN rubro configurado: esa sucursal vende
 *    de todo (regla del vacío), así que recortarlo sería inventar un límite.
 *
 * Si no, la lista deduplicada de rubros de sus sucursales.
 */
export function resolverRubrosPermitidos(
  sucursales: Array<{ sucursal_rubros?: Array<{ rubro_id: string }> | null } | null>,
  accesoElevado: boolean,
): string[] | null {
  if (accesoElevado) return null;
  const rubrosPorSucursal = (sucursales ?? [])
    .filter((s) => s !== null)
    .map((s) => (s?.sucursal_rubros ?? []).map((sr) => sr.rubro_id));
  if (rubrosPorSucursal.length === 0) return null;
  if (rubrosPorSucursal.some((ids) => ids.length === 0)) return null;
  return [...new Set(rubrosPorSucursal.flat())];
}

/**
 * Cruza los rubros permitidos con el que pidió la pantalla.
 *
 *   null  → sin restricción y sin rubro pedido: no se filtra
 *   [id]  → el rubro pedido (ya validado)
 *   [...] → sin rubro pedido pero con restricción: todos los permitidos
 *   []    → pidió uno que no tiene permitido: el resultado sale vacío
 *
 * Mismo criterio que `sucursalesAConsultar`: un rubro guardado viejo no tira
 * 403, pero tampoco muestra nada que no le corresponda.
 */
export function rubrosAConsultar(permitidos: string[] | null, rubroPedido?: string | null): string[] | null {
  if (permitidos === null) return rubroPedido ? [rubroPedido] : null;
  if (!rubroPedido) return permitidos;
  return permitidos.includes(rubroPedido) ? [rubroPedido] : [];
}

/**
 * Rubro con el que arranca el usuario, derivado de sus sucursales asignadas.
 *
 * Devuelve null —"todos los rubros"— cuando no hay una respuesta única: sin
 * asignaciones, o con asignaciones que cubren más de un rubro. Preferimos que
 * el usuario vea de más y ajuste con el selector antes que encerrarlo por
 * accidente en un rubro que no es el suyo.
 */
export function resolverRubroDefault(
  sucursales: Array<{ sucursal_rubros?: Array<{ rubro_id: string }> | null }>,
): string | null {
  const rubros = new Set(
    (sucursales ?? []).flatMap((s) => (s.sucursal_rubros ?? []).map((sr) => sr.rubro_id)),
  );
  return rubros.size === 1 ? [...rubros][0] : null;
}
