/**
 * ╔══════════════════════════════════════════════════════════════╗
 * ║        SCRIPT DE MIGRACIÓN: Productos desde xlsx             ║
 * ╠══════════════════════════════════════════════════════════════╣
 * ║  Lee:  scripts/data_script/productos.xlsx                    ║
 * ║  Columnas esperadas:                                         ║
 * ║    descripcion | costo | precio | codigo_barra | iva         ║
 * ║                                                              ║
 * ║  Hace upsert de productos por codigo_barra (si existe)       ║
 * ║  o por descripcion (si no hay código de barra).              ║
 * ╠══════════════════════════════════════════════════════════════╣
 * ║  Instrucciones:                                              ║
 * ║  1. Ajustar la sección CONFIG si es necesario                ║
 * ║  2. Dry-run:                                                 ║
 * ║     npx ts-node scripts/migrate-access/migrate-productos.ts --dry ║
 * ║  3. Real:                                                    ║
 * ║     npx ts-node scripts/migrate-access/migrate-productos.ts  ║
 * ╚══════════════════════════════════════════════════════════════╝
 */

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

// ══════════════════════════════════════════════════════════════════
//  CONFIG — ajustar según empresa/entorno destino
// ══════════════════════════════════════════════════════════════════
const CONFIG = {
  EMPRESA_ID: 'd09e0188-5b69-425f-944b-4ae9209d5cb9',

  // IDs de catálogos (ajustar si cambian en otra empresa)
  AFECTACION_ID: '7b634413-a746-4152-9941-4d586ed75825', // IVA gravado
  UNIDAD_MEDIDA_ID: '4f6a1318-1a42-454d-874e-a72f6cda04df', // Unidad

  // Valores por defecto cuando el Excel no los provee
  TIPO: 'producto' as const,
  MANEJA_INVENTARIO: true,
  IVA_DEFAULT: 10, // % — se usa si la columna "iva" viene vacía
  PROPORCION_IVA_DEFAULT: 100,

  // Ruta al archivo de datos
  XLSX_PATH: path.resolve(__dirname, '../../scripts/data_script/productos.xlsx'),
};

// ══════════════════════════════════════════════════════════════════
//  LOGGER
// ══════════════════════════════════════════════════════════════════
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const LOG_FILE = path.join(__dirname, `reporte-productos-${timestamp}.log`);
const logStream = fs.createWriteStream(LOG_FILE, { flags: 'a' });

type LogLevel = 'INFO' | 'OK' | 'SKIP' | 'WARN' | 'ERROR' | 'FATAL' | 'RAW';

function log(level: LogLevel, msg: string) {
  const ts = new Date().toISOString().slice(11, 19);
  const prefix: Record<LogLevel, string> = {
    INFO: `[${ts}] INFO  `,
    OK: `[${ts}] ✓     `,
    SKIP: `[${ts}] ~     `,
    WARN: `[${ts}] WARN  `,
    ERROR: `[${ts}] ✗ ERR `,
    FATAL: `[${ts}] ✗✗FATAL`,
    RAW: '',
  };
  const line = prefix[level] + msg;
  console.log(line);
  logStream.write(line + '\n');
}

const info = (m: string) => log('INFO', m);
const ok = (m: string) => log('OK', m);
const skip = (m: string) => log('SKIP', m);
const warn = (m: string) => log('WARN', m);
const err = (m: string) => log('ERROR', m);
const raw = (m: string) => log('RAW', m);

// ══════════════════════════════════════════════════════════════════
//  HELPERS
// ══════════════════════════════════════════════════════════════════
function toStr(val: any): string {
  if (val === null || val === undefined) return '';
  return String(val).trim();
}

function toNum(val: any): number {
  if (val === null || val === undefined) return 0;
  const s = String(val).replace(/\./g, '').replace(',', '.');
  const n = parseFloat(s);
  return isNaN(n) ? 0 : n;
}

// Guaraní no usa decimales
function toPrice(val: any): number {
  return Math.max(0, Math.round(toNum(val)));
}

// ══════════════════════════════════════════════════════════════════
//  MAIN
// ══════════════════════════════════════════════════════════════════
const prisma = new PrismaClient();
const DRY = process.argv.includes('--dry');

