/**
 * Cálculo fiscal de ítems y subtotales de factura según la NT 13 de SIFEN, en el
 * backend. Es el mismo cálculo que hace el frontend (`utils/invoiceCalculations.js`)
 * para que los servicios que emiten facturas solos (vertical inmobiliario, y a
 * futuro cualquier otro) no tengan que rearmar las fórmulas a mano.
 *
 * Precio con IVA incluido:
 *   base gravada  = 100 · total · prop / (10000 + tasa · prop)
 *   IVA           = base gravada · tasa / 100
 *   base exenta   = 100 · total · (100 − prop) / (10000 + tasa · prop)   (sólo afectación 4)
 */
export enum AfectacionIva {
  GRAVADO = 1,
  EXONERADO = 2,
  EXENTO = 3,
  GRAVADO_PARCIAL = 4,
}

export interface LineaFiscalInput {
  cantidad: number;
  precio_unitario: number; // IVA incluido
  afectacion_iva: AfectacionIva;
  porcentaje_iva: number; // 0, 5 o 10
  proporcion_iva?: number; // sólo afectación 4 (ej: 30 para inmuebles)
}

export interface LineaFiscal {
  cantidad: number;
  precio_unitario: number;
  total_bruto: number;
  descuento: number;
  porcentaje_descuento: number;
  descuento_global: number;
  total_operacion: number;
  afectacion_iva: AfectacionIva;
  porcentaje_iva: number;
  proporcion_iva: number;
  base_gravada: number;
  liquidacion_iva: number;
  base_gravada_exenta: number;
}

const r = (n: number, dec: number) => Math.round(n * 10 ** dec) / 10 ** dec;

export function calcularLineaFiscal(input: LineaFiscalInput): LineaFiscal {
  const cantidad = Number(input.cantidad) || 0;
  const precio = Number(input.precio_unitario) || 0;
  const total = Math.max(precio * cantidad, 0);
  const afectacion = input.afectacion_iva;

  let tasa = Number(input.porcentaje_iva) || 0;
  let prop = 100;
  if (afectacion === AfectacionIva.EXENTO || afectacion === AfectacionIva.EXONERADO) {
    tasa = 0;
    prop = 0;
  } else if (afectacion === AfectacionIva.GRAVADO_PARCIAL) {
    prop = Number(input.proporcion_iva) || 0;
    if (prop <= 0 || prop >= 100) {
      throw new Error('La afectación gravado parcial necesita una proporción entre 1 y 99');
    }
  }
  if (afectacion !== AfectacionIva.EXENTO && afectacion !== AfectacionIva.EXONERADO && tasa !== 5 && tasa !== 10) {
    throw new Error(`Tasa de IVA inválida para un ítem gravado: ${tasa}`);
  }

  let base = 0;
  let iva = 0;
  let exenta = 0;
  if (afectacion === AfectacionIva.GRAVADO || afectacion === AfectacionIva.GRAVADO_PARCIAL) {
    const divisor = 10000 + tasa * prop;
    base = (100 * total * prop) / divisor;
    iva = (base * tasa) / 100;
    if (afectacion === AfectacionIva.GRAVADO_PARCIAL) exenta = (100 * total * (100 - prop)) / divisor;
  }

  return {
    cantidad,
    precio_unitario: precio,
    total_bruto: total,
    descuento: 0,
    porcentaje_descuento: 0,
    descuento_global: 0,
    total_operacion: total,
    afectacion_iva: afectacion,
    porcentaje_iva: tasa,
    proporcion_iva: prop,
    base_gravada: r(base, 8),
    liquidacion_iva: r(iva, 8),
    base_gravada_exenta: r(exenta, 8),
  };
}

/** Subtotales con los nombres de `SubtotalDto`, igual que `calcularSubtotales` del frontend. */
export function calcularSubtotalFiscal(lineas: LineaFiscal[], dticam?: number | null) {
  const acc = {
    exento: 0,
    exonerado: 0,
    sub5: 0,
    sub10: 0,
    iva5: 0,
    iva10: 0,
    base5: 0,
    base10: 0,
    total: 0,
  };
  for (const l of lineas) {
    acc.total += l.total_operacion;
    if (l.afectacion_iva === AfectacionIva.EXENTO) acc.exento += l.total_operacion;
    else if (l.afectacion_iva === AfectacionIva.EXONERADO) acc.exonerado += l.total_operacion;
    else {
      // En el parcial, la parte no gravada va al subtotal exento y el subtotal de la
      // tasa es base + IVA; en el gravado total eso es igual al total de la línea.
      const gravado =
        l.afectacion_iva === AfectacionIva.GRAVADO ? l.total_operacion : l.base_gravada + l.liquidacion_iva;
      if (l.afectacion_iva === AfectacionIva.GRAVADO_PARCIAL) acc.exento += l.base_gravada_exenta;
      if (l.porcentaje_iva === 5) {
        acc.sub5 += gravado;
        acc.iva5 += l.liquidacion_iva;
        acc.base5 += l.base_gravada;
      } else {
        acc.sub10 += gravado;
        acc.iva10 += l.liquidacion_iva;
        acc.base10 += l.base_gravada;
      }
    }
  }
  const tc = Number(dticam) || 0;
  return {
    subtotal_exento: r(acc.exento, 2),
    subtotal_exonerado: r(acc.exonerado, 2),
    subtotal_5: r(acc.sub5, 2),
    subtotal_10: r(acc.sub10, 2),
    liquidacion_iva_5: r(acc.iva5, 2),
    liquidacion_iva_10: r(acc.iva10, 2),
    liquidacion_redondeo_5: 0,
    liquidacion_redondeo_10: 0,
    liquidacion_iva_comision: 0,
    total_iva: r(acc.iva5 + acc.iva10, 2),
    total_operacion: r(acc.total, 2),
    total_descuento: 0,
    total_descuento_global_item: 0,
    total_descuento_particular_item: 0,
    total_bruto_operacion: r(acc.total, 2),
    total_anticipo_item: 0,
    total_anticipo_global_item: 0,
    porcentaje_descuento_total: 0,
    total_anticipo_operacion: 0,
    total_redondeo: 0,
    total_comision: 0,
    total_general_operacion: r(acc.total, 2),
    total_base_gravada_5: r(acc.base5, 2),
    total_base_gravada_10: r(acc.base10, 2),
    total_base_gravada: r(acc.base5 + acc.base10, 2),
    // En moneda extranjera SIFEN pide el total en guaraníes
    total_gs: tc > 0 ? Math.round(acc.total * tc) : r(acc.total, 2),
  };
}
