// Calibration helpers for the pumping-station predicted volume / level
// streams. Pure functions over a context bag holding the live
// MeasurementContainer + basin geometry. After every calibration the
// integrator state is reset so the next tick starts from the new anchor.

function _resetFlowState(ctx, timestamp) {
  if (ctx.flowAggregator?.resetState) {
    ctx.flowAggregator.resetState(timestamp);
    return;
  }
  ctx._predictedFlowState = { inflow: 0, outflow: 0, lastTimestamp: timestamp };
}

function _clearSeries(measurements, type) {
  const series = measurements.type(type).variant('predicted').position('atequipment');
  if (series.exists()) {
    const m = series.get();
    if (m) {
      m.values = [];
      m.timestamps = [];
    }
  }
}

function _levelFromVolume(basin, volume) {
  const area = basin.surfaceArea;
  return area > 0 ? Math.max(volume, 0) / area : 0;
}

function _volumeFromLevel(basin, level) {
  const area = basin.surfaceArea;
  return area > 0 ? Math.max(level, 0) * area : 0;
}

function calibratePredictedVolume(ctx, calibratedVol, timestamp = Date.now()) {
  if (!ctx?.measurements || !ctx.basin) {
    throw new Error('calibratePredictedVolume: ctx.measurements and ctx.basin required');
  }
  const { measurements, basin } = ctx;

  _clearSeries(measurements, 'volume');
  _clearSeries(measurements, 'level');

  measurements.type('volume').variant('predicted').position('atequipment')
    .value(calibratedVol, timestamp, 'm3').unit('m3');
  measurements.type('level').variant('predicted').position('atequipment')
    .value(_levelFromVolume(basin, calibratedVol), timestamp, 'm');

  _resetFlowState(ctx, timestamp);
}

function calibratePredictedLevel(ctx, level, timestamp = Date.now(), unit = 'm') {
  if (!ctx?.measurements || !ctx.basin) {
    throw new Error('calibratePredictedLevel: ctx.measurements and ctx.basin required');
  }
  const { measurements, basin } = ctx;

  _clearSeries(measurements, 'volume');
  _clearSeries(measurements, 'level');

  measurements.type('level').variant('predicted').position('atequipment')
    .value(level, timestamp, unit);
  measurements.type('volume').variant('predicted').position('atequipment')
    .value(_volumeFromLevel(basin, level), timestamp, 'm3');

  _resetFlowState(ctx, timestamp);
}

function setManualInflow(ctx, value, timestamp = Date.now(), unit = 'm3/s') {
  if (!ctx?.measurements) throw new Error('setManualInflow: ctx.measurements required');
  const num = Number(value);
  ctx.measurements.type('flow').variant('predicted').position('in').child('manual-qin')
    .value(num, timestamp, unit);
}

// Manual outflow injection mirroring setManualInflow — basin-docs adds this
// for the dashboard's q_out topic so tests can drive a drain stroke without
// instantiating a real pump.
function setManualOutflow(ctx, value, timestamp = Date.now(), unit = 'm3/s') {
  if (!ctx?.measurements) throw new Error('setManualOutflow: ctx.measurements required');
  const num = Number(value);
  ctx.measurements.type('flow').variant('predicted').position('out').child('manual-qout')
    .value(num, timestamp, unit);
}

module.exports = {
  calibratePredictedVolume,
  calibratePredictedLevel,
  setManualInflow,
  setManualOutflow,
};
