/**
 * ╔══════════════════════════════════════════════════════════════════════════╗
 * ║  IMPORTACIÓN: SERVICIOS.xlsx → productos (tipo "servicio")               ║
 * ╠══════════════════════════════════════════════════════════════════════════╣
 * ║  Uso:                                                                    ║
 * ║    npx ts-node scripts/data_script/cliente-naviera/import-servicios-naviera.ts \
 * ║      --ruc=80100092 --dry                                                ║
 * ║    npx ts-node scripts/data_script/cliente-naviera/import-servicios-naviera.ts \
 * ║      --ruc=80100092                                                      ║
 * ║                                                                          ║
 * ║  Flags:                                                                  ║
 * ║    --ruc=<8 dígitos>   RUC sin DV de la empresa destino   (obligatorio)  ║
 * ║    --file=<ruta>       Excel (default: SERVICIOS.xlsx de esta carpeta)   ║
 * ║    --hoja=<nombre>     Hoja a leer (default: la primera)                 ║
 * ║    --iva=<0|5|10>      Porcentaje de IVA (default 10)                    ║
 * ║    --prefijo=<XXX>     Prefijo del cod_producto (default SRV)            ║
 * ║    --sin-categoria     No crea ni asigna la categoría "SERVICIOS"        ║
 * ║    --dry               Simula: no escribe nada en la BD                  ║
 * ╠══════════════════════════════════════════════════════════════════════════╣
 * ║  La planilla trae UNA sola columna: DESCRIPCIÓN. Todo lo demás son       ║
 * ║  defaults acordados:                                                     ║
 * ║                                                                          ║
 * ║    tipo               = "servicio"  (así lo escribe el resto del ERP)    ║
 * ║    afectación         = Gravado IVA                                      ║
 * ║    porcentaje_iva     = 10                                               ║
 * ║    proporcion_iva     = 100                                              ║
 * ║    unidad de medida   = UNI (Unidad)                                     ║
 * ║    maneja_inventario  = false                                            ║
 * ║    precio             = 0                                                ║
 * ║                                                                          ║
 * ║  `maneja_inventario` en false es deliberado: facturas/pedidos/remisión   ║
 * ║  descuentan stock y piden depósito según ese flag (facturas.service.ts   ║
 * ║  ~252, ~907). Un servicio con el flag en true intentaría mover stock de  ║
 * ║  algo que no existe.                                                     ║
 * ║                                                                          ║
 * ║  El precio queda en 0: la planilla no lo trae y no se inventa.           ║
 * ╚══════════════════════════════════════════════════════════════════════════╝
 */

import * as path from 'path';
import * as fs from 'fs';
import * as XLSX from 'xlsx';
import { PrismaClient } from '@prisma/client';

// ══════════════════════════════════════════════════════════════════════════
//  ARGUMENTOS
// ══════════════════════════════════════════════════════════════════════════
const args = process.argv.slice(2);
const flag = (nombre: string): string | undefined => {
  const hit = args.find((a) => a.startsWith(`--${nombre}=`));
  return hit ? hit.slice(nombre.length + 3) : undefined;
};

const RUC_EMPRESA = flag('ruc');
const HOJA = flag('hoja');
const DRY = args.includes('--dry');
const SIN_CATEGORIA = args.includes('--sin-categoria');
const PREFIJO = (flag('prefijo') ?? 'SRV').toUpperCase();
const IVA = Number(flag('iva') ?? 10);
const XLSX_FILE = path.resolve(flag('file') ?? path.join(__dirname, 'SERVICIOS.xlsx'));

// ══════════════════════════════════════════════════════════════════════════
//  DEFAULTS DEL SERVICIO
// ══════════════════════════════════════════════════════════════════════════
const CAT = {
  AFECTACION_GRAVADO_IVA: '7b634413-a746-4152-9941-4d586ed75825', // cód 1
  UNIDAD_UNI: '4f6a1318-1a42-454d-874e-a72f6cda04df', // cód 77 — Unidad
};
const NOMBRE_CATEGORIA = 'SERVICIOS';
const PROPORCION_IVA = 100;
const TIPO = 'servicio';

// ══════════════════════════════════════════════════════════════════════════
//  LOG
// ══════════════════════════════════════════════════════════════════════════
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const LOG_FILE = path.join(__dirname, `import-servicios-naviera-${stamp}.log`);
const logStream = fs.createWriteStream(LOG_FILE, { flags: 'a' });
const say = (linea: string) => {
  console.log(linea);
  logStream.write(linea + '\n');
};

// ══════════════════════════════════════════════════════════════════════════
//  HELPERS
// ══════════════════════════════════════════════════════════════════════════

