/**
 * Script: create-demo-ventas
 *
 * Crea una cuenta demo completa para ventas:
 *  - Tenant "Demo Ventas SA de CV" (plan custom, sin cobro)
 *  - Usuario owner: demo@horuxfin.com / Demo12345!
 *  - Base de datos propia con datos ficticios de contabilidad
 *  - Contribuyente, clientes/proveedores, CFDIs, bancos, conciliaciones,
 *    obligaciones fiscales y cartera.
 *
 * Ejecución:
 *   cd apps/api && npx tsx scripts/create-demo-ventas.ts
 */
import { PrismaClient } from '@prisma/client';
import { Pool } from 'pg';
import bcrypt from 'bcryptjs';
import { randomUUID } from 'crypto';
import { tenantDb } from '../src/config/database.ts';

const prisma = new PrismaClient();

const DEMO = {
  rfc: 'DEMO2501019X2',
  nombre: 'Demo Ventas SA de CV',
  email: 'demo@horuxfin.com',
  password: 'Demo12345!',
  databaseName: 'horux_demoventas',
  codigoPostal: '01000',
};

const CLIENTES = [
  { rfc: 'CLI123456AB1', nombre: 'Cliente Alfa SA' },
  { rfc: 'CLI123456AB2', nombre: 'Cliente Beta SA' },
  { rfc: 'CLI123456AB3', nombre: 'Cliente Gamma SA' },
  { rfc: 'CLI123456AB4', nombre: 'Cliente Delta SA' },
  { rfc: 'CLI123456AB5', nombre: 'Cliente Epsilon SA' },
];

const PROVEEDORES = [
  { rfc: 'PRO123456AB1', nombre: 'Proveedor Materiales SA' },
  { rfc: 'PRO123456AB2', nombre: 'Proveedor Servicios SA' },
  { rfc: 'PRO123456AB3', nombre: 'Proveedor Logistica SA' },
  { rfc: 'PRO123456AB4', nombre: 'Proveedor Tecnologia SA' },
  { rfc: 'PRO123456AB5', nombre: 'Proveedor Papeleria SA' },
];

const PRODUCTOS = [
  { clave: '84111506', descripcion: 'Servicio de consultoria', unidad: 'Servicio' },
  { clave: '43232408', descripcion: 'Licencia de software', unidad: 'Licencia' },
  { clave: '81141500', descripcion: 'Soporte tecnico', unidad: 'Servicio' },
  { clave: '81121700', descripcion: 'Desarrollo web', unidad: 'Servicio' },
  { clave: '86101500', descripcion: 'Capacitacion', unidad: 'Servicio' },
  { clave: '50151500', descripcion: 'Materiales de oficina', unidad: 'Pieza' },
  { clave: '80181600', descripcion: 'Publicidad', unidad: 'Servicio' },
  { clave: '81112200', descripcion: 'Diseno grafico', unidad: 'Servicio' },
];

function parseDatabaseUrl(url: string) {
  const parsed = new URL(url);
  return {
    host: parsed.hostname,
    port: parseInt(parsed.port || '5432'),
    user: decodeURIComponent(parsed.username),
    password: decodeURIComponent(parsed.password),
  };
}

