// Formación y desarrollo — datos mock y helpers deterministas para la nueva
// arquitectura de 6 dominios (Resumen / Inducción / Entrenamiento al cargo /
// Capacitación interna / Capacitación externa / Configuración). Este archivo
// NO define vistas — solo datos, catálogos de estado y funciones puras. Se
// carga después de competencias.jsx (reutiliza CARGOS_MAESTRO, PROVEEDORES,
// COURSES cuando aplica) y antes de los training-*.jsx que sí renderizan.
//
// Fuente funcional: prompt "Evolución visual de Formación y desarrollo"
// (.claude/prompt-formacion-desarrollo-prototipo-gh.md). Varias reglas de
// cálculo aquí son explícitamente supuestos de demo — ver
// FORMATION_ASSUMPTIONS más abajo y la vista Resumen, que los expone.

const FORMATION_DEMO_TODAY = '2026-05-05';
const FORMATION_REINDUCCION_DIAS_ALERTA = 30; // "Próxima a vencer" — supuesto de demo, ver Resumen

// ─────────────────────────────────────────── Helpers de fecha (deterministas) ─────────
const _fd_pad = (n) => String(n).padStart(2, '0');
const _fd_toISO = (d) => `${d.getFullYear()}-${_fd_pad(d.getMonth() + 1)}-${_fd_pad(d.getDate())}`;
const _fd_fromISO = (iso) => new Date(`${iso}T00:00:00`);
const _fd_addDays = (iso, days) => { const d = _fd_fromISO(iso); d.setDate(d.getDate() + days); return _fd_toISO(d); };
const _fd_addMonths = (iso, months) => { const d = _fd_fromISO(iso); d.setMonth(d.getMonth() + months); return _fd_toISO(d); };
const _fd_addYears = (iso, years) => { const d = _fd_fromISO(iso); d.setFullYear(d.getFullYear() + years); return _fd_toISO(d); };
const _fd_daysBetween = (fromIso, toIso) => Math.round((_fd_fromISO(toIso) - _fd_fromISO(fromIso)) / 86400000);
const _fd_isSameMonth = (iso, refIso) => !!iso && iso.slice(0, 7) === refIso.slice(0, 7);
const _fd_labelCO = (iso) => !iso ? 'Sin información' : _fd_fromISO(iso).toLocaleDateString('es-CO', { day: '2-digit', month: 'short', year: 'numeric' });
const _fd_monthLabelCO = (iso) => _fd_fromISO(iso).toLocaleDateString('es-CO', { month: 'long', year: 'numeric' });

// ─────────────────────────────────────────── Catálogos de estado ─────────
const IND_SOPORTE_OPCIONES = ['Sí', 'No', 'Pendiente'];
const IND_ESTADO_OPCIONES = ['Desistió del proceso', 'Corrieron la fecha de ingreso', 'Pendiente por revisar', 'Ejecutado'];
const IND_ESTADO_META = {
  'Desistió del proceso': { tone: 'rose' }, 'Corrieron la fecha de ingreso': { tone: 'amber' },
  'Pendiente por revisar': { tone: 'navy' }, 'Ejecutado': { tone: 'green' },
};

const REIND_ESTADO_META = {
  vencida: { label: 'Vencida', tone: 'rose' },
  proxima: { label: 'Próxima a vencer', tone: 'amber' },
  completa: { label: 'Completa', tone: 'green' },
};

const JOB_TRAINING_ESTADOS = ['Pendiente por ejecutar', 'Ejecutado', 'Vencido', 'No aplica'];
const JOB_TRAINING_MOTIVOS = ['Ingreso nuevo', 'Cambio de cargo', 'Cambio de planta', 'Vencimiento de periodo', 'Solicitud del líder'];
const JOB_ALERTA_META = {
  vigente: { label: 'Vigente', tone: 'green' }, proxima: { label: 'Próxima a vencer', tone: 'amber' }, vencida: { label: 'Vencida', tone: 'rose' }, pendiente: { label: 'Pendiente por ejecutar', tone: 'navy' },
};

const INTERNAL_ACADEMIC_STATES = ['Inscrito', 'Pendiente por ejecutar', 'Aprobado'];
const INTERNAL_OPERATIONAL_STATES = ['Planeado', 'Ejecutado', 'Retirado', 'Pendiente por ejecutar'];
const INTERNAL_PROCESOS = ['Calidad', 'SST', 'Ambiental', 'GH', 'Gestión del Riesgo', 'Cumplimiento', 'Operaciones'];
const INTERNAL_PROPUESTA_ESTADOS = ['Borrador', 'En revisión', 'Devuelta', 'Aprobada'];

const EXTERNAL_SOLICITUD_ESTADOS = ['En revisión', 'Aprobado', 'Devuelto', 'Rechazado'];
const EXTERNAL_EXAMEN_ESTADOS = ['Apto', 'No aplica', 'No apto', 'Vencido', 'En validación de SST'];
const EXTERNAL_SEGUIMIENTO_ESTADOS = ['Retirado', 'Cancelado', 'En exámenes', 'Programado a curso', 'Próximo a vencer', 'Vencido', 'Vigente', 'En trámite'];

