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

const MeasurementContainer = require('../src/measurements/MeasurementContainer.js');

function makeContainer() {
  return new MeasurementContainer({
    windowSize: 10,
    defaultUnits: {
      flow: 'm3/h',
      pressure: 'mbar',
    },
  });
}

test('stores and retrieves measurements via chain API', () => {
  const c = makeContainer();
  c.type('flow').variant('measured').position('upstream').value(100, 1, 'm3/h');

  assert.equal(c.type('flow').variant('measured').position('upstream').getCurrentValue(), 100);
  assert.equal(c.type('flow').variant('measured').position('upstream').exists(), true);
});

test('distance(null) auto-derives from position mapping', () => {
  const c = makeContainer();
  c.type('pressure').variant('measured').position('upstream').distance(null).value(5, 1, 'mbar');

  const m = c.type('pressure').variant('measured').position('upstream').get();
  assert.equal(m.distance, Number.POSITIVE_INFINITY);
});

test('getLaggedSample with requested unit converts sample value', () => {
  const c = makeContainer();
  c.type('flow').variant('measured').position('upstream').value(3.6, 1, 'm3/h');
  c.type('flow').variant('measured').position('upstream').value(7.2, 2, 'm3/h');

  const previous = c.type('flow').variant('measured').position('upstream').getLaggedSample(1, 'm3/s');
  assert.ok(previous);
  assert.equal(previous.unit, 'm3/s');
  assert.ok(Math.abs(previous.value - 0.001) < 1e-8);
});

test('difference computes current and average delta between positions', () => {
  const c = makeContainer();
  c.type('pressure').variant('measured').position('downstream').value(120, 1, 'mbar');
  c.type('pressure').variant('measured').position('downstream').value(130, 2, 'mbar');
  c.type('pressure').variant('measured').position('upstream').value(100, 1, 'mbar');
  c.type('pressure').variant('measured').position('upstream').value(110, 2, 'mbar');

  const diff = c.type('pressure').variant('measured').difference();
  assert.equal(diff.value, 20);
  assert.equal(diff.avgDiff, 20);
  assert.equal(diff.unit, 'mbar');
});

test('_convertPositionNum2Str maps signs to labels', () => {
  const c = makeContainer();
  assert.equal(c._convertPositionNum2Str(0), 'atEquipment');
  assert.equal(c._convertPositionNum2Str(1), 'downstream');
  assert.equal(c._convertPositionNum2Str(-1), 'upstream');
});

test('storeCanonical stores anchor unit internally and can emit preferred output units', () => {
  const c = new MeasurementContainer({
    windowSize: 10,
    autoConvert: true,
    defaultUnits: { flow: 'm3/h' },
    preferredUnits: { flow: 'm3/h' },
    canonicalUnits: { flow: 'm3/s' },
    storeCanonical: true,
  });

  c.type('flow').variant('measured').position('upstream').value(3.6, 1, 'm3/h');

  const internal = c.type('flow').variant('measured').position('upstream').getCurrentValue();
  assert.ok(Math.abs(internal - 0.001) < 1e-9);

  const flat = c.getFlattenedOutput({ requestedUnits: { flow: 'm3/h' } });
  assert.ok(Math.abs(flat['flow.measured.upstream.default'] - 3.6) < 1e-9);
});

test('strict unit validation rejects missing required unit and incompatible units', () => {
  const c = new MeasurementContainer({
    windowSize: 10,
    strictUnitValidation: true,
    throwOnInvalidUnit: true,
    requireUnitForTypes: ['flow'],
  });

  assert.throws(() => {
    c.type('flow').variant('measured').position('upstream').value(10, 1);
  }, /Missing source unit/i);

  assert.throws(() => {
    c.type('flow').variant('measured').position('upstream').value(10, 1, 'mbar');
  }, /Incompatible|unknown source unit/i);
});
