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

const { PIDController, CascadePIDController } = require('../src/pid/index.js');

test('pid supports freeze/unfreeze with held output', () => {
  const pid = new PIDController({
    kp: 2,
    ki: 0.5,
    kd: 0.1,
    sampleTime: 100,
    outputMin: 0,
    outputMax: 100,
  });

  const t0 = Date.now();
  const first = pid.update(10, 2, t0 + 100);
  pid.freeze({ output: first, trackMeasurement: true });
  const frozen = pid.update(10, 4, t0 + 200);
  assert.equal(frozen, first);

  pid.unfreeze();
  const resumed = pid.update(10, 4, t0 + 300);
  assert.equal(Number.isFinite(resumed), true);
});

test('pid supports dynamic tunings and gain scheduling', () => {
  const pid = new PIDController({
    kp: 1,
    ki: 0,
    kd: 0,
    sampleTime: 100,
    outputMin: -100,
    outputMax: 100,
    gainSchedule: [
      { min: Number.NEGATIVE_INFINITY, max: 5, kp: 1, ki: 0, kd: 0 },
      { min: 5, max: Number.POSITIVE_INFINITY, kp: 3, ki: 0, kd: 0 },
    ],
  });

  const t0 = Date.now();
  const low = pid.update(10, 9, t0 + 100, { gainInput: 4 });
  const high = pid.update(10, 9, t0 + 200, { gainInput: 6 });

  assert.equal(high > low, true);

  const tuned = pid.update(10, 9, t0 + 300, { tunings: { kp: 10, ki: 0, kd: 0 } });
  assert.equal(tuned > high, true);
});

test('pid applies deadband and output rate limits', () => {
  const pid = new PIDController({
    kp: 10,
    ki: 0,
    kd: 0,
    deadband: 0.5,
    sampleTime: 100,
    outputMin: 0,
    outputMax: 100,
    outputRateLimitUp: 5,   // units per second
    outputRateLimitDown: 5, // units per second
  });

  const t0 = Date.now();
  const out1 = pid.update(10, 10, t0 + 100); // inside deadband -> no action
  const out2 = pid.update(20, 0, t0 + 200);  // strong error but limited by rate

  assert.equal(out1, 0);
  // 5 units/sec * 0.1 sec = max 0.5 rise per cycle
  assert.equal(out2 <= 0.5 + 1e-9, true);
});

test('cascade pid computes primary and secondary outputs', () => {
  const cascade = new CascadePIDController({
    primary: {
      kp: 2,
      ki: 0,
      kd: 0,
      sampleTime: 100,
      outputMin: 0,
      outputMax: 100,
    },
    secondary: {
      kp: 1,
      ki: 0,
      kd: 0,
      sampleTime: 100,
      outputMin: 0,
      outputMax: 100,
    },
  });

  const t0 = Date.now();
  const result = cascade.update({
    setpoint: 10,
    primaryMeasurement: 5,
    secondaryMeasurement: 2,
    timestamp: t0 + 100,
  });

  assert.equal(typeof result.primaryOutput, 'number');
  assert.equal(typeof result.secondaryOutput, 'number');
  assert.equal(result.primaryOutput > 0, true);
  assert.equal(result.secondaryOutput > 0, true);
});