// ─────────────────────────────────────────── Empleados del módulo ─────────
// Roster local documentado (ver componentes de Dashboard/Mi perfil de esta
// misma sesión para el mismo patrón): reutiliza los IDs reales de EMPLOYEES
// (employee-list.jsx) para las 5 personas que el requerimiento nombra
// explícitamente, y agrega personas nuevas (rango E04xx) para cubrir toda la
// variedad exigida sin inflar el roster real de Empleados. Todas las sedes
// están limitadas a Cali, Yumbo y Barranquilla, como pide el alcance.
const FORMATION_EMPLOYEES = [
  { id: 'E0023', doc: '42.221.087', name: 'Carolina Restrepo S.', cargo: 'Jefe de RRHH', cargoId: 'CGO-014', area: 'Gestión Humana', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-ADM-001', lineaNegocio: 'Corporativo', proceso: 'Gestión Humana', subproceso: 'Talento y cultura', genero: 'F', fechaIngreso: '2019-02-02', jefeInmediato: 'Diana Isabel Ruiz Galvis', director: 'Diana Isabel Ruiz Galvis · Directora de GH', celular: '3114567821', correo: 'carolina.restrepo@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Directivo', frecuenciaReentrenoMeses: null, tipoContrato: 'Directo' },
  { id: 'E0067', doc: '16.443.221', name: 'Luis Miguel Marín', cargo: 'Líder de Aprovechamiento', cargoId: 'CGO-013', area: 'Aprovechamiento', sociedad: 'ECOENTORNO', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-001', lineaNegocio: 'Aprovechamiento', proceso: 'Operaciones', subproceso: 'Supervisión de planta', genero: 'M', fechaIngreso: '2021-04-11', jefeInmediato: 'Andrés Camilo Botero Salazar', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3123456789', correo: 'luis.marin@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Liderazgo', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo' },
  { id: 'E0247', doc: '1.087.456.231', name: 'Andrés Felipe Mejía', cargo: 'Operario de Clasificación', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'M', fechaIngreso: '2026-04-28', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3114567821', correo: 'andres.mejia@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0246', doc: '1.094.221.087', name: 'Laura Gómez Restrepo', cargo: 'Operaria de Planta', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'F', fechaIngreso: '2026-03-01', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3011234567', correo: 'laura.gomez@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo',
    historialContractual: [
      { tipo: 'Temporal', empresa: 'Tempocali S.A.S.', desde: '2025-11-01', hasta: '2026-02-28' },
      { tipo: 'Directo', empresa: 'Atica · ASEI', desde: '2026-03-01', hasta: null },
    ] },
  { id: 'E0177', doc: '75.221.487', name: 'Jorge Iván Patiño', cargo: 'Conductor', cargoId: 'CGO-009', area: 'Recolección', sociedad: 'ASEI', regional: 'Regional Caribe', planta: 'Barranquilla', centroCosto: 'CC-LOG-002', lineaNegocio: 'Logística', proceso: 'Logística', subproceso: 'Recolección y transporte', genero: 'M', fechaIngreso: '2023-06-11', jefeInmediato: 'María Camila Duarte Rincón', director: 'María Camila Duarte Rincón · Directora de Logística', celular: '3187765432', correo: 'jorge.patino@atica.co', estadoLaboral: 'Retirado', fechaRetiro: '2026-04-10', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo' },

  { id: 'E0401', doc: '1.098.221.665', name: 'María Camila Duarte Rincón', cargo: 'Directora de Logística', cargoId: 'CGO-012', area: 'Logística', sociedad: 'ECOLOGISTICA', regional: 'Regional Caribe', planta: 'Barranquilla', centroCosto: 'CC-LOG-001', lineaNegocio: 'Logística', proceso: 'Logística', subproceso: 'Dirección de logística', genero: 'F', fechaIngreso: '2020-02-10', jefeInmediato: 'Ricardo Andrés Fajardo Uribe', director: 'Ricardo Andrés Fajardo Uribe · CEO', celular: '3057789231', correo: 'camila.duarte@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Directivo', frecuenciaReentrenoMeses: null, tipoContrato: 'Directo' },
  { id: 'E0402', doc: '1.098.443.221', name: 'Diego Andrés Toro', cargo: 'Aux. Aprovechamiento', cargoId: 'CGO-003', area: 'Aprovechamiento', sociedad: 'ECOENTORNO', regional: 'Regional Occidente', planta: 'Yumbo', centroCosto: 'CC-APR-006', lineaNegocio: 'Aprovechamiento', proceso: 'Operaciones', subproceso: 'Aprovechamiento', genero: 'M', fechaIngreso: '2023-03-20', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3134455667', correo: 'diego.toro@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0403', doc: '1.087.665.443', name: 'Paula Valencia Ríos', cargo: 'Téc. Mantenimiento', cargoId: 'CGO-006', area: 'Mantenimiento', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-MNT-004', lineaNegocio: 'Mantenimiento', proceso: 'Operaciones', subproceso: 'Mantenimiento', genero: 'F', fechaIngreso: '2025-02-03', jefeInmediato: 'Andrés Camilo Botero Salazar', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3145566778', correo: 'paula.valencia@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Técnico', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo' },
  { id: 'E0404', doc: '1.067.443.221', name: 'Nicolás Cárdenas', cargo: 'Conductor', cargoId: 'CGO-009', area: 'Recolección', sociedad: 'ASEI', regional: 'Regional Caribe', planta: 'Barranquilla', centroCosto: 'CC-LOG-002', lineaNegocio: 'Logística', proceso: 'Logística', subproceso: 'Recolección y transporte', genero: 'M', fechaIngreso: '2024-09-05', jefeInmediato: 'María Camila Duarte Rincón', director: 'María Camila Duarte Rincón · Directora de Logística', celular: '3156677889', correo: 'nicolas.cardenas@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo',
    cambioCargo: { fecha: '2026-04-25', cargoAnterior: 'Aux. Aprovechamiento', cargoNuevo: 'Conductor', plantaAnterior: 'Yumbo', plantaNueva: 'Barranquilla' } },
  { id: 'E0405', doc: '10.087.231.456', name: 'Carlos Andrés Vélez', cargo: 'Operario de Clasificación', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'M', fechaIngreso: '2024-03-14', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3167788990', correo: 'carlos.velez@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0406', doc: '1.076.554.332', name: 'Kevin Steven Guerrero', cargo: 'Operario de Clasificación', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'M', fechaIngreso: '2023-08-01', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3178899001', correo: 'kevin.guerrero@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0407', doc: '1.076.221.445', name: 'Tatiana Marcela Sánchez', cargo: 'Op. Aprovechamiento', cargoId: 'CGO-003', area: 'Aprovechamiento', sociedad: 'ECOENTORNO', regional: 'Regional Occidente', planta: 'Yumbo', centroCosto: 'CC-APR-006', lineaNegocio: 'Aprovechamiento', proceso: 'Operaciones', subproceso: 'Aprovechamiento', genero: 'F', fechaIngreso: '2022-11-08', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3189900112', correo: 'tatiana.sanchez@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0408', doc: '1.093.221.876', name: 'Camilo Andrés Salazar', cargo: 'Op. Aprovechamiento', cargoId: 'CGO-003', area: 'Aprovechamiento', sociedad: 'ECOENTORNO', regional: 'Regional Occidente', planta: 'Yumbo', centroCosto: 'CC-APR-006', lineaNegocio: 'Aprovechamiento', proceso: 'Operaciones', subproceso: 'Aprovechamiento', genero: 'M', fechaIngreso: '2024-01-22', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3190011223', correo: 'camilo.salazar@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0409', doc: '1.045.667.882', name: 'Valentina Duarte Rincón', cargo: 'Aux. Administrativo', cargoId: 'CGO-014', area: 'Administración', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-ADM-050', lineaNegocio: 'Corporativo', proceso: 'Gestión Humana', subproceso: 'Administración', genero: 'F', fechaIngreso: '2025-07-14', jefeInmediato: 'Carolina Restrepo S.', director: 'Diana Isabel Ruiz Galvis · Directora de GH', celular: '3201122334', correo: 'valentina.duarte@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo' },
  { id: 'E0410', doc: '1.098.554.221', name: 'Esteban Ossa', cargo: 'Op. Clasificación', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'M', fechaIngreso: '2026-05-05', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3212233445', correo: 'esteban.ossa@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0411', doc: '1.087.332.556', name: 'Karen Rentería', cargo: 'Aux. Bodega', cargoId: 'CGO-004', area: 'Operaciones', sociedad: 'ASEI', regional: 'Regional Occidente', planta: 'Cali', centroCosto: 'CC-OP-010', lineaNegocio: 'Operaciones', proceso: 'Operaciones', subproceso: 'Clasificación de residuos', genero: 'F', fechaIngreso: '2026-04-08', jefeInmediato: 'Luis Miguel Marín', director: 'Andrés Camilo Botero Salazar · Director de Operaciones', celular: '3223344556', correo: 'karen.renteria@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 6, tipoContrato: 'Directo' },
  { id: 'E0412', doc: '1.076.998.213', name: 'Jhonatan Herrera', cargo: 'Conductor Recolección', cargoId: 'CGO-009', area: 'Recolección', sociedad: 'ASEI', regional: 'Regional Caribe', planta: 'Barranquilla', centroCosto: 'CC-LOG-002', lineaNegocio: 'Logística', proceso: 'Logística', subproceso: 'Recolección y transporte', genero: 'M', fechaIngreso: '2026-04-04', jefeInmediato: 'María Camila Duarte Rincón', director: 'María Camila Duarte Rincón · Directora de Logística', celular: '3234455667', correo: 'jhonatan.herrera@atica.co', estadoLaboral: 'Activo', nivelCargo: 'Operativo', frecuenciaReentrenoMeses: 12, tipoContrato: 'Directo' },
];
const getFormationEmployee = (id) => FORMATION_EMPLOYEES.find(e => e.id === id);
const findFormationEmployeeByDoc = (doc) => FORMATION_EMPLOYEES.find(e => e.doc.replace(/[.\s]/g, '') === (doc || '').replace(/[.\s]/g, ''));

// ─────────────────────────────────────────── 6.1 · Inducción y reinducción ─────────
// Un registro por persona. `induccion` = seguimiento del ingreso actual.
// `reinduccion` = ciclo anual (incluye `historial` con ciclos previos).
const FORMATION_INDUCCION = {
  E0247: { soportes: 'Pendiente', estado: 'Pendiente por revisar', actualizadoEn: '2026-04-29' },
  E0246: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2026-03-05' },
  E0401: { soportes: 'No', estado: 'Corrieron la fecha de ingreso', actualizadoEn: '2026-02-15', notaFechaCorrida: 'Ingreso reprogramado del 15/01/2026 al 10/02/2026 por disponibilidad de planta.' },
  E0410: { soportes: 'Pendiente', estado: 'Pendiente por revisar', actualizadoEn: '2026-05-05' },
  E0411: { soportes: 'No', estado: 'Pendiente por revisar', actualizadoEn: '2026-04-09' },
  E0412: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2026-04-06' },
  E0409: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2025-07-16' },
  E0403: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2025-02-05' },
  E0402: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2023-03-22' },
  E0404: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2024-09-07' },
  E0405: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2024-03-16' },
  E0406: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2023-08-03' },
  E0407: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2022-11-10' },
  E0408: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2024-01-24' },
  E0177: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2023-06-13' },
  E0067: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2021-04-13' },
  E0023: { soportes: 'Sí', estado: 'Ejecutado', actualizadoEn: '2019-02-04' },
};

const FORMATION_REINDUCCION = {
  E0067: { ultimaEjecutada: '2026-04-01', certificadoDescargado: 'Sí', historial: [{ anio: 2025, fecha: '2025-04-05' }, { anio: 2026, fecha: '2026-04-01' }] },
  E0402: { ultimaEjecutada: '2024-03-10', certificadoDescargado: 'Sí', historial: [{ anio: 2023, fecha: '2023-03-20' }, { anio: 2024, fecha: '2024-03-10' }] }, // vencida (>1 año antes de hoy)
  E0403: { ultimaEjecutada: '2025-05-20', certificadoDescargado: 'No', historial: [{ anio: 2025, fecha: '2025-05-20' }] }, // próxima a vencer: vence 2026-05-20, 15 días después de hoy
  E0405: { ultimaEjecutada: '2025-11-20', certificadoDescargado: 'Sí', historial: [{ anio: 2024, fecha: '2024-11-18' }, { anio: 2025, fecha: '2025-11-20' }] }, // completa
  E0177: { ultimaEjecutada: '2025-06-15', certificadoDescargado: 'Sí', historial: [{ anio: 2024, fecha: '2024-06-14' }, { anio: 2025, fecha: '2025-06-15' }] },
};

const calcVencimientoReinduccion = (fechaIngresoISO, ultimaReinduccionISO) => _fd_addYears(ultimaReinduccionISO || fechaIngresoISO, 1);
const clasificarEstadoReinduccion = (vencimientoISO, hoyISO = FORMATION_DEMO_TODAY) => {
  const dias = _fd_daysBetween(hoyISO, vencimientoISO);
  if (dias < 0) return 'vencida';
  if (dias <= FORMATION_REINDUCCION_DIAS_ALERTA) return 'proxima';
  return 'completa';
};

const calcConsolidadoInduccion = (mesISO, empleados = FORMATION_EMPLOYEES, registros = FORMATION_INDUCCION) => {
  const ingresos = empleados.filter(e => _fd_isSameMonth(e.fechaIngreso, mesISO));
  const realizadas = ingresos.filter((e) => {
    const r = registros[e.id];
    return r && (r.estado === 'Ejecutado' || r.estado === 'Corrieron la fecha de ingreso');
  });
  const pct = ingresos.length > 0 ? Math.round((realizadas.length / ingresos.length) * 100) : 0;
  return { mes: mesISO, ingresos: ingresos.length, realizadas: realizadas.length, pct };
};

// ─────────────────────────────────────────── 6.2 · Entrenamiento al cargo ─────────
// Temas–Cargo: qué temas debe cubrir cada cargo (alimenta GH-FO-24 y la
// matriz). Independiente de COMPETENCIAS_MAESTRAS (esa es la matriz ICONTEC
// de competencias legales; esta es la matriz operativa "entrenamiento en el
// puesto", con temas más prácticos — máquina, procedimiento, EPP puntual).
const FORMATION_TEMAS_CARGO = [
  { id: 'TC-01', cargoId: 'CGO-004', cargo: 'Operario de Clasificación', tema: 'Operación segura de banda transportadora', horas: 4, obligatorio: true, version: 3, fechaVigencia: '2026-02-01', responsable: 'Luis Miguel Marín' },
  { id: 'TC-02', cargoId: 'CGO-004', cargo: 'Operario de Clasificación', tema: 'Clasificación de material peligroso', horas: 2, obligatorio: true, version: 3, fechaVigencia: '2026-02-01', responsable: 'Luis Miguel Marín' },
  { id: 'TC-03', cargoId: 'CGO-004', cargo: 'Operario de Clasificación', tema: 'Uso de EPP térmico', horas: 1, obligatorio: true, version: 2, fechaVigencia: '2025-06-01', responsable: 'Luis Miguel Marín' },
  { id: 'TC-04', cargoId: 'CGO-003', cargo: 'Op. Aprovechamiento', tema: 'Operación de prensa compactadora', horas: 3, obligatorio: true, version: 3, fechaVigencia: '2026-02-01', responsable: 'Luis Miguel Marín' },
  { id: 'TC-05', cargoId: 'CGO-003', cargo: 'Op. Aprovechamiento', tema: 'Separación de material reciclable', horas: 2, obligatorio: true, version: 3, fechaVigencia: '2026-02-01', responsable: 'Luis Miguel Marín' },
  { id: 'TC-06', cargoId: 'CGO-006', cargo: 'Téc. Mantenimiento', tema: 'Bloqueo y etiquetado LOTO', horas: 4, obligatorio: true, version: 2, fechaVigencia: '2025-09-01', responsable: 'Andrés Camilo Botero Salazar' },
  { id: 'TC-07', cargoId: 'CGO-006', cargo: 'Téc. Mantenimiento', tema: 'Trabajo seguro en alturas', horas: 8, obligatorio: true, version: 2, fechaVigencia: '2025-09-01', responsable: 'Andrés Camilo Botero Salazar' },
  { id: 'TC-08', cargoId: 'CGO-009', cargo: 'Conductor', tema: 'Manejo defensivo y PESV', horas: 6, obligatorio: true, version: 3, fechaVigencia: '2026-02-01', responsable: 'María Camila Duarte Rincón' },
  { id: 'TC-09', cargoId: 'CGO-009', cargo: 'Conductor', tema: 'Manipulación de cargas', horas: 2, obligatorio: false, version: 3, fechaVigencia: '2026-02-01', responsable: 'María Camila Duarte Rincón' },
  { id: 'TC-10', cargoId: 'CGO-013', cargo: 'Supervisor de Planta', tema: 'Liderazgo de equipos operativos', horas: 8, obligatorio: false, version: 1, fechaVigencia: '2024-05-01', responsable: 'Andrés Camilo Botero Salazar' },
];

const FORMATION_MATRIZ_VERSIONES = [
  { version: 3, fecha: '2026-02-01', responsable: 'Luis Miguel Marín', cargosAfectados: ['Operario de Clasificación', 'Op. Aprovechamiento', 'Conductor'], nota: 'Se agregó "Clasificación de material peligroso" y se actualizaron horas de banda transportadora.' },
  { version: 2, fecha: '2025-09-01', responsable: 'Andrés Camilo Botero Salazar', cargosAfectados: ['Téc. Mantenimiento', 'Operario de Clasificación'], nota: 'Se incorporó LOTO y trabajo seguro en alturas para mantenimiento.' },
  { version: 1, fecha: '2024-05-01', responsable: 'Diana Isabel Ruiz Galvis', cargosAfectados: ['Todos los cargos operativos'], nota: 'Primera versión formal de Temas–Cargo, migrada desde el archivo físico de planta.' },
];

// Entrenamientos ejecutados / pendientes por persona — una fila por ciclo.
// vencimiento = fechaEjecutada + frecuenciaReentrenoMeses de la persona.
const FORMATION_ENTRENAMIENTOS = [
  { id: 'ENT-001', empId: 'E0405', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Sí', entrenamientoMaquinas: 'Sí', estado: 'Ejecutado', soportesPendientes: 'No', fechaEjecutada: '2026-03-01', horasEjecutadas: 6, maquina: 'Banda transportadora L2', fechaMaquina: '2026-03-02', horasMaquina: 2, alerta: 'vigente', auditoria: 'No', seguimientoGH: 'Cerrado' },
  { id: 'ENT-002', empId: 'E0406', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Sí', entrenamientoMaquinas: 'Sí', estado: 'Ejecutado', soportesPendientes: 'No', fechaEjecutada: '2025-10-15', horasEjecutadas: 6, maquina: 'Banda transportadora L1', fechaMaquina: '2025-10-16', horasMaquina: 2, alerta: 'vencida', auditoria: 'No', seguimientoGH: 'Cerrado' },
  { id: 'ENT-003', empId: 'E0247', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Pendiente', entrenamientoMaquinas: 'Pendiente', estado: 'Pendiente por ejecutar', soportesPendientes: 'Sí', fechaEjecutada: null, horasEjecutadas: null, maquina: 'Banda transportadora L2', fechaMaquina: null, horasMaquina: null, alerta: 'pendiente', auditoria: 'No', seguimientoGH: 'Abierto' },
  { id: 'ENT-004', empId: 'E0404', motivo: 'Cambio de cargo', entrenamientoCargo: 'Pendiente', entrenamientoMaquinas: 'No aplica', estado: 'Pendiente por ejecutar', soportesPendientes: 'Sí', fechaEjecutada: null, horasEjecutadas: null, maquina: 'Vehículo recolector R-14', fechaMaquina: null, horasMaquina: null, alerta: 'pendiente', auditoria: 'Sí', seguimientoGH: 'Abierto', fechaCambio: '2026-04-25' },
  { id: 'ENT-004H', empId: 'E0404', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Sí', entrenamientoMaquinas: 'Sí', estado: 'Ejecutado', soportesPendientes: 'No', fechaEjecutada: '2024-09-08', horasEjecutadas: 3, maquina: 'Prensa compactadora P3', fechaMaquina: '2024-09-09', horasMaquina: 1, alerta: 'vencida', auditoria: 'No', seguimientoGH: 'Cerrado' },
  { id: 'ENT-005', empId: 'E0402', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Sí', entrenamientoMaquinas: 'Sí', estado: 'Ejecutado', soportesPendientes: 'No', fechaEjecutada: '2023-03-21', horasEjecutadas: 5, maquina: 'Prensa compactadora P1', fechaMaquina: '2023-03-22', horasMaquina: 1, alerta: 'vencida', auditoria: 'No', seguimientoGH: 'Cerrado' },
  { id: 'ENT-006', empId: 'E0403', motivo: 'Ingreso nuevo', entrenamientoCargo: 'Sí', entrenamientoMaquinas: 'No aplica', estado: 'Ejecutado', soportesPendientes: 'No', fechaEjecutada: '2025-09-01', horasEjecutadas: 12, maquina: null, fechaMaquina: null, horasMaquina: null, alerta: 'vigente', auditoria: 'No', seguimientoGH: 'Cerrado' },
];

const calcVencimientoEntrenamiento = (fechaEjecutadaISO, frecuenciaMeses) => (fechaEjecutadaISO && frecuenciaMeses) ? _fd_addMonths(fechaEjecutadaISO, frecuenciaMeses) : null;
const clasificarAlertaEntrenamiento = (fechaEjecutadaISO, frecuenciaMeses, hoyISO = FORMATION_DEMO_TODAY) => {
  if (!fechaEjecutadaISO) return 'pendiente';
  const venc = calcVencimientoEntrenamiento(fechaEjecutadaISO, frecuenciaMeses);
  if (!venc) return 'vigente';
  const dias = _fd_daysBetween(hoyISO, venc);
  if (dias < 0) return 'vencida';
  if (dias <= 45) return 'proxima';
  return 'vigente';
};

// Reentrenamientos — cola generada a partir de FORMATION_ENTRENAMIENTOS
// vencidos/próximos + registros ya completados con su próximo ciclo.
const FORMATION_REENTRENAMIENTOS = [
  { id: 'REENT-001', empId: 'E0407', origenEntrenamientoId: null, estado: 'Ejecutado', motivo: 'Vencimiento de periodo', novedad: 'Sin novedad', soportesPendientes: 'No', horasEjecutadas: 4, maquina: 'Prensa compactadora P2', fechaEjecutada: '2025-11-10', horasPractica: 2, fechaPractica: '2025-11-11', auditoria: 'No', seguimientoGH: 'Cerrado' },
  { id: 'REENT-002', empId: 'E0406', origenEntrenamientoId: 'ENT-002', estado: 'Pendiente por ejecutar', motivo: 'Vencimiento de periodo', novedad: 'Sin novedad', soportesPendientes: 'Sí', horasEjecutadas: null, maquina: 'Banda transportadora L1', fechaEjecutada: null, horasPractica: null, fechaPractica: null, auditoria: 'No', seguimientoGH: 'Abierto' },
  { id: 'REENT-003', empId: 'E0402', origenEntrenamientoId: 'ENT-005', estado: 'Pendiente por ejecutar', motivo: 'Vencimiento de periodo', novedad: 'Reprogramado por incapacidad', soportesPendientes: 'Sí', horasEjecutadas: null, maquina: 'Prensa compactadora P1', fechaEjecutada: null, horasPractica: null, fechaPractica: null, auditoria: 'No', seguimientoGH: 'Abierto' },
];
const calcProximoReentrenamiento = (fechaEjecutadaISO, frecuenciaMeses) => calcVencimientoEntrenamiento(fechaEjecutadaISO, frecuenciaMeses);

// ─────────────────────────────────────────── 6.3 · Capacitación interna ─────────
const FORMATION_PROPUESTAS_INTERNAS = [
  { id: 'PRI-001', proceso: 'SST', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', publico: ['Operario de Clasificación'], objetivo: 'Reducir incidentes en línea de clasificación', estado: 'Aprobada', creadoPor: 'Luis Miguel Marín', material: { tipo: 'PDF', nombre: 'prevencion-banda.pdf', tamKB: 842 }, preguntas: [
    { enunciado: '¿Cuál es el primer paso ante una obstrucción en la banda?', opciones: ['Detener y bloquear', 'Empujar con la mano', 'Ignorar y seguir', 'Avisar al final del turno'], correcta: 0 },
    { enunciado: '¿Qué EPP es obligatorio en la línea de clasificación?', opciones: ['Ninguno', 'Guantes térmicos y botas dieléctricas', 'Solo casco', 'Solo tapabocas'], correcta: 1 },
  ] },
  { id: 'PRI-002', proceso: 'Ambiental', tema: 'Manejo de residuos peligrosos', mes: '2026-05', publico: ['Op. Aprovechamiento'], objetivo: 'Cumplir con la normativa ambiental vigente', estado: 'En revisión', creadoPor: 'Luis Miguel Marín', material: { tipo: 'Video', nombre: 'manejo-respel.mp4', tamKB: 15230 }, preguntas: [
    { enunciado: '¿Cómo se deben separar los residuos peligrosos?', opciones: ['Junto con los ordinarios', 'Por categoría y rotulados', 'Sin rotular', 'En cualquier recipiente'], correcta: 1 },
  ] },
  { id: 'PRI-003', proceso: 'Calidad', tema: 'Control de calidad en aprovechamiento', mes: '2026-06', publico: ['Op. Aprovechamiento', 'Operario de Clasificación'], objetivo: 'Estandarizar criterios de calidad del material recuperado', estado: 'Borrador', creadoPor: 'Carolina Restrepo S.', material: null, preguntas: [] },
];

// Seguimiento por persona — una fila = una asignación. Separa estadoAcademico
// (Inscrito/Pendiente por ejecutar/Aprobado) de estadoOperativo
// (Planeado/Ejecutado/Retirado/Pendiente por ejecutar).
const FORMATION_SEGUIMIENTO_INTERNO = [
  { id: 'SGI-001', empId: 'E0247', propuestaId: 'PRI-001', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', estadoAcademico: 'Inscrito', estadoOperativo: 'Planeado', fechaEjecucion: null, nota: null, soporte: null, intentos: [] },
  { id: 'SGI-002', empId: 'E0405', propuestaId: 'PRI-001', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', estadoAcademico: 'Aprobado', estadoOperativo: 'Ejecutado', fechaEjecucion: '2026-05-02', nota: 92, soporte: { tipo: 'diploma', nombre: 'diploma-carlos-velez.pdf' }, intentos: [{ fecha: '2026-05-02', nota: 92, resultado: 'Aprobado' }] },
  { id: 'SGI-003', empId: 'E0408', propuestaId: 'PRI-001', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', estadoAcademico: 'Pendiente por ejecutar', estadoOperativo: 'Ejecutado', fechaEjecucion: '2026-05-03', nota: 38, soporte: null, intentos: [{ fecha: '2026-05-03', nota: 38, resultado: 'Reprobado' }] },
  { id: 'SGI-004', empId: 'E0246', propuestaId: 'PRI-001', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', estadoAcademico: 'Aprobado', estadoOperativo: 'Ejecutado', fechaEjecucion: '2026-05-04', nota: 88, soporte: { tipo: 'diploma', nombre: 'diploma-laura-gomez.pdf' }, intentos: [{ fecha: '2026-05-04', nota: 88, resultado: 'Aprobado' }] },
  { id: 'SGI-005', empId: 'E0177', propuestaId: 'PRI-001', tema: 'Prevención de accidentes en banda transportadora', mes: '2026-05', estadoAcademico: 'Aprobado', estadoOperativo: 'Retirado', fechaEjecucion: '2026-04-08', nota: 95, soporte: { tipo: 'diploma', nombre: 'diploma-jorge-patino.pdf' }, intentos: [{ fecha: '2026-04-08', nota: 95, resultado: 'Aprobado' }] },
];

// ─────────────────────────────────────────── 6.4 · Capacitograma (catálogo maestro) ─────────
const CAPACITOGRAMA_TIPOS = ['Interno', 'Externo', 'Inducción', 'Entrenamiento', 'Reentrenamiento'];
const FORMATION_CAPACITOGRAMA = [
  { id: 'CGR-001', codigo: 'CGR-SST-001', categoria: 'SST', curso: 'Trabajo seguro en alturas', tipo: 'Externo', cargosAplicables: ['Téc. Mantenimiento'], duracionHoras: 8, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: true, periodicidadExamenMeses: 12, soportesObligatorios: ['Certificado del proveedor', 'Examen médico de alturas'], entidadRegulatoria: 'Ministerio de Trabajo', activo: true, version: 2, fechaVigencia: '2025-09-01' },
  { id: 'CGR-002', codigo: 'CGR-SST-002', categoria: 'SST', curso: 'Manejo defensivo y PESV', tipo: 'Externo', cargosAplicables: ['Conductor'], duracionHoras: 6, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Certificado del proveedor'], entidadRegulatoria: 'Ministerio de Transporte', activo: true, version: 3, fechaVigencia: '2026-02-01' },
  { id: 'CGR-003', codigo: 'CGR-AMB-001', categoria: 'Ambiental', curso: 'Manejo de residuos peligrosos', tipo: 'Interno', cargosAplicables: ['Op. Aprovechamiento'], duracionHoras: 2, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: true, periodicidadExamenMeses: 12, soportesObligatorios: ['Evaluación interna'], entidadRegulatoria: 'Autoridad Ambiental Regional', activo: true, version: 1, fechaVigencia: '2026-01-15' },
  { id: 'CGR-004', codigo: 'CGR-IND-001', categoria: 'GH', curso: 'Inducción general Atica', tipo: 'Inducción', cargosAplicables: ['Todos los cargos'], duracionHoras: 6, frecuenciaMeses: null, vigenciaMeses: null, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Lista de asistencia'], entidadRegulatoria: 'Política interna', activo: true, version: 4, fechaVigencia: '2026-01-01' },
  { id: 'CGR-005', codigo: 'CGR-IND-002', categoria: 'GH', curso: 'Reinducción anual', tipo: 'Inducción', cargosAplicables: ['Todos los cargos'], duracionHoras: 3, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Lista de asistencia'], entidadRegulatoria: 'Política interna', activo: true, version: 2, fechaVigencia: '2025-05-01' },
  { id: 'CGR-006', codigo: 'CGR-ENT-001', categoria: 'Operaciones', curso: 'Entrenamiento al cargo — Operario de Clasificación', tipo: 'Entrenamiento', cargosAplicables: ['Operario de Clasificación'], duracionHoras: 6, frecuenciaMeses: 6, vigenciaMeses: 6, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Formato GH-FO-24'], entidadRegulatoria: 'Interno', activo: true, version: 3, fechaVigencia: '2026-02-01' },
  { id: 'CGR-007', codigo: 'CGR-REENT-001', categoria: 'Operaciones', curso: 'Reentrenamiento — Op. Aprovechamiento', tipo: 'Reentrenamiento', cargosAplicables: ['Op. Aprovechamiento'], duracionHoras: 4, frecuenciaMeses: 6, vigenciaMeses: 6, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Formato GH-FO-24'], entidadRegulatoria: 'Interno', activo: true, version: 3, fechaVigencia: '2026-02-01' },
  { id: 'CGR-008', codigo: 'CGR-CUMP-001', categoria: 'Cumplimiento', curso: 'Habeas data y protección de datos', tipo: 'Interno', cargosAplicables: ['Todos los cargos administrativos'], duracionHoras: 1, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: true, periodicidadExamenMeses: 12, soportesObligatorios: ['Evaluación interna'], entidadRegulatoria: 'Ley 1581 de 2012', activo: true, version: 1, fechaVigencia: '2025-11-01' },
  { id: 'CGR-009', codigo: 'CGR-RIE-001', categoria: 'Gestión del Riesgo', curso: 'Identificación de peligros y evaluación de riesgos', tipo: 'Interno', cargosAplicables: ['Líder de Aprovechamiento', 'Supervisor de Planta'], duracionHoras: 4, frecuenciaMeses: 24, vigenciaMeses: 24, aplicaExamen: false, periodicidadExamenMeses: null, soportesObligatorios: ['Lista de asistencia'], entidadRegulatoria: 'Decreto 1072 de 2015', activo: true, version: 1, fechaVigencia: '2025-03-01' },
  { id: 'CGR-010', codigo: 'CGR-SST-003', categoria: 'SST', curso: 'Brigada de emergencias y primeros auxilios', tipo: 'Externo', cargosAplicables: ['Todos los cargos operativos'], duracionHoras: 16, frecuenciaMeses: 12, vigenciaMeses: 12, aplicaExamen: true, periodicidadExamenMeses: 12, soportesObligatorios: ['Certificado del proveedor'], entidadRegulatoria: 'ARL', activo: false, version: 1, fechaVigencia: '2024-06-01' },
];

const obligacionesFormativasPorCargo = (cargoLabel) => FORMATION_CAPACITOGRAMA.filter(c => c.activo && (
  c.cargosAplicables.includes(cargoLabel) || c.cargosAplicables.includes('Todos los cargos') || c.cargosAplicables.includes('Todos los cargos operativos')
));

// ─────────────────────────────────────────── 6.5 · Capacitación externa ─────────
const EXTERNAL_CATEGORIAS = [...new Set(FORMATION_CAPACITOGRAMA.filter(c => c.tipo === 'Externo').map(c => c.categoria))];
const cursosExternosPorCategoria = (categoria) => FORMATION_CAPACITOGRAMA.filter(c => c.tipo === 'Externo' && c.categoria === categoria && c.activo);

const FORMATION_SOLICITUDES_EXTERNAS = [
  { id: 'SOLX-001', empId: 'E0403', capacitogramaId: 'CGR-001', categoria: 'SST', curso: 'Trabajo seguro en alturas', solicitadoPor: 'E0067', fecha: '2026-04-10', estado: 'Aprobado', comentario: null },
  { id: 'SOLX-002', empId: 'E0411', capacitogramaId: 'CGR-002', categoria: 'SST', curso: 'Manejo defensivo y PESV', solicitadoPor: 'E0067', fecha: '2026-04-18', estado: 'Devuelto', comentario: 'Falta cotización actualizada del proveedor. Adjúntala y reenvía.' },
  { id: 'SOLX-003', empId: 'E0412', capacitogramaId: 'CGR-002', categoria: 'SST', curso: 'Manejo defensivo y PESV', solicitadoPor: 'E0401', fecha: '2026-04-20', estado: 'Rechazado', comentario: 'El colaborador ya tomó este curso hace 4 meses; no aplica renovación anticipada.' },
];

// Seguimiento y costos — un registro por persona con curso externo activo.
// El examen y la periodicidad se heredan del Capacitograma; lo demás es
// gestión académica/financiera propia del seguimiento.
const FORMATION_SEGUIMIENTO_EXTERNO = [
  {
    id: 'SGX-001', empId: 'E0403', solicitudId: 'SOLX-001', capacitogramaId: 'CGR-001',
    examenEstado: 'Apto', fechaExamen: '2026-04-29', estadoSolicitud: 'Vigente',
    horas: 8, anio: 2026, fechaRealizacion: '2026-04-30', fechaVencimiento: '2027-04-30', empresaCertificadora: 'Consejo Colombiano de Seguridad',
    valorCurso: 850000, valorColaborador: 0, autorizaDescuento: 'No', reportaNomina: 'No', centroCosto: 'CC-MNT-004', costeo: 'Presupuesto SST 2026', ordenCompra: 'OC-2026-0142', liberacionOC: 'Sí', proveedor: 'Consejo Colombiano de Seguridad', factura: 'FAC-CCS-3321', comentarios: 'Certificación vigente por 12 meses desde la fecha de realización.',
    soportes: [{ tipo: 'Certificado del proveedor', nombre: 'certificado-alturas-paula.pdf', estado: 'Cargado' }, { tipo: 'Evidencia Ministerio de Trabajo', nombre: 'registro-mintrabajo-paula.pdf', estado: 'Cargado' }],
  },
  {
    id: 'SGX-002', empId: 'E0402', solicitudId: null, capacitogramaId: 'CGR-001',
    examenEstado: 'En validación de SST', fechaExamen: '2026-05-03', estadoSolicitud: 'En exámenes',
    horas: 8, anio: 2026, fechaRealizacion: '2026-05-02', fechaVencimiento: null, empresaCertificadora: 'Consejo Colombiano de Seguridad',
    valorCurso: 850000, valorColaborador: 0, autorizaDescuento: 'No', reportaNomina: 'No', centroCosto: 'CC-APR-006', costeo: 'Presupuesto SST 2026', ordenCompra: 'OC-2026-0151', liberacionOC: 'No', proveedor: 'Consejo Colombiano de Seguridad', factura: null, comentarios: 'A la espera de validación del examen médico de alturas por parte de SST antes de emitir el certificado.',
    soportes: [{ tipo: 'Certificado del proveedor', nombre: null, estado: 'Pendiente' }],
  },
  {
    id: 'SGX-003', empId: 'E0401', solicitudId: null, capacitogramaId: 'CGR-001',
    examenEstado: 'No aplica', fechaExamen: null, estadoSolicitud: 'Próximo a vencer',
    horas: 8, anio: 2025, fechaRealizacion: '2025-06-05', fechaVencimiento: '2026-06-05', empresaCertificadora: 'Consejo Colombiano de Seguridad',
    valorCurso: 820000, valorColaborador: 0, autorizaDescuento: 'No', reportaNomina: 'No', centroCosto: 'CC-LOG-001', costeo: 'Presupuesto SST 2025', ordenCompra: 'OC-2025-0298', liberacionOC: 'Sí', proveedor: 'Consejo Colombiano de Seguridad', factura: 'FAC-CCS-2987', comentarios: 'Renovación pendiente de programar.',
    soportes: [{ tipo: 'Certificado del proveedor', nombre: null, estado: 'Pendiente' }],
  },
  {
    id: 'SGX-004', empId: 'E0246', solicitudId: null, capacitogramaId: 'CGR-002',
    examenEstado: 'No aplica', fechaExamen: null, estadoSolicitud: 'Vigente',
    horas: 6, anio: 2026, fechaRealizacion: '2026-01-20', fechaVencimiento: '2027-01-20', empresaCertificadora: 'Tempocali S.A.S. (proveedor temporal)',
    valorCurso: 380000, valorColaborador: 0, autorizaDescuento: 'No', reportaNomina: 'No', centroCosto: 'CC-OP-010', costeo: 'Tempocali S.A.S.', ordenCompra: null, liberacionOC: 'No', proveedor: 'Tempocali S.A.S.', factura: 'FAC-TC-0087', comentarios: 'Certificado obtenido mientras el colaborador estaba vinculado como temporal — se conserva tras el paso a contratación directa.',
    soportes: [{ tipo: 'Certificado del proveedor', nombre: 'certificado-pesv-laura-temporal.pdf', estado: 'Cargado' }],
  },
];

// ─────────────────────────────────────────── 6.6 · Historial de envíos (WhatsApp / correo) ─────────
const FORMATION_ENVIOS = [
  { id: 'ENV-001', fecha: '2026-05-01 08:15', canal: 'Correo', destinatarios: 5, asunto: 'Capacitación asignada: Prevención de accidentes en banda transportadora', resultado: 'Enviado' },
  { id: 'ENV-002', fecha: '2026-05-01 08:16', canal: 'WhatsApp', destinatarios: 5, asunto: 'Capacitación asignada: Prevención de accidentes en banda transportadora', resultado: 'Enviado' },
  { id: 'ENV-003', fecha: '2026-05-04 16:40', canal: 'Correo', destinatarios: 1, asunto: 'Recordatorio: capacitación pendiente por ejecutar', resultado: 'Enviado' },
];

// ─────────────────────────────────────────── Supuestos por validar con GH ─────────
const FORMATION_ASSUMPTIONS = [
  { id: 1, texto: 'Si "Corrieron la fecha de ingreso" debe contar como inducción realizada en el consolidado mensual.' },
  { id: 2, texto: 'Si la reinducción anual se calcula desde la fecha de ingreso o desde la última reinducción ejecutada. El demo usa la última ejecutada cuando existe.' },
  { id: 3, texto: `Cuántos días definen "Próxima a vencer". El demo aplica ${FORMATION_REINDUCCION_DIAS_ALERTA} días.` },
  { id: 4, texto: 'Cuáles son las unidades válidas de frecuencia de reentrenamiento (meses vs. años vs. horas trabajadas).' },
  { id: 5, texto: 'Si el vencimiento de cursos externos debe ser automático (Capacitograma) o editable por GH caso a caso.' },
  { id: 6, texto: 'Quién genera y firma los diplomas internos — GH, el líder o el sistema de forma automática.' },
  { id: 7, texto: 'Cuál es el mapeo definitivo de campos del formato GH-FO-24 V7 contra el archivo oficial.' },
  { id: 8, texto: 'Qué integraciones y plantillas se usarán realmente para WhatsApp y correo (proveedor, plantillas aprobadas).' },
];

window.FORMATION_DEMO_TODAY = FORMATION_DEMO_TODAY;
window.FORMATION_REINDUCCION_DIAS_ALERTA = FORMATION_REINDUCCION_DIAS_ALERTA;

// Helpers de fecha
window._fd_addDays = _fd_addDays;
window._fd_addMonths = _fd_addMonths;
window._fd_addYears = _fd_addYears;
window._fd_daysBetween = _fd_daysBetween;
window._fd_isSameMonth = _fd_isSameMonth;
window._fd_labelCO = _fd_labelCO;
window._fd_monthLabelCO = _fd_monthLabelCO;

// Catálogos de estado
window.IND_SOPORTE_OPCIONES = IND_SOPORTE_OPCIONES;
window.IND_ESTADO_OPCIONES = IND_ESTADO_OPCIONES;
window.IND_ESTADO_META = IND_ESTADO_META;
window.REIND_ESTADO_META = REIND_ESTADO_META;
window.JOB_TRAINING_ESTADOS = JOB_TRAINING_ESTADOS;
window.JOB_TRAINING_MOTIVOS = JOB_TRAINING_MOTIVOS;
window.JOB_ALERTA_META = JOB_ALERTA_META;
window.INTERNAL_ACADEMIC_STATES = INTERNAL_ACADEMIC_STATES;
window.INTERNAL_OPERATIONAL_STATES = INTERNAL_OPERATIONAL_STATES;
window.INTERNAL_PROCESOS = INTERNAL_PROCESOS;
window.INTERNAL_PROPUESTA_ESTADOS = INTERNAL_PROPUESTA_ESTADOS;
window.EXTERNAL_SOLICITUD_ESTADOS = EXTERNAL_SOLICITUD_ESTADOS;
window.EXTERNAL_EXAMEN_ESTADOS = EXTERNAL_EXAMEN_ESTADOS;
window.EXTERNAL_SEGUIMIENTO_ESTADOS = EXTERNAL_SEGUIMIENTO_ESTADOS;
window.CAPACITOGRAMA_TIPOS = CAPACITOGRAMA_TIPOS;
window.EXTERNAL_CATEGORIAS = EXTERNAL_CATEGORIAS;

// Helpers de cálculo
window.calcVencimientoReinduccion = calcVencimientoReinduccion;
window.clasificarEstadoReinduccion = clasificarEstadoReinduccion;
window.calcConsolidadoInduccion = calcConsolidadoInduccion;
window.calcVencimientoEntrenamiento = calcVencimientoEntrenamiento;
window.clasificarAlertaEntrenamiento = clasificarAlertaEntrenamiento;
window.calcProximoReentrenamiento = calcProximoReentrenamiento;
window.obligacionesFormativasPorCargo = obligacionesFormativasPorCargo;
window.cursosExternosPorCategoria = cursosExternosPorCategoria;

window.FORMATION_EMPLOYEES = FORMATION_EMPLOYEES;
window.getFormationEmployee = getFormationEmployee;
window.findFormationEmployeeByDoc = findFormationEmployeeByDoc;
window.FORMATION_INDUCCION = FORMATION_INDUCCION;
window.FORMATION_REINDUCCION = FORMATION_REINDUCCION;
window.FORMATION_TEMAS_CARGO = FORMATION_TEMAS_CARGO;
window.FORMATION_MATRIZ_VERSIONES = FORMATION_MATRIZ_VERSIONES;
window.FORMATION_ENTRENAMIENTOS = FORMATION_ENTRENAMIENTOS;
window.FORMATION_REENTRENAMIENTOS = FORMATION_REENTRENAMIENTOS;
window.FORMATION_PROPUESTAS_INTERNAS = FORMATION_PROPUESTAS_INTERNAS;
window.FORMATION_SEGUIMIENTO_INTERNO = FORMATION_SEGUIMIENTO_INTERNO;
window.FORMATION_CAPACITOGRAMA = FORMATION_CAPACITOGRAMA;
window.FORMATION_SOLICITUDES_EXTERNAS = FORMATION_SOLICITUDES_EXTERNAS;
window.FORMATION_SEGUIMIENTO_EXTERNO = FORMATION_SEGUIMIENTO_EXTERNO;
window.FORMATION_ENVIOS = FORMATION_ENVIOS;
window.FORMATION_ASSUMPTIONS = FORMATION_ASSUMPTIONS;