/** Clave de comparación: sin acentos, sin espacios dobles, sin punto final. */
const normalizar = (s: string) =>
  s
    .toLowerCase()
    .normalize('NFD')
    .replace(/[̀-ͯ]/g, '')
    .replace(/\s+/g, ' ')
    .replace(/\.+$/, '')
    .trim();

interface Servicio {
  fila: number;
  descripcion: string;
  clave: string;
}

// ══════════════════════════════════════════════════════════════════════════
//  LECTURA
// ══════════════════════════════════════════════════════════════════════════
function leerExcel(ruta: string): { servicios: Servicio[]; descartadas: string[]; hoja: string } {
  const wb = XLSX.readFile(ruta);
  const nombreHoja = HOJA ?? wb.SheetNames[0];
  const hoja = wb.Sheets[nombreHoja];
  if (!hoja) throw new Error(`La hoja "${nombreHoja}" no existe. Hojas: ${wb.SheetNames.join(', ')}`);

  const filas: any[][] = XLSX.utils.sheet_to_json(hoja, { header: 1, defval: '' });
  const servicios: Servicio[] = [];
  const descartadas: string[] = [];

  // La primera columna no vacía es la de descripciones; se saltea el
  // encabezado "DESCRIPCIÓN" y cualquier fila de título.
  filas.forEach((fila, i) => {
    const valor = String(fila.find((c) => String(c ?? '').trim()) ?? '').trim();
    if (!valor) return;

    const nroFila = i + 1;
    if (normalizar(valor) === 'descripcion') return; // encabezado

    if (valor.length < 3) {
      descartadas.push(`fila ${nroFila}: "${valor}" es demasiado corta para ser un servicio`);
      return;
    }
    if (valor.length > 2000) {
      descartadas.push(`fila ${nroFila}: descripción de ${valor.length} caracteres (máximo 2000)`);
      return;
    }

    servicios.push({ fila: nroFila, descripcion: valor, clave: normalizar(valor) });
  });

  return { servicios, descartadas, hoja: nombreHoja };
}

// ══════════════════════════════════════════════════════════════════════════
//  MAIN
// ══════════════════════════════════════════════════════════════════════════
const prisma = new PrismaClient();

