const fs = require('fs');
const path = require('path');
const { Pool } = require('pg');
const { parseXml } = require('/root/HoruxDespachosNuevo/apps/api/dist/services/sat/sat-parser.service.js');

const DB_PASSWORD = 'ZxHMrmnwanvLfLDdNJdRthFjWF2Lj1Rb';
const BASE_DIR = '/root/HoruxDespachosNuevo/apps/api/data/xmls';

function getPool(dbName) {
  return new Pool({ host: 'localhost', user: 'postgres', password: DB_PASSWORD, database: dbName });
}

async function reprocessXml(filePath, rfc, tipoCfdi) {
  let xmlContent = fs.readFileSync(filePath, 'utf-8');
  if (xmlContent.charCodeAt(0) === 0xFEFF) {
    xmlContent = xmlContent.slice(1);
  }

  const cfdi = parseXml(xmlContent, tipoCfdi);
  if (!cfdi) {
    console.log(`  SKIP: parseXml returned null for ${filePath}`);
    return false;
  }

  const dbName = `horux_${rfc.toLowerCase()}`;
  const pool = getPool(dbName);

  try {
    const uuidNorm = cfdi.uuid.toLowerCase();
    const { rows: existing } = await pool.query(
      `SELECT id FROM cfdis WHERE LOWER(uuid) = $1`, [uuidNorm]
    );

    if (existing.length === 0) {
      console.log(`  SKIP: CFDI ${uuidNorm} not found in DB ${dbName}`);
      return false;
    }

    const cfdiId = existing[0].id;
    const tc = cfdi.tipoCambio || 1;
    const m = (v) => (v || 0) * tc;

    // Update cfdis
    await pool.query(
      `UPDATE cfdis SET
        serie = $1, folio = $2, status = $3, fecha_emision = $4, fecha_cert_sat = $5,
        rfc_emisor = $6, nombre_emisor = $7, rfc_receptor = $8, nombre_receptor = $9,
        subtotal = $10, subtotal_mxn = $11, descuento = $12, descuento_mxn = $13,
        total = $14, total_mxn = $15, moneda = $16, tipo_cambio = $17,
        metodo_pago = $18, forma_pago = $19, uso_cfdi = $20, pac = $21,
        isr_retencion = $22, isr_retencion_mxn = $23,
        iva_traslado = $24, iva_traslado_mxn = $25,
        iva_retencion = $26, iva_retencion_mxn = $27,
        ieps_traslado = $28, ieps_traslado_mxn = $29,
        ieps_retencion = $30, ieps_retencion_mxn = $31,
        impuestos_locales_trasladado = $32, impuestos_locales_trasladado_mxn = $33,
        impuestos_locales_retenidos = $34, impuestos_locales_retenidos_mxn = $35,
        monto_pago = $36, monto_pago_mxn = $37,
        fecha_pago_p = $38, num_parcialidad = $39,
        isr_retencion_pago = $40, isr_retencion_pago_mxn = $41,
        iva_traslado_pago = $42, iva_traslado_pago_mxn = $43,
        iva_retencion_pago = $44, iva_retencion_pago_mxn = $45,
        ieps_traslado_pago = $46, ieps_traslado_pago_mxn = $47,
        ieps_retencion_pago = $48, ieps_retencion_pago_mxn = $49,
        fecha_pago = $50, fecha_inicial_pago = $51, fecha_final_pago = $52,
        num_dias_pagados = $53, num_seguro_social = $54, puesto = $55,
        salario_base_cot_apor = $56, salario_base_cot_apor_mxn = $57,
        salario_diario_integrado = $58, salario_diario_integrado_mxn = $59,
        total_percepciones = $60, total_percepciones_mxn = $61,
        total_deducciones = $62, total_deducciones_mxn = $63,
        imp_retenidos_nomina = $64, imp_retenidos_nomina_mxn = $65,
        otras_deducciones_nomina = $66, otras_deducciones_nomina_mxn = $67,
        subsidio_causado = $68, subsidio_causado_mxn = $69,
        regimen_fiscal_emisor = $70, regimen_fiscal_receptor = $71,
        xml_original = $72, cfdi_tipo_relacion = $73, cfdis_relacionados = $74,
        saldo_insoluto = $75, uuid_relacionado = $76,
        actualizado_en = NOW()
      WHERE id = $77`,
      [
        cfdi.serie, cfdi.folio, cfdi.status, cfdi.fechaEmision, cfdi.fechaCertSat,
        cfdi.rfcEmisor, cfdi.nombreEmisor, cfdi.rfcReceptor, cfdi.nombreReceptor,
        cfdi.subtotal, m(cfdi.subtotal), cfdi.descuento, m(cfdi.descuento),
        cfdi.total, m(cfdi.total), cfdi.moneda, cfdi.tipoCambio,
        cfdi.metodoPago, cfdi.formaPago, cfdi.usoCfdi, cfdi.pac,
        cfdi.isrRetencion, m(cfdi.isrRetencion),
        cfdi.ivaTraslado, m(cfdi.ivaTraslado),
        cfdi.ivaRetencion, m(cfdi.ivaRetencion),
        cfdi.iepsTraslado, m(cfdi.iepsTraslado),
        cfdi.iepsRetencion, m(cfdi.iepsRetencion),
        cfdi.impuestosLocalesTrasladado, m(cfdi.impuestosLocalesTrasladado),
        cfdi.impuestosLocalesRetenidos, m(cfdi.impuestosLocalesRetenidos),
        cfdi.montoPago, m(cfdi.montoPago),
        cfdi.fechaPagoP, cfdi.numParcialidad,
        cfdi.isrRetencionPago, m(cfdi.isrRetencionPago),
        cfdi.ivaTrasladoPago, m(cfdi.ivaTrasladoPago),
        cfdi.ivaRetencionPago, m(cfdi.ivaRetencionPago),
        cfdi.iepsTrasladoPago, m(cfdi.iepsTrasladoPago),
        cfdi.iepsRetencionPago, m(cfdi.iepsRetencionPago),
        cfdi.fechaPago, cfdi.fechaInicialPago, cfdi.fechaFinalPago,
        cfdi.numDiasPagados, cfdi.numSeguroSocial, cfdi.puesto,
        cfdi.salarioBaseCotApor, m(cfdi.salarioBaseCotApor),
        cfdi.salarioDiarioIntegrado, m(cfdi.salarioDiarioIntegrado),
        cfdi.totalPercepciones, m(cfdi.totalPercepciones),
        cfdi.totalDeducciones, m(cfdi.totalDeducciones),
        cfdi.impRetenidosNomina, m(cfdi.impRetenidosNomina),
        cfdi.otrasDeduccionesNomina, m(cfdi.otrasDeduccionesNomina),
        cfdi.subsidioCausado, m(cfdi.subsidioCausado),
        cfdi.regimenFiscalEmisor, cfdi.regimenFiscalReceptor,
        xmlContent, cfdi.cfdiTipoRelacion, cfdi.cfdisRelacionados,
        cfdi.saldoInsoluto, cfdi.uuidRelacionado,
        cfdiId
      ]
    );

    // Re-insert conceptos
    await pool.query(`DELETE FROM cfdi_conceptos WHERE cfdi_id = $1`, [cfdiId]);
    for (const c of cfdi.conceptos || []) {
      await pool.query(
        `INSERT INTO cfdi_conceptos (
          cfdi_id, clave_prod_serv, no_identificacion, descripcion, cantidad,
          clave_unidad, unidad, valor_unitario, valor_unitario_mxn, importe, importe_mxn,
          descuento, descuento_mxn,
          isr_retencion, isr_retencion_mxn, iva_traslado, iva_traslado_mxn,
          iva_retencion, iva_retencion_mxn, ieps_traslado, ieps_traslado_mxn,
          ieps_retencion, ieps_retencion_mxn
        ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)`,
        [
          cfdiId, c.claveProdServ, c.noIdentificacion, c.descripcion, c.cantidad,
          c.claveUnidad, c.unidad, c.valorUnitario, m(c.valorUnitario), c.importe, m(c.importe),
          c.descuento, m(c.descuento),
          c.isrRetencion, m(c.isrRetencion), c.ivaTraslado, m(c.ivaTraslado),
          c.ivaRetencion, m(c.ivaRetencion), c.iepsTraslado, m(c.iepsTraslado),
          c.iepsRetencion, m(c.iepsRetencion)
        ]
      );
    }

    console.log(`  OK: ${uuidNorm} updated in ${dbName}`);
    return true;
  } catch (err) {
    console.error(`  ERROR: ${filePath} - ${err.message}`);
    return false;
  } finally {
    await pool.end();
  }
}