async function main() {
  console.log('🌱 Creando cuenta demo "Demo Ventas"...\n');

  const ownerRole = await prisma.rol.findUnique({ where: { nombre: 'owner' } });
  if (!ownerRole) throw new Error('Rol owner no encontrado en BD central');

  // ============================================================
  // 1. Tenant
  // ============================================================
  let tenant = await prisma.tenant.findUnique({ where: { rfc: DEMO.rfc } });
  if (!tenant) {
    tenant = await prisma.tenant.create({
      data: {
        nombre: DEMO.nombre,
        rfc: DEMO.rfc,
        plan: 'custom',
        databaseName: DEMO.databaseName,
        verticalProfile: 'CONTABLE',
        dbMode: 'MANAGED',
        dbSchemaVersion: 0,
        codigoPostal: DEMO.codigoPostal,
        active: true,
      },
    });
    console.log('✅ Tenant creado:', tenant.nombre, `(${tenant.rfc})`);
  } else {
    await prisma.tenant.update({
      where: { id: tenant.id },
      data: { plan: 'custom', active: true, verticalProfile: 'CONTABLE' },
    });
    console.log('✅ Tenant actualizado:', tenant.nombre, `(${tenant.rfc})`);
  }

  // ============================================================
  // 2. Usuario owner
  // ============================================================
  let user = await prisma.user.findUnique({ where: { email: DEMO.email } });
  const passwordHash = await bcrypt.hash(DEMO.password, 12);
  if (!user) {
    user = await prisma.user.create({
      data: {
        email: DEMO.email,
        passwordHash,
        nombre: 'Usuario Demo',
        lastTenantId: tenant.id,
      },
    });
    console.log('✅ Usuario creado:', user.email);
  } else {
    user = await prisma.user.update({
      where: { id: user.id },
      data: { passwordHash, lastTenantId: tenant.id },
    });
    console.log('✅ Usuario actualizado:', user.email);
  }

  // ============================================================
  // 3. Membership
  // ============================================================
  await prisma.tenantMembership.upsert({
    where: { userId_tenantId: { userId: user.id, tenantId: tenant.id } },
    update: { rolId: ownerRole.id, isOwner: true, active: true },
    create: {
      userId: user.id,
      tenantId: tenant.id,
      rolId: ownerRole.id,
      isOwner: true,
      active: true,
    },
  });
  console.log('✅ Membership owner asignada');

  // ============================================================
  // 4. Suscripción custom gratis/ilimitada (status authorized)
  // ============================================================
  const now = new Date();
  const periodEnd = new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000);
  const existingSub = await prisma.subscription.findFirst({
    where: { tenantId: tenant.id },
    orderBy: { createdAt: 'desc' },
  });

  if (!existingSub) {
    await prisma.subscription.create({
      data: {
        tenantId: tenant.id,
        plan: 'custom',
        status: 'authorized',
        amount: 0,
        frequency: 'monthly',
        currentPeriodStart: now,
        currentPeriodEnd: periodEnd,
      },
    });
  } else {
    await prisma.subscription.update({
      where: { id: existingSub.id },
      data: {
        plan: 'custom',
        status: 'authorized',
        amount: 0,
        currentPeriodStart: now,
        currentPeriodEnd: periodEnd,
        pendingPlan: null,
        pendingFrequency: null,
        pendingEffectiveAt: null,
        upgradePreferenceId: null,
        upgradeTargetPlan: null,
        upgradeTargetAmount: null,
      },
    });
  }
  console.log('✅ Suscripción custom activa (gratis)');

  // ============================================================
  // 5. Régimen fiscal activo del tenant
  // ============================================================
  const regimen = await prisma.regimen.findUnique({ where: { clave: '601' } });
  if (regimen) {
    await prisma.tenantRegimenActivo.upsert({
      where: { tenantId_regimenId: { tenantId: tenant.id, regimenId: regimen.id } },
      update: {},
      create: { tenantId: tenant.id, regimenId: regimen.id },
    });
    console.log('✅ Régimen 601 activado para el tenant');
  }

  // ============================================================
  // 6. Base de datos del tenant
  // ============================================================
  await tenantDb.provisionDatabase(DEMO.rfc, DEMO.databaseName);
  const pool = await tenantDb.getPool(tenant.id, DEMO.databaseName);
  console.log('✅ Base de datos del tenant provisionada:', DEMO.databaseName);

  // ============================================================
  // 7. Datos ficticios en BD del tenant
  // ============================================================
  await seedTenantData(pool, tenant.id, user.id);

  console.log('\n🎉 Demo Ventas lista');
  console.log('   Login:', DEMO.email, '/', DEMO.password);
  console.log('   Tenant:', DEMO.nombre, `(${DEMO.rfc})`);
  console.log('   BD:', DEMO.databaseName);
}

