// Basic tests for MeasurementRouter.

const test = require('node:test');
const assert = require('node:assert/strict');

const { MeasurementContainer, coolprop } = require('generalFunctions');
const MeasurementRouter = require('../../src/measurement/measurementRouter');

// CoolProp is async-init; ensure it's warm before any pressure-conversion
// test runs.
test.before(async () => {
  await coolprop.init({ refrigerant: 'Water' });
});

function makeBasin() {
  return {
    surfaceArea: 10,
    minVol: 2,
    maxVol: 50,
    maxVolAtOverflow: 45,
    overflowLevel: 4.5,
    outflowLevel: 0.2,
    inflowLevel: 3,
  };
}

function makeMeasurements() {
  return new MeasurementContainer({
    autoConvert: true,
    preferredUnits: { flow: 'm3/s', level: 'm', volume: 'm3' },
  });
}

function fakeLogger() {
  const calls = { warn: [], info: [], error: [], debug: [] };
  return {
    warn: (m) => calls.warn.push(m),
    info: (m) => calls.info.push(m),
    error: (m) => calls.error.push(m),
    debug: (m) => calls.debug.push(m),
    _calls: calls,
  };
}

test('onLevelMeasurement writes volume + percent', async () => {
  const measurements = makeMeasurements();
  const basin = makeBasin();
  const router = new MeasurementRouter({ measurements, basin });

  router.onLevelMeasurement('atequipment', 2.5, { unit: 'm', timestamp: Date.now() });

  const lvl = measurements.type('level').variant('measured').position('atequipment').getCurrentValue('m');
  assert.ok(Math.abs(lvl - 2.5) < 1e-9);

  const vol = measurements.type('volume').variant('measured').position('atequipment').getCurrentValue('m3');
  // 2.5 m * 10 m² = 25 m3.
  assert.ok(Math.abs(vol - 25) < 1e-9, `volume was ${vol}`);

  const pct = measurements.type('volumePercent').variant('measured').position('atequipment').getCurrentValue('%');
  // (25 - 2) / (45 - 2) * 100 ≈ 53.488...
  assert.ok(pct > 53 && pct < 54, `percent was ${pct}`);
});

test('onPressureMeasurement falls back to assumed temperature and warns', async () => {
  const measurements = makeMeasurements();
  const basin = makeBasin();
  const logger = fakeLogger();
  const router = new MeasurementRouter({ measurements, basin, logger });

  // No temperature seeded — must fall back to assumed 15C.
  measurements.type('pressure').variant('measured').position('atequipment')
    .value(20000, Date.now(), 'Pa');
  router.onPressureMeasurement('atequipment', 20000, { unit: 'Pa', timestamp: Date.now() });

  const warned = logger._calls.warn.some((m) => /assuming 15C|temperature/i.test(m));
  assert.ok(warned, 'expected a warn about missing temperature');

  const assumedT = measurements.type('temperature').variant('assumed').position('atequipment')
    .getCurrentValue('K');
  assert.ok(Number.isFinite(assumedT), 'assumed temperature was not stored');

  const lvl = measurements.type('level').variant('predicted').position('atequipment')
    .getCurrentValue('m');
  // 20000 Pa / (~999 kg/m³ * 9.80665) ≈ 2.04 m.
  assert.ok(lvl > 1.9 && lvl < 2.2, `derived level was ${lvl}`);
});

test('route() dispatches by measurement type', async () => {
  const measurements = makeMeasurements();
  const basin = makeBasin();
  const router = new MeasurementRouter({ measurements, basin });

  const handledLevel = router.route('level', 1.5, 'atequipment', { unit: 'm' });
  assert.equal(handledLevel, true);
  const lvl = measurements.type('level').variant('measured').position('atequipment').getCurrentValue('m');
  assert.ok(Math.abs(lvl - 1.5) < 1e-9);

  // Unknown type returns false (no dispatch).
  const handledOther = router.route('flow', 0.1, 'in', {});
  assert.equal(handledOther, false);
});

test('constructor rejects missing context fields', async () => {
  assert.throws(() => new MeasurementRouter({}));
  assert.throws(() => new MeasurementRouter({ measurements: makeMeasurements() }));
});