async function main() {
  let processed = 0;
  let skipped = 0;
  let errors = 0;

  const rfcs = fs.readdirSync(BASE_DIR).filter(d => fs.statSync(path.join(BASE_DIR, d)).isDirectory());

  for (const rfc of rfcs) {
    const rfcDir = path.join(BASE_DIR, rfc);
    const tipos = fs.readdirSync(rfcDir).filter(d => fs.statSync(path.join(rfcDir, d)).isDirectory());

    for (const tipo of tipos) {
      const tipoDir = path.join(rfcDir, tipo);
      const packages = fs.readdirSync(tipoDir).filter(d => fs.statSync(path.join(tipoDir, d)).isDirectory());

      for (const pkg of packages) {
        const pkgDir = path.join(tipoDir, pkg);
        const files = fs.readdirSync(pkgDir).filter(f => f.endsWith('.xml'));

        for (const file of files) {
          const filePath = path.join(pkgDir, file);
          const buf = fs.readFileSync(filePath);
          if (buf.length < 3 || !(buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF)) {
            continue;
          }

          console.log(`Processing ${filePath}...`);
          const ok = await reprocessXml(filePath, rfc, tipo);
          if (ok) processed++;
          else skipped++;
          if (!ok) errors++;
        }
      }
    }
  }

  console.log(`\nDone. Processed: ${processed}, Skipped/Errors: ${errors}`);
}

main().catch(console.error);