async function main() {
  if (!RUC_EMPRESA) throw new Error('Falta --ruc=<8 dígitos>. Ej: --ruc=80100092');
  if (!fs.existsSync(XLSX_FILE)) throw new Error(`No existe el archivo: ${XLSX_FILE}`);
  if (![0, 5, 10].includes(IVA)) throw new Error(`--iva inválido: ${IVA}. Válidos: 0, 5, 10`);

  const { servicios, descartadas, hoja } = leerExcel(XLSX_FILE);

  say('═'.repeat(78));
  say('  IMPORTACIÓN DE SERVICIOS');
  say('═'.repeat(78));
  say(`  Archivo   : ${XLSX_FILE}`);
  say(`  Hoja      : ${hoja}`);
  say(`  RUC       : ${RUC_EMPRESA}`);
  say(`  IVA       : ${IVA}%  ·  proporción ${PROPORCION_IVA}%  ·  Gravado IVA`);
  say(`  Categoría : ${SIN_CATEGORIA ? '(ninguna)' : NOMBRE_CATEGORIA}`);
  say(`  Modo      : ${DRY ? 'DRY-RUN (no escribe)' : 'REAL'}`);
  say(`  Log       : ${LOG_FILE}`);
  say('═'.repeat(78));

  const empresa = await prisma.empresas.findUnique({
    where: { ruc: RUC_EMPRESA },
    select: { id: true, ruc: true, razon_social: true },
  });
  if (!empresa) throw new Error(`No existe empresa con RUC ${RUC_EMPRESA}`);
  say(`\n▸ Empresa: ${empresa.razon_social} (${empresa.ruc})  id=${empresa.id}\n`);

  say(`▸ Excel: ${servicios.length} descripciones`);
  if (descartadas.length) {
    say(`  ${descartadas.length} filas descartadas:`);
    descartadas.forEach((d) => say(`    ✗ ${d}`));
  }

  // ── 1. Duplicados dentro del Excel ────────────────────────────────────
  const porClave = new Map<string, Servicio>();
  const duplicados: string[] = [];
  for (const s of servicios) {
    const previa = porClave.get(s.clave);
    if (previa) {
      duplicados.push(`fila ${s.fila} "${s.descripcion}" repite la fila ${previa.fila}`);
      continue;
    }
    porClave.set(s.clave, s);
  }
  if (duplicados.length) {
    say(`\n  ${duplicados.length} duplicados en el Excel (se toma el primero):`);
    duplicados.forEach((d) => say(`    ~ ${d}`));
  }

  // ── 2. Contrastar con lo que la empresa ya tiene ──────────────────────
  const existentes = await prisma.productos.findMany({
    where: { empresa_id: empresa.id, deleted: false },
    select: { descripcion: true, cod_producto: true },
  });
  const clavesExistentes = new Set(existentes.map((p) => normalizar(p.descripcion)));
  const codigosUsados = new Set(existentes.map((p) => p.cod_producto).filter(Boolean) as string[]);

  const aCrear = [...porClave.values()].filter((s) => !clavesExistentes.has(s.clave));
  const yaEstaban = [...porClave.values()].filter((s) => clavesExistentes.has(s.clave));

  say(`\n  Únicos en el Excel      : ${porClave.size}`);
  say(`  Ya existían en la empresa: ${yaEstaban.length}`);
  say(`  A crear                 : ${aCrear.length}`);
  yaEstaban.forEach((s) => say(`    ~ ya existe: ${s.descripcion}`));

  // ── 3. Categoría ──────────────────────────────────────────────────────
  let categoriaId: string | null = null;
  if (!SIN_CATEGORIA) {
    const existente = await prisma.categorias.findFirst({
      where: { empresa_id: empresa.id, descripcion: NOMBRE_CATEGORIA, deleted_at: false },
      select: { id: true },
    });
    if (existente) {
      categoriaId = existente.id;
      say(`\n▸ Categoría "${NOMBRE_CATEGORIA}" ya existía  id=${categoriaId}`);
    } else if (DRY) {
      say(`\n▸ Categoría "${NOMBRE_CATEGORIA}" se crearía`);
    } else {
      const creada = await prisma.categorias.create({
        data: { empresa_id: empresa.id, descripcion: NOMBRE_CATEGORIA, activo: true, deleted_at: false },
        select: { id: true },
      });
      categoriaId = creada.id;
      say(`\n▸ Categoría "${NOMBRE_CATEGORIA}" creada  id=${categoriaId}`);
    }
  }

  // ── 4. Crear ──────────────────────────────────────────────────────────
  say('\n▸ Creando servicios:');
  let creados = 0;
  let secuencia = 0;
  const errores: string[] = [];

  /** Siguiente código libre con el prefijo, saltando los ya usados. */
  const siguienteCodigo = (): string => {
    let codigo: string;
    do {
      secuencia++;
      codigo = `${PREFIJO}${String(secuencia).padStart(4, '0')}`;
    } while (codigosUsados.has(codigo));
    codigosUsados.add(codigo);
    return codigo;
  };

  for (const s of aCrear) {
    const codigo = siguienteCodigo();
    try {
      if (!DRY) {
        await prisma.productos.create({
          data: {
            empresa_id: empresa.id,
            descripcion: s.descripcion,
            cod_producto: codigo,
            tipo: TIPO,
            afectacion_id: CAT.AFECTACION_GRAVADO_IVA,
            porcentaje_iva: IVA,
            proporcion_iva: PROPORCION_IVA,
            unidad_medida_id: CAT.UNIDAD_UNI,
            categoria_id: categoriaId,
            // Un servicio no mueve stock: ver la nota de cabecera.
            maneja_inventario: false,
            maneja_lote: false,
            // La planilla no trae precio; se carga desde la pantalla.
            precio: 0,
            active: true,
            deleted: false,
          },
        });
      }
      creados++;
      say(`  + ${codigo}  ${s.descripcion}`);
    } catch (e: any) {
      errores.push(`fila ${s.fila} "${s.descripcion}": ${e.message}`);
      say(`  ✗ ${codigo}  ${s.descripcion}  → ${e.message}`);
    }
  }

  // ── 5. Resumen ────────────────────────────────────────────────────────
  say('\n' + '═'.repeat(78));
  say('  RESUMEN');
  say('═'.repeat(78));
  say(`  Descripciones en el Excel : ${servicios.length + descartadas.length}`);
  say(`  Descartadas               : ${descartadas.length}`);
  say(`  Duplicadas en el Excel    : ${duplicados.length}`);
  say(`  Ya existían en la empresa : ${yaEstaban.length}`);
  say(`  Servicios creados         : ${creados}`);
  say(`  Errores                   : ${errores.length}`);
  errores.forEach((e) => say(`    ✗ ${e}`));

  say(`\n  ⚠ Todos quedaron con precio 0: la planilla no trae precios.`);
  say('    Hay que cargarlos desde Productos, o con una lista de precios.');

  if (DRY) say('\n  (DRY-RUN: no se escribió nada en la base de datos)');
  say('');
}

main()
  .catch((e) => {
    say(`\n✖ ${e.message}\n`);
    process.exitCode = 1;
  })
  .finally(async () => {
    logStream.end();
    await prisma.$disconnect();
  });
