import type { PrismaService } from '../../prisma/prisma.service';
import { DimensionesDocumento } from './reglas-mapeo';

/**
 * Dimensiones de cabecera que publica cada familia de documento para las reglas
 * de cuenta (spec 2026-10-13 "reglas de cuenta por dimensión", §3.2 y §5.1).
 * Cada documento publica solo lo que tiene: un dato vacío no se publica y una
 * regla que lo usa no aplica. La tabla de qué publica cada origen es
 * `DIMENSIONES_POR_ORIGEN` (reglas-mapeo.ts).
 */

type Prisma = Pick<PrismaService, 'clientes' | 'proveedores' | 'empresas_sucursales'>;

export const dimensionMoneda = (codigo?: string | null): DimensionesDocumento => ({ MONEDA: codigo ?? 'PYG' });

/** Quita las dimensiones vacías: el documento no las tiene. */
const soloConValor = (d: DimensionesDocumento): DimensionesDocumento =>
  Object.fromEntries(Object.entries(d).filter(([, v]) => v !== undefined && v !== null && v !== '')) as DimensionesDocumento;

// `clientes` no tiene empresa: el cliente es el del documento, que ya es de la empresa.
async function tipoCliente(prisma: Prisma, clienteId?: string | null): Promise<string | null> {
  if (!clienteId) return null;
  const c = await prisma.clientes.findUnique({ where: { id: clienteId }, select: { tipo_cliente: true } });
  return c?.tipo_cliente ?? null;
}

async function tipoProveedor(prisma: Prisma, empresaId: string, proveedorId?: string | null): Promise<string | null> {
  if (!proveedorId) return null;
  const p = await prisma.proveedores.findFirst({ where: { id: proveedorId, empresa_id: empresaId }, select: { tipo_entidad: true } });
  return p?.tipo_entidad ?? null;
}

/**
 * Factura y NC de venta. La sucursal es la que tiene como `punto_establecimiento`
 * el `dest` del documento. CANAL solo si el documento trae formas de pago (la NC
 * no tiene): POS si alguna salió de una sesión de caja, si no FACTURA.
 */
export async function dimensionesVenta(
  prisma: Prisma,
  empresaId: string,
  f: {
    dest: string | null;
    cliente_id: string | null;
    moneda?: { codigo: string } | null;
    factura_forma_pagos?: Array<{ sesion_caja_id?: string | null }>;
  },
): Promise<DimensionesDocumento> {
  const [sucursal, tipo] = await Promise.all([
    f.dest
      ? prisma.empresas_sucursales.findFirst({
          where: { empresa_id: empresaId, punto_establecimiento: f.dest },
          // Si dos sucursales comparten establecimiento, gana la activa (y la por defecto).
          orderBy: [{ active: { sort: 'desc', nulls: 'last' } }, { es_default: 'desc' }, { id: 'asc' }],
          select: { id: true },
        })
      : null,
    tipoCliente(prisma, f.cliente_id),
  ]);
  return soloConValor({
    SUCURSAL: sucursal?.id ?? null,
    ...dimensionMoneda(f.moneda?.codigo),
    TIPO_CLIENTE: tipo,
    CANAL: f.factura_forma_pagos ? (f.factura_forma_pagos.some((fp) => fp.sesion_caja_id) ? 'POS' : 'FACTURA') : null,
  });
}

/** Documentos de proveedor (compra, NC de compra, gasto, despacho): sucursal propia, moneda y tipo de proveedor. */
export async function dimensionesProveedor(
  prisma: Prisma,
  empresaId: string,
  d: { sucursal_id?: string | null; proveedor_id?: string | null; moneda?: { codigo: string } | null },
): Promise<DimensionesDocumento> {
  return soloConValor({
    SUCURSAL: d.sucursal_id ?? null,
    ...dimensionMoneda(d.moneda?.codigo),
    TIPO_PROVEEDOR: await tipoProveedor(prisma, empresaId, d.proveedor_id),
  });
}

/** Documentos de cliente sin `dest` (recibos): sucursal propia, moneda y tipo de cliente. */
export async function dimensionesCliente(
  prisma: Prisma,
  empresaId: string,
  d: { sucursal_id?: string | null; cliente_id?: string | null; moneda?: { codigo: string } | null },
): Promise<DimensionesDocumento> {
  return soloConValor({
    SUCURSAL: d.sucursal_id ?? null,
    ...dimensionMoneda(d.moneda?.codigo),
    TIPO_CLIENTE: await tipoCliente(prisma, d.cliente_id),
  });
}
