/**
 * Completa los datos fiscales que quedaron vacíos en los clientes migrados.
 *
 *   Empresa por defecto: 9ec23228-946b-4ac3-8d9f-d547ce464c28
 *                        (Roberto Manuel Escobar Servian — Taller 8 Center)
 *
 * ── Por qué existe ──────────────────────────────────────────────────
 * `migrate-ventas-adeudadas-taller.ts` creó las personas con razón social, RUC
 * y DV solamente. Le faltó `naturaleza_id`, y de ahí se cae todo lo demás: el
 * formulario de Editar Cliente arma sus campos a partir de la naturaleza, así
 * que sin ella no muestra ni el RUC ni el tipo de contribuyente, y además
 * blanquea "Tipo Operación" en pantalla aunque en la base esté cargado
 * (ClienteFormDialog reinicia ese select cuando la naturaleza no resuelve).
 *
 * Peor todavía: quedaron con tipo_operacion B2C teniendo RUC, combinación que
 * `personas.service.validarPersonaParaFactura` rechaza — "Tipo de operación
 * inválido para contribuyente: debe ser B2B o B2G". Esas facturas no se
 * hubieran podido emitir.
 *
 * ── Reglas ──────────────────────────────────────────────────────────
 * Son las mismas que valida el backend al facturar:
 *   • Tiene RUC        → Contribuyente + tipo_contribuyente + DV + B2B/B2G
 *   • Tiene nro_doc    → No contribuyente + tipo_documento + B2C/B2F
 *   • No tiene ninguno → no se puede resolver, se reporta y no se toca
 *
 * No pisa nada que ya esté cargado. En particular respeta B2G y B2F, que son
 * elecciones válidas y deliberadas (la Municipalidad es B2G).
 *
 *   Dry-run:  pnpm reparar:clientes:taller
 *   Aplicar:  pnpm reparar:clientes:taller --apply
 */

import { PrismaClient } from '@prisma/client';

const EMPRESA_ID = process.env.EMPRESA_ID || '9ec23228-946b-4ac3-8d9f-d547ce464c28';
const APLICAR = process.argv.includes('--apply');

// Catálogos globales
const NATURALEZA_CONTRIBUYENTE = '45f32973-7d88-4d47-b2ea-d63b4d448d54'; // código 1
const NATURALEZA_NO_CONTRIBUYENTE = '50093f82-e37d-4120-ad06-cdfdfffa7ec7'; // código 2
const TIPO_CONTRIB_JURIDICA = 'b0da490e-ff65-4c8f-a5e5-823e17a67e9e';
const TIPO_CONTRIB_FISICA = 'da9fdf2b-d72c-4bb7-8689-526a7f846c83';
const TIPO_DOC_CI_PARAGUAYA = '7ce2cf6d-af16-4be2-a1da-628127a2c3c9'; // código 1
const TIPO_OP_B2B = '886b4cb8-17d8-443b-bb00-77664604ed01'; // código 1
const TIPO_OP_B2C = 'f4464d3e-b711-424c-9833-84a9d4eca8e8'; // código 2
const PAIS_PARAGUAY = '253381af-b7e6-4c2f-9e90-33be1aea687b';

/** Dígito verificador módulo 11 — el mismo cálculo que usa la SET. */
function dvModulo11(numero: string, baseMax = 11): number {
  let total = 0;
  let k = 2;
  let alfa = '';
  for (const c of numero) alfa += /\d/.test(c) ? c : c.charCodeAt(0);
  for (let i = alfa.length - 1; i >= 0; i--) {
    k = k > baseMax ? 2 : k;
    total += parseInt(alfa.charAt(i), 10) * k++;
  }
  const resto = total % 11;
  return resto > 1 ? 11 - resto : 0;
}

const vacio = (v: string | null | undefined) => v == null || String(v).trim() === '';

const prisma = new PrismaClient();