async function main() {
  raw('');
  raw('╔══════════════════════════════════════════════════╗');
  raw(`║  MIGRACIÓN DE PRODUCTOS  ${DRY ? '[ DRY RUN ]     ' : '[ REAL — ESCRIBE ]'} ║`);
  raw(`║  Archivo: ${path.basename(CONFIG.XLSX_PATH).padEnd(39)}║`);
  raw(`║  Log:     ${path.basename(LOG_FILE).padEnd(39)}║`);
  raw('╚══════════════════════════════════════════════════╝');
  raw('');

  // ── Leer Excel ────────────────────────────────────────────────
  if (!fs.existsSync(CONFIG.XLSX_PATH)) {
    log('FATAL', `Archivo no encontrado: ${CONFIG.XLSX_PATH}`);
    process.exit(1);
  }

  const wb = XLSX.readFile(CONFIG.XLSX_PATH);
  const ws = wb.Sheets[wb.SheetNames[0]];
  const rows: any[] = XLSX.utils.sheet_to_json(ws, { defval: null });

  info(`Filas leídas del Excel : ${rows.length}`);
  info(`Columnas detectadas    : ${Object.keys(rows[0] ?? {}).join(', ')}`);
  raw('');

  // ── Contadores ────────────────────────────────────────────────
  let creados = 0;
  let actualizados = 0;
  let omitidos = 0;
  const errores: string[] = [];

  // ── Procesar filas ────────────────────────────────────────────
  for (const r of rows) {
    const descripcion = toStr(r['descripcion']);
    const codigoBarra = toStr(r['codigo_barra']);
    const precioCosto = toPrice(r['costo']);
    const precio = toPrice(r['precio']);
    const iva = r['iva'] != null ? Number(r['iva']) : CONFIG.IVA_DEFAULT;

    if (!descripcion) {
      warn(`Fila omitida — sin descripción: ${JSON.stringify(r)}`);
      omitidos++;
      continue;
    }

    const label = codigoBarra ? `[${codigoBarra}] ${descripcion}` : `[sin código] ${descripcion}`;

    if (DRY) {
      info(`[DRY] ${label} | precio: ${precio} | IVA: ${iva}%`);
      creados++;
      continue;
    }

    try {
      // Duplicado si coincide codigo_barra, cod_producto o descripcion dentro de la misma empresa
      const orConditions: any[] = [
        { descripcion: { equals: descripcion, mode: 'insensitive' as const } },
      ];
      if (codigoBarra) {
        orConditions.push({ codigo_barra: codigoBarra });
        orConditions.push({ cod_producto: codigoBarra });
      }

      const existing = await prisma.productos.findFirst({
        where: { empresa_id: CONFIG.EMPRESA_ID, OR: orConditions },
        select: { id: true, descripcion: true, cod_producto: true, codigo_barra: true },
      });

      const data = {
        descripcion,
        codigo_barra: codigoBarra || null,
        precio_costo: precioCosto,
        precio,
        afectacion_id: CONFIG.AFECTACION_ID,
        unidad_medida_id: CONFIG.UNIDAD_MEDIDA_ID,
        porcentaje_iva: iva,
        proporcion_iva: CONFIG.PROPORCION_IVA_DEFAULT,
        tipo: CONFIG.TIPO,
        maneja_inventario: CONFIG.MANEJA_INVENTARIO,
      };

      if (existing) {
        await prisma.productos.update({ where: { id: existing.id }, data });
        skip(`Actualizado | ${label}`);
        actualizados++;
      } else {
        await prisma.productos.create({
          data: { ...data, empresa_id: CONFIG.EMPRESA_ID },
        });
        ok(`Creado      | ${label}`);
        creados++;
      }
    } catch (e: any) {
      const msg = `${label} | ${e.message}`;
      errores.push(msg);
      err(msg);
    }
  }

  // ── Resumen ───────────────────────────────────────────────────
  raw('');
  raw('╔══════════════════════════════════════════════════╗');
  raw('║                  RESUMEN FINAL                   ║');
  raw('╠══════════════════════════════════════════════════╣');
  raw(`║  Total filas Excel    : ${String(rows.length).padStart(6)}                  ║`);
  raw(`║  Creados              : ${String(creados).padStart(6)}                  ║`);
  raw(`║  Actualizados         : ${String(actualizados).padStart(6)}                  ║`);
  raw(`║  Omitidos (sin desc.) : ${String(omitidos).padStart(6)}                  ║`);
  raw(`║  Errores              : ${String(errores.length).padStart(6)}                  ║`);
  raw('╚══════════════════════════════════════════════════╝');

  if (errores.length > 0) {
    raw('');
    raw('── ERRORES ──────────────────────────────────────');
    errores.forEach((e, i) => raw(`  [${String(i + 1).padStart(3)}] ${e}`));
  }

  raw('');
  if (DRY) {
    raw('⚠  DRY RUN — No se escribió nada en la base de datos.');
    raw('   Ejecutar sin --dry para aplicar la migración real.');
  } else {
    raw(
      errores.length === 0
        ? '✓  Migración completada sin errores.'
        : `⚠  Migración completada con ${errores.length} error(es). Revisar sección ERRORES arriba.`,
    );
  }
  raw(`\nLog guardado en: ${LOG_FILE}\n`);
}

main()
  .catch((e) => {
    log('FATAL', `Error fatal: ${e.message}\n${e.stack}`);
    process.exit(1);
  })
  .finally(async () => {
    logStream.end();
    await prisma.$disconnect();
  });
