import { timeToMinutes } from './timeUtils'; import { calculateSingleDoseConcentration } from './pharmacokinetics'; import type { Dose, Deviation, SteadyStateConfig, PkParams, ConcentrationPoint } from '../constants/defaults'; interface DoseWithTime extends Omit { time: number; isPlan?: boolean; } export const calculateCombinedProfile = ( doseSchedule: Dose[], deviationList: Deviation[] = [], correction: Deviation | null = null, steadyStateConfig: SteadyStateConfig, simulationDays: string, pkParams: PkParams ): ConcentrationPoint[] => { const dataPoints: ConcentrationPoint[] = []; const timeStepHours = 0.25; const totalHours = (parseInt(simulationDays, 10) || 3) * 24; const daysToSimulate = Math.min(parseInt(steadyStateConfig.daysOnMedication, 10) || 0, 5); for (let t = 0; t <= totalHours; t += timeStepHours) { let totalLdx = 0; let totalDamph = 0; const allDoses: DoseWithTime[] = []; const maxDayOffset = (parseInt(simulationDays, 10) || 3) - 1; for (let day = -daysToSimulate; day <= maxDayOffset; day++) { const dayOffset = day * 24 * 60; doseSchedule.forEach(d => { // Skip doses with empty or invalid time values const timeStr = String(d.time || '').trim(); const doseStr = String(d.dose || '').trim(); const doseNum = parseFloat(doseStr); if (!timeStr || timeStr === '' || !doseStr || doseStr === '' || doseNum === 0 || isNaN(doseNum)) { return; } allDoses.push({ ...d, time: timeToMinutes(d.time) + dayOffset, isPlan: true }); }); } const currentDeviations = [...deviationList]; if (correction) { currentDeviations.push({ ...correction, isAdditional: true }); } currentDeviations.forEach(dev => { // Skip deviations with empty or invalid time values const timeStr = String(dev.time || '').trim(); const doseStr = String(dev.dose || '').trim(); const doseNum = parseFloat(doseStr); if (!timeStr || timeStr === '' || !doseStr || doseStr === '' || doseNum === 0 || isNaN(doseNum)) { return; } const devTime = timeToMinutes(dev.time) + (dev.dayOffset || 0) * 24 * 60; if (!dev.isAdditional) { const closestDoseIndex = allDoses.reduce((closest, dose, index) => { if (!dose.isPlan) return closest; const diff = Math.abs(dose.time - devTime); if (diff <= 60 && diff < closest.minDiff) { return { index, minDiff: diff }; } return closest; }, { index: -1, minDiff: 61 }).index; if (closestDoseIndex !== -1) { allDoses.splice(closestDoseIndex, 1); } } allDoses.push({ ...dev, time: devTime }); }); allDoses.forEach(doseInfo => { const timeSinceDoseHours = t - doseInfo.time / 60; const concentrations = calculateSingleDoseConcentration(doseInfo.dose, timeSinceDoseHours, pkParams); totalLdx += concentrations.ldx; totalDamph += concentrations.damph; }); dataPoints.push({ timeHours: t, ldx: totalLdx, damph: totalDamph }); } return dataPoints; };