// Basic unit tests for thresholdValidator.
// Run with:  node --test test/basic/thresholdValidator.basic.test.js

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

const { validateThresholdOrdering } = require('../../src/basin/thresholdValidator');
const BasinGeometry = require('../../src/basin/BasinGeometry');

// A valid baseline: outlet 0.2 < inflow 3 < overflow 4.5 ≤ height 5,
// dryRun = 0.2 * 1.10 = 0.22 ≤ minLevel 1 ≤ start 2 < max 4
// ≤ highVolumeSafetyLevel 4.275.
function validBasinAndCfg() {
  const basin = new BasinGeometry(
    { volume: 50, height: 5, inflowLevel: 3, outflowLevel: 0.2, overflowLevel: 4.5 },
    { minHeightBasedOn: 'outlet' }
  );
  const levelbased = { minLevel: 1, startLevel: 2, maxLevel: 4 };
  const safety = { dryRunThresholdPercent: 10, overfillThresholdPercent: 95 };
  return { basin, levelbased, safety };
}

test('valid ordering returns empty array', () => {
  const { basin, levelbased, safety } = validBasinAndCfg();
  const issues = validateThresholdOrdering(basin, levelbased, safety);
  assert.deepEqual(issues, []);
});

test('outflowLevel >= inflowLevel triggers issue with correct shape', () => {
  const basin = new BasinGeometry(
    // outflow 3.5 > inflow 3 — invariant broken.
    { volume: 50, height: 5, inflowLevel: 3, outflowLevel: 3.5, overflowLevel: 4.5 },
    { minHeightBasedOn: 'outlet' }
  );
  const issues = validateThresholdOrdering(basin, { minLevel: 1, startLevel: 2, maxLevel: 4 }, { dryRunThresholdPercent: 0, overfillThresholdPercent: 100 });
  const hit = issues.find((i) => i.aName === 'outflowLevel' && i.bName === 'inflowLevel');
  assert.ok(hit, 'expected an outflowLevel < inflowLevel issue');
  assert.equal(hit.op, '<');
  assert.equal(hit.a, 3.5);
  assert.equal(hit.b, 3);
  assert.match(hit.msg, /outflowLevel.*<.*inflowLevel/);
});

test('maxLevel >= highVolumeSafetyLevel triggers issue', () => {
  const { basin } = validBasinAndCfg();
  // highVolumeSafetyLevel = overflowLevel × highPct/100 = 4.5 × 0.80 = 3.6.
  // maxLevel 4 > 3.6 → expect a `maxLevel <= highVolumeSafetyLevel` issue.
  const issues = validateThresholdOrdering(
    basin,
    { minLevel: 1, startLevel: 2, maxLevel: 4 },
    { dryRunThresholdPercent: 10, overfillThresholdPercent: 80 }
  );
  const hit = issues.find((i) => i.aName === 'maxLevel' && i.bName === 'highVolumeSafetyLevel');
  assert.ok(hit, 'expected a maxLevel <= highVolumeSafetyLevel issue');
  assert.equal(hit.op, '<=');
  assert.equal(hit.a, 4);
  assert.ok(Math.abs(hit.b - 3.6) < 1e-9);
});

test('NaN / undefined values are skipped, not flagged as issues', () => {
  const { basin } = validBasinAndCfg();
  const issues = validateThresholdOrdering(
    basin,
    { minLevel: undefined, startLevel: NaN, maxLevel: 4 },
    { dryRunThresholdPercent: 10, overfillThresholdPercent: 95 }
  );
  // dryRunLevel <= minLevel skipped (minLevel undefined → NaN)
  // minLevel <= startLevel skipped (both NaN-ish)
  // startLevel < maxLevel skipped (startLevel NaN)
  // maxLevel <= highVolumeSafetyLevel still checked → 4 ≤ 4.275 OK.
  // Geometry checks also OK.
  assert.deepEqual(issues, []);
});

test('multiple violations produce multiple issues in stable order', () => {
  // Build a basin with two geometry violations.
  const basin = new BasinGeometry(
    // outflow 4 > inflow 3 (broken)  AND  overflow 6 > height 5 (broken)
    { volume: 50, height: 5, inflowLevel: 3, outflowLevel: 4, overflowLevel: 6 },
    { minHeightBasedOn: 'outlet' }
  );
  const issues = validateThresholdOrdering(
    basin,
    { minLevel: 1, startLevel: 2, maxLevel: 4 },
    { dryRunThresholdPercent: 0, overfillThresholdPercent: 100 }
  );
  // Expect at least the two geometry issues, in declaration order:
  //   outflowLevel < inflowLevel comes before overflowLevel <= basinHeight.
  const idxOutflow = issues.findIndex((i) => i.aName === 'outflowLevel');
  const idxOverflow = issues.findIndex((i) => i.aName === 'overflowLevel' && i.bName === 'basinHeight');
  assert.ok(idxOutflow >= 0, 'expected outflowLevel issue');
  assert.ok(idxOverflow >= 0, 'expected overflowLevel <= basinHeight issue');
  assert.ok(idxOutflow < idxOverflow, 'issues should be in check-declaration order');
});

test('accepts a plain basin object (duck-typed via getters)', () => {
  const plainBasin = {
    volEmptyBasin: 50,
    heightBasin: 5,
    inflowLevel: 3,
    outflowLevel: 0.2,
    overflowLevel: 4.5,
    surfaceArea: 10,
    maxVol: 50,
    maxVolAtOverflow: 45,
    minVolAtInflow: 30,
    minVolAtOutflow: 2,
    minVol: 2,
    minHeightBasedOn: 'outlet',
  };
  const issues = validateThresholdOrdering(
    plainBasin,
    { minLevel: 1, startLevel: 2, maxLevel: 4 },
    { dryRunThresholdPercent: 10, overfillThresholdPercent: 95 }
  );
  assert.deepEqual(issues, []);
});

test('omitted levelbased / safety objects are tolerated', () => {
  const { basin } = validBasinAndCfg();
  // No control or safety supplied → only geometry checks run; valid basin geometry → []
  const issues = validateThresholdOrdering(basin, undefined, undefined);
  assert.deepEqual(issues, []);
});