async function seedTenantData(pool: Pool, tenantId: string, ownerId: string) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');

    // Contribuyente principal
    const { rows: [entidad] } = await client.query<{ id: string }>(`
      INSERT INTO entidades_gestionadas (tipo, nombre, identificador, supervisor_user_id)
      VALUES ('CONTRIBUYENTE', $1, $2, $3)
      ON CONFLICT DO NOTHING
      RETURNING id
    `, [DEMO.nombre, DEMO.rfc, ownerId]);

    let contribuyenteId: string;
    if (entidad) {
      contribuyenteId = entidad.id;
      await client.query(`
        INSERT INTO contribuyentes (entidad_id, rfc, regimen_fiscal, codigo_postal)
        VALUES ($1, $2, $3, $4)
        ON CONFLICT (entidad_id) DO NOTHING
      `, [contribuyenteId, DEMO.rfc, '601', DEMO.codigoPostal]);
    } else {
      const { rows: [existing] } = await client.query<{ id: string }>(`
        SELECT e.id FROM entidades_gestionadas e
        JOIN contribuyentes c ON c.entidad_id = e.id
        WHERE e.identificador = $1
      `, [DEMO.rfc]);
      contribuyenteId = existing.id;
    }
    console.log('✅ Contribuyente principal creado:', DEMO.rfc);

    // RFCs de clientes y proveedores
    const rfcs = new Map<string, number>();
    for (const c of [...CLIENTES, ...PROVEEDORES]) {
      const { rows: [r] } = await client.query<{ id: number }>(`
        INSERT INTO rfcs (rfc, razon_social, regimen_fiscal)
        VALUES ($1, $2, $3)
        ON CONFLICT (rfc) DO UPDATE SET razon_social = EXCLUDED.razon_social
        RETURNING id
      `, [c.rfc, c.nombre, c.rfc.startsWith('CLI') ? '601' : '601']);
      rfcs.set(c.rfc, r.id);
    }
    // RFC del contribuyente principal
    const { rows: [rfcPrincipal] } = await client.query<{ id: number }>(`
      INSERT INTO rfcs (rfc, razon_social, regimen_fiscal)
      VALUES ($1, $2, $3)
      ON CONFLICT (rfc) DO UPDATE SET razon_social = EXCLUDED.razon_social
      RETURNING id
    `, [DEMO.rfc, DEMO.nombre, '601']);
    rfcs.set(DEMO.rfc, rfcPrincipal.id);

    // Bancos del contribuyente
    const { rows: [banco1] } = await client.query<{ id: number }>(`
      INSERT INTO bancos (banco, terminacion_cuenta, contribuyente_id)
      VALUES ($1, $2, $3) RETURNING id
    `, ['BBVA', '1234', contribuyenteId]);
    const { rows: [banco2] } = await client.query<{ id: number }>(`
      INSERT INTO bancos (banco, terminacion_cuenta, contribuyente_id)
      VALUES ($1, $2, $3) RETURNING id
    `, ['Santander', '5678', contribuyenteId]);
    console.log('✅ Bancos creados');

    // Generar CFDIs
    const tipos: Array<'EMITIDO' | 'RECIBIDO'> = ['EMITIDO', 'RECIBIDO'];
    const cfdiIds: number[] = [];

    for (let i = 0; i < 60; i++) {
      const tipo = tipos[i % 2];
      const esEmitido = tipo === 'EMITIDO';
      const contraparte = esEmitido
        ? CLIENTES[i % CLIENTES.length]
        : PROVEEDORES[i % PROVEEDORES.length];

      const subtotal = Math.floor(Math.random() * 40000) + 2000;
      const iva = Math.round(subtotal * 0.16 * 100) / 100;
      const total = Math.round((subtotal + iva) * 100) / 100;

      const daysAgo = Math.floor(Math.random() * 540); // hasta ~18 meses atrás
      const fecha = new Date();
      fecha.setDate(fecha.getDate() - daysAgo);
      fecha.setHours(10 + (i % 8), 0, 0, 0);

      const year = String(fecha.getFullYear());
      const month = String(fecha.getMonth() + 1).padStart(2, '0');
      const fechaStr = fecha.toISOString();

      const metodoPago = Math.random() > 0.3 ? 'PUE' : 'PPD';
      const formasPago = ['01', '02', '03', '04'];
      const formaPago = formasPago[i % formasPago.length];
      const usoCfdi = esEmitido ? 'G03' : 'G01';

      const rfcEmisor = esEmitido ? DEMO.rfc : contraparte.rfc;
      const nombreEmisor = esEmitido ? DEMO.nombre : contraparte.nombre;
      const rfcReceptor = esEmitido ? contraparte.rfc : DEMO.rfc;
      const nombreReceptor = esEmitido ? contraparte.nombre : DEMO.nombre;

      const { rows: [cfdi] } = await client.query<{ id: number }>(`
        INSERT INTO cfdis (
          year, month, type, uuid, serie, folio, status, fecha_emision,
          rfc_emisor_id, rfc_emisor, nombre_emisor,
          rfc_receptor_id, rfc_receptor, nombre_receptor,
          subtotal, subtotal_mxn, descuento, descuento_mxn,
          total, total_mxn, moneda, tipo_cambio, tipo_comprobante,
          metodo_pago, forma_pago, uso_cfdi,
          iva_traslado, iva_traslado_mxn,
          regimen_fiscal_emisor, regimen_fiscal_receptor,
          contribuyente_id, fecha_efectiva, meses_global, año_global
        ) VALUES (
          $1, $2, $3, $4, $5, $6, $7, $8,
          $9, $10, $11,
          $12, $13, $14,
          $15, $16, $17, $18,
          $19, $20, $21, $22, $23,
          $24, $25, $26,
          $27, $28,
          $29, $30,
          $31, $32, $33, $34
        ) RETURNING id
      `, [
        year, month, tipo, randomUUID(), 'DEMO', String(1000 + i),
        'Vigente', fechaStr,
        rfcs.get(rfcEmisor), rfcEmisor, nombreEmisor,
        rfcs.get(rfcReceptor), rfcReceptor, nombreReceptor,
        subtotal, subtotal, 0, 0,
        total, total, 'MXN', 1, 'I',
        metodoPago, formaPago, usoCfdi,
        iva, iva,
        '601', '601',
        contribuyenteId, fechaStr, month, year,
      ]);
      cfdiIds.push(cfdi.id);

      // Conceptos
      const numConceptos = Math.floor(Math.random() * 3) + 1;
      for (let j = 0; j < numConceptos; j++) {
        const prod = PRODUCTOS[(i + j) % PRODUCTOS.length];
        const cantidad = Math.floor(Math.random() * 5) + 1;
        const valorUnitario = Math.floor(Math.random() * 4000) + 500;
        const importe = Math.round(cantidad * valorUnitario * 100) / 100;
        const ivaConcepto = Math.round(importe * 0.16 * 100) / 100;

        await client.query(`
          INSERT INTO cfdi_conceptos (
            cfdi_id, clave_prod_serv, descripcion, cantidad, clave_unidad, unidad,
            valor_unitario, valor_unitario_mxn, importe, importe_mxn,
            iva_traslado, iva_traslado_mxn
          ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
        `, [
          cfdi.id, prod.clave, prod.descripcion, cantidad, 'E48', prod.unidad,
          valorUnitario, valorUnitario, importe, importe,
          ivaConcepto, ivaConcepto,
        ]);
      }
    }
    console.log('✅ 60 CFDIs y conceptos creados');

    // Conciliaciones para algunos CFDIs PPD pagados con transferencia (forma 02/03)
    const { rows: cfdisPpd } = await client.query<{ id: number; year: string; month: string }>(`
      SELECT id, year, month FROM cfdis
      WHERE metodo_pago = 'PPD' AND forma_pago IN ('02', '03')
      ORDER BY id LIMIT 15
    `);

    for (const c of cfdisPpd) {
      const bancoId = Math.random() > 0.5 ? banco1.id : banco2.id;
      const fechaPago = new Date();
      fechaPago.setDate(fechaPago.getDate() - Math.floor(Math.random() * 30));
      await client.query(`
        INSERT INTO conciliaciones (anio, mes, id_cfdi, fecha_de_pago, id_banco)
        VALUES ($1, $2, $3, $4, $5)
        ON CONFLICT (id_cfdi) DO NOTHING
      `, [c.year, c.month, c.id, fechaPago.toISOString().split('T')[0], bancoId]);
    }
    console.log('✅ Conciliaciones creadas');

    // Obligaciones fiscales asignadas al contribuyente
    const obligaciones = [
      { id: 'isr-provisional', nombre: 'Pago provisional de ISR', categoria: 'Federal mensual' },
      { id: 'iva-mensual', nombre: 'Pago mensual definitivo de IVA', categoria: 'Federal mensual' },
      { id: 'ret-isr-honorarios', nombre: 'Retenciones de ISR por honorarios y arrendamiento a PF', categoria: 'Federal mensual' },
      { id: 'diot', nombre: 'DIOT', categoria: 'Informativa mensual' },
      { id: 'imss-cuotas', nombre: 'Cuotas obrero-patronales IMSS', categoria: 'Seguridad social' },
      { id: 'anual-isr-pm', nombre: 'Declaración Anual de ISR PM', categoria: 'Anual' },
      { id: 'isn', nombre: 'ISN - Impuesto Sobre Nómina', categoria: 'Estatal' },
      { id: 'isrtp', nombre: 'Impuesto sobre remuneración al trabajo', categoria: 'Estatal' },
    ];

    for (const o of obligaciones) {
      await client.query(`
        INSERT INTO obligaciones_contribuyente (
          contribuyente_id, catalogo_id, nombre, frecuencia, fecha_limite, categoria, activa, es_recomendada
        ) VALUES ($1, $2, $3, $4, $5, $6, true, true)
        ON CONFLICT DO NOTHING
      `, [contribuyenteId, o.id, o.nombre, 'mensual', 'Día 17 del mes siguiente', o.categoria]);
    }
    console.log('✅ Obligaciones fiscales asignadas');

    // Cartera principal con el contribuyente
    const { rows: [cartera] } = await client.query<{ id: string }>(`
      INSERT INTO carteras (supervisor_user_id, nombre, descripcion)
      VALUES ($1, $2, $3) RETURNING id
    `, [ownerId, 'Cartera Principal', 'Clientes y prospectos de Demo Ventas']);

    await client.query(`
      INSERT INTO cartera_entidades (cartera_id, entidad_id)
      VALUES ($1, $2)
      ON CONFLICT DO NOTHING
    `, [cartera.id, contribuyenteId]);
    console.log('✅ Cartera principal creada');

    // Alertas y recordatorios de ejemplo
    await client.query(`
      INSERT INTO alertas (tipo, titulo, mensaje, prioridad, fecha_vencimiento)
      VALUES
        ('obligacion', 'Declaración mensual de IVA', 'Pago de IVA correspondiente a mayo 2026', 'alta', NOW() + INTERVAL '10 days'),
        ('obligacion', 'Pago provisional ISR', 'Pago provisional de ISR de mayo 2026', 'alta', NOW() + INTERVAL '10 days'),
        ('sat', 'Sincronización SAT pendiente', 'Última sincronización hace más de 7 días', 'media', NOW() + INTERVAL '3 days')
    `);

    await client.query(`
      INSERT INTO recordatorios (titulo, descripcion, fecha_limite, notas, completado, privado, creado_por)
      VALUES
        ('Revisar estados de cuenta', 'Conciliar pagos de clientes', NOW() + INTERVAL '5 days', 'Prioridad alta', false, false, $1),
        ('Enviar facturas del mes', 'Facturación recurrente a clientes', NOW() + INTERVAL '7 days', 'Clientes Alfa y Beta', false, false, $1)
    `, [ownerId]);
    console.log('✅ Alertas y recordatorios de ejemplo creados');

    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK');
    throw err;
  } finally {
    client.release();
  }
}

main()
  .catch((e) => {
    console.error('\n❌ Error creando demo:', e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
    await tenantDb.shutdown();
  });