async function run() {
  console.log('');
  console.log('╔════════════════════════════════════════════════════════════╗');
  console.log(`║  REPARAR CLIENTES MIGRADOS   ${(APLICAR ? '[ APLICA — ESCRIBE ]' : '[ DRY RUN ]').padEnd(29)}║`);
  console.log('╚════════════════════════════════════════════════════════════╝');

  const empresa = await prisma.empresas.findUnique({
    where: { id: EMPRESA_ID },
    select: { razon_social: true, ruc: true },
  });
  if (!empresa) {
    console.log(`✗ La empresa ${EMPRESA_ID} no existe.`);
    return;
  }
  console.log(`Empresa: ${empresa.razon_social} (RUC ${empresa.ruc})\n`);

  const clientes = await prisma.clientes.findMany({
    where: { personas: { empresa_id: EMPRESA_ID }, deleted: false },
    include: { personas: true, tipo_operacion: { select: { codigo: true, descripcion: true } } },
    orderBy: { created_at: 'asc' },
  });
  console.log(`Clientes de la empresa: ${clientes.length}\n`);

  let conCambios = 0;
  let sinCambios = 0;
  const irreparables: string[] = [];
  const dvCalculado: string[] = [];
  const opCorregida: string[] = [];

  for (const c of clientes) {
    const p = c.personas;
    const nombre = (p.razon_social ?? '(sin nombre)').substring(0, 40);

    const parchePersona: Record<string, any> = {};
    const parcheCliente: Record<string, any> = {};

    // ── Naturaleza: es la raíz de la que cuelga todo lo demás ────────
    let naturaleza = p.naturaleza_id;
    if (!naturaleza) {
      if (!vacio(p.ruc)) naturaleza = NATURALEZA_CONTRIBUYENTE;
      else if (!vacio(p.nro_documento)) naturaleza = NATURALEZA_NO_CONTRIBUYENTE;
    }
    if (!naturaleza) {
      irreparables.push(`${nombre} — sin RUC y sin nro. de documento, no hay con qué resolver la naturaleza`);
      sinCambios++;
      continue;
    }
    if (naturaleza !== p.naturaleza_id) parchePersona.naturaleza_id = naturaleza;

    const esContribuyente = naturaleza === NATURALEZA_CONTRIBUYENTE;

    if (esContribuyente) {
      const rucNum = parseInt(String(p.ruc), 10);
      if (!p.tipo_contribuyente_id) {
        parchePersona.tipo_contribuyente_id = rucNum > 80_000_000 ? TIPO_CONTRIB_JURIDICA : TIPO_CONTRIB_FISICA;
      }
      if (vacio(p.dv) && !vacio(p.ruc)) {
        // El CSV de origen tampoco lo traía para estos: se calcula y se avisa.
        const dv = String(dvModulo11(String(p.ruc).trim()));
        parchePersona.dv = dv;
        dvCalculado.push(`${nombre} — RUC ${p.ruc} → DV ${dv}`);
      }
    } else {
      if (!p.tipo_documento_id) parchePersona.tipo_documento_id = TIPO_DOC_CI_PARAGUAYA;
    }

    // ── País ────────────────────────────────────────────────────────
    // No lo exige la factura (`cPaisRec` va fijo en 'PRY'), pero dejarlo nulo
    // hace que el formulario abra el campo vacío y el usuario no sepa si falta.
    if (!p.pais_id) parchePersona.pais_id = PAIS_PARAGUAY;

    // ── Tipo de operación coherente con la naturaleza ────────────────
    // Contribuyente admite B2B (1) o B2G (3); no contribuyente, B2C (2) o
    // B2F (4). Sólo se corrige lo que quedó fuera de su par válido — B2G y B2F
    // son elecciones deliberadas y no se tocan.
    const codOp = Number(c.tipo_operacion?.codigo);
    if (esContribuyente && codOp !== 1 && codOp !== 3) {
      parcheCliente.tipo_operacion_id = TIPO_OP_B2B;
      opCorregida.push(`${nombre} — ${c.tipo_operacion?.descripcion} → B2B (tiene RUC ${p.ruc})`);
    } else if (!esContribuyente && codOp !== 2 && codOp !== 4) {
      parcheCliente.tipo_operacion_id = TIPO_OP_B2C;
      opCorregida.push(`${nombre} — ${c.tipo_operacion?.descripcion} → B2C (sin RUC)`);
    }

    const campos = [...Object.keys(parchePersona), ...Object.keys(parcheCliente)];
    if (campos.length === 0) {
      sinCambios++;
      continue;
    }

    conCambios++;
    console.log(`  ${nombre.padEnd(42)} ${campos.join(', ')}`);

    if (!APLICAR) continue;

    if (Object.keys(parchePersona).length) {
      await prisma.personas.update({ where: { id: p.id }, data: parchePersona });
    }
    if (Object.keys(parcheCliente).length) {
      await prisma.clientes.update({ where: { id: c.id }, data: parcheCliente });
    }
  }

  const bloque = (titulo: string, lista: string[]) => {
    if (!lista.length) return;
    console.log(`\n── ${titulo} (${lista.length})`);
    lista.forEach((l) => console.log(`   • ${l}`));
  };

  bloque('TIPO DE OPERACIÓN CORREGIDO — la combinación anterior no dejaba facturar', opCorregida);
  bloque('DV CALCULADO POR MÓDULO 11 — el origen no lo traía, conviene verificarlo', dvCalculado);
  bloque('NO SE PUEDEN COMPLETAR — hay que cargarles el documento a mano', irreparables);

  console.log('');
  console.log('╔════════════════════════════════════════════════════════════╗');
  console.log('║                      RESUMEN                               ║');
  console.log('╚════════════════════════════════════════════════════════════╝');
  console.log(`  Clientes revisados        : ${String(clientes.length).padStart(5)}`);
  console.log(`  Con datos a completar     : ${String(conCambios).padStart(5)}`);
  console.log(`  Ya estaban completos      : ${String(sinCambios).padStart(5)}`);
  console.log(`  Sin forma de resolver     : ${String(irreparables.length).padStart(5)}`);
  console.log('');
  console.log(APLICAR ? '✓  Reparación aplicada.' : '⚠  DRY RUN — no se escribió nada. Usar --apply para aplicar.');
  console.log('');
}

run()
  .catch((e) => {
    console.error(`✗✗ FATAL: ${e.message}\n${e.stack}`);
    process.exitCode = 1;
  })
  .finally(() => prisma.$disconnect());
