const { MeasurementContainer } = require('./index');
const { POSITIONS } = require('../constants/positions');

const measurements = new MeasurementContainer();

console.log('=== MEASUREMENT CONTAINER EXAMPLES ===\n');
console.log('This guide shows how to use the MeasurementContainer for storing,');
console.log('retrieving, and converting measurement data with automatic unit handling.\n');

// ====================================
// BASIC SETUP EXAMPLES
// ====================================
console.log('--- Example 1: Basic Setup & Distance ---');

// Create a basic container
const basicContainer = new MeasurementContainer({ windowSize: 20 });

// Subscribe to events to monitor changes
basicContainer.emitter.on('flow.predicted.upstream', (data) => {
  console.log(`📡 Event: Flow predicted upstream = ${data.value} ${data.unit || ''} (distance=${data.distance ?? 'n/a'}m)`);
});

// Subscribe to all measured flow events using wildcard
basicContainer.emitter.on('flow.measured.*', (data) => {
  console.log(`📡 Event: Flow measured ${data.position} = ${data.value} ${data.unit || ''} (distance=${data.distance ?? 'n/a'}m)`);
});

// Basic value setting with distance
console.log('\nSetting pressure values with distances:');
basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .distance(1.5)
  .value(100)
  .unit('psi');

basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.DOWNSTREAM)
  .distance(5.2)
  .value(95)
  .unit('psi');

// Distance persists - no need to set it again for same position
basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.DOWNSTREAM)
  .value(90); // distance 5.2 is automatically reused

console.log('✅ Basic setup complete\n');

// Retrieve and display the distance
const upstreamPressure = basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .get();

console.log(`Retrieved upstream pressure: ${upstreamPressure.getCurrentValue()} ${upstreamPressure.unit}`);
console.log(`Distance from parent: ${upstreamPressure.distance ?? 'not set'} m\n`);

// ====================================
// AUTO-CONVERSION SETUP
// ====================================
console.log('--- Example 2: Auto-Conversion Setup ---');

const autoContainer = new MeasurementContainer({
  autoConvert: true,
  windowSize: 50,
  defaultUnits: {
    pressure: 'bar',
    flow: 'l/min',
    power: 'kW',
    temperature: 'C'
  },
  preferredUnits: {
    pressure: 'psi'
  }
});

// Values automatically convert to preferred units
console.log('Adding pressure with auto-conversion:');
autoContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .distance(0.5)
  .value(1.5, Date.now(), 'bar'); // Input: 1.5 bar → Auto-stored as ~21.76 psi


const converted = autoContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .get();

console.log(`Stored as: ${converted.getCurrentValue()} ${converted.unit} (distance=${converted.distance}m)`);
console.log('✅ Auto-conversion complete\n');

// ====================================
// UNIT CONVERSION ON RETRIEVAL
// ====================================
console.log('--- Example 3: Unit Conversion on Retrieval ---');

autoContainer
  .type('flow')
  .variant('predicted')
  .position(POSITIONS.UPSTREAM)
  .distance(2.4)
  .value(100, Date.now(), 'l/min');

const flowMeasurement = autoContainer
  .type('flow')
  .variant('predicted')
  .position(POSITIONS.UPSTREAM)
  .get();

console.log(`Flow in l/min: ${flowMeasurement.getCurrentValue('l/min')}`);
console.log(`Flow in m³/h: ${flowMeasurement.getCurrentValue('m3/h').toFixed(2)}`);
console.log(`Flow in gal/min: ${flowMeasurement.getCurrentValue('gal/min').toFixed(2)}`);
console.log(`Distance: ${flowMeasurement.distance}m\n`);

// ====================================
// SMART UNIT SELECTION
// ====================================
console.log('--- Example 4: Smart Unit Selection ---');

autoContainer
  .type('pressure')
  .variant('test')
  .position('sensor')
  .distance(0.2)
  .value(0.001, Date.now(), 'bar');

const bestUnit = autoContainer
  .type('pressure')
  .variant('test')
  .position('sensor')
  .getBestUnit();

if (bestUnit) {
  console.log(`Best unit: ${bestUnit.val.toFixed(2)} ${bestUnit.unit}`);
}

const availableUnits = autoContainer.getAvailableUnits('pressure');
console.log(`Available units: ${availableUnits.slice(0, 5).join(', ')}...\n`);

// ====================================
// BASIC RETRIEVAL
// ====================================
console.log('--- Example 5: Basic Value Retrieval ---');

const upstreamVal = basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .getCurrentValue();

const upstreamData = basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .get();

console.log(`Upstream: ${upstreamVal} ${upstreamData.unit} at ${upstreamData.distance}m`);

const downstreamVal = basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.DOWNSTREAM)
  .getCurrentValue();

const downstreamData = basicContainer
  .type('pressure')
  .variant('measured')
  .position(POSITIONS.DOWNSTREAM)
  .get();

  //check wether a serie exists
const hasSeries = basicContainer // eslint-disable-line no-unused-vars
  .type("flow")
  .variant("measured")
  .exists();                // true if any position exists

const hasUpstreamValues = basicContainer // eslint-disable-line no-unused-vars
  .type("flow")
  .variant("measured")
  .exists({ position: POSITIONS.UPSTREAM, requireValues: true });

// Passing everything explicitly
const hasPercent = basicContainer.exists({ // eslint-disable-line no-unused-vars
  type: "volume",
  variant: "percent",
  position: POSITIONS.AT_EQUIPMENT,
});


console.log(`Downstream: ${downstreamVal} ${downstreamData.unit} at ${downstreamData.distance}m\n`);

// ====================================
// CALCULATIONS & STATISTICS
// ====================================
console.log('--- Example 6: Calculations & Statistics ---');

basicContainer
  .type('flow')
  .variant('predicted')
  .position(POSITIONS.UPSTREAM)
  .distance(3.0)
  .value(200)
  .unit('gpm');

basicContainer
  .type('flow')
  .variant('predicted')
  .position(POSITIONS.DOWNSTREAM)
  .distance(8.5)
  .value(195)
  .unit('gpm');

const flowAvg = basicContainer
  .type('flow')
  .variant('predicted')
  .position(POSITIONS.UPSTREAM)
  .getAverage();

console.log(`Average upstream flow: ${flowAvg.toFixed(1)} gpm`);

const pressureDiff = basicContainer
  .type('pressure')
  .variant('measured')
  .difference();

console.log(`Pressure difference: ${pressureDiff.value} ${pressureDiff.unit}\n`);

//reversable difference
const deltaP = basicContainer.type("pressure").variant("measured").difference(); // eslint-disable-line no-unused-vars -- defaults to downstream - upstream
const netFlow = basicContainer.type("flow").variant("measured").difference({ from: POSITIONS.UPSTREAM, to: POSITIONS.DOWNSTREAM }); // eslint-disable-line no-unused-vars

// ====================================
// ADVANCED STATISTICS & HISTORY
// ====================================
console.log('--- Example 7: Advanced Statistics & History ---');

basicContainer
  .type('flow')
  .variant('measured')
  .position(POSITIONS.UPSTREAM)
  .distance(3.0)
  .value(210)
  .value(215)
  .value(205)
  .value(220)
  .value(200)
  .unit('m3/h');

const stats = basicContainer
  .type('flow')
  .variant('measured')
  .position(POSITIONS.UPSTREAM);

const statsData = stats.get();

console.log('Flow Statistics:');
console.log(`  Current: ${stats.getCurrentValue()} ${statsData.unit}`);
console.log(`  Average: ${stats.getAverage().toFixed(1)} ${statsData.unit}`);
console.log(`  Min: ${stats.getMin()} ${statsData.unit}`);
console.log(`  Max: ${stats.getMax()} ${statsData.unit}`);
console.log(`  Distance: ${statsData.distance}m`);

const allValues = stats.getAllValues();
console.log(`  Samples: ${allValues.values.length}`);
console.log(`  History: [${allValues.values.join(', ')}]\n`);

console.log('--- Lagged sample comparison ---');

const latestSample = stats.getLaggedSample(0);   // newest sample object
const prevSample = stats.getLaggedSample(1);
const prevPrevSample = stats.getLaggedSample(2);

if (prevSample) {
  const delta = (latestSample?.value ?? 0) - (prevSample.value ?? 0);
  console.log(
    `Current vs previous: ${latestSample?.value} ${statsData.unit} (t=${latestSample?.timestamp}) vs ` +
    `${prevSample.value} ${prevSample.unit} (t=${prevSample.timestamp})`
  );
  console.log(`Δ = ${delta.toFixed(2)} ${statsData.unit}`);
}

if (prevPrevSample) {
  console.log(
    `Previous vs 2-steps-back timestamps: ${new Date(prevSample.timestamp).toISOString()} vs ` +
    `${new Date(prevPrevSample.timestamp).toISOString()}`
  );
}

// ====================================
// DYNAMIC UNIT MANAGEMENT
// ====================================
console.log('--- Example 8: Dynamic Unit Management ---');

autoContainer.setPreferredUnit('flow', 'm3/h');
console.log('Changed preferred flow unit to m³/h');

autoContainer
  .type('flow')
  .variant('realtime')
  .position('inlet')
  .distance(1.2)
  .value(150, Date.now(), 'l/min');

const realtimeFlow = autoContainer
  .type('flow')
  .variant('realtime')
  .position('inlet')
  .get();

console.log(`Stored as: ${realtimeFlow.getCurrentValue()} ${realtimeFlow.unit}`);
console.log(`Original: ${realtimeFlow.getCurrentValue('l/min').toFixed(1)} l/min`);
console.log(`Distance: ${realtimeFlow.distance}m\n`);

// ====================================
// DATA EXPLORATION
// ====================================
console.log('--- Example 9: Data Exploration ---');

console.log('Available types:', basicContainer.getTypes());
console.log('Pressure variants:', basicContainer.type('pressure').getVariants());
console.log('Measured pressure positions:', basicContainer.type('pressure').variant('measured').getPositions());

console.log('\nData Structure:');
basicContainer.getTypes().forEach(type => {
  const variants = basicContainer.type(type).getVariants();
  if (variants.length > 0) {
    console.log(`${type.toUpperCase()}:`);
    variants.forEach(variant => {
      const positions = basicContainer.type(type).variant(variant).getPositions();
      positions.forEach(position => {
        const m = basicContainer.type(type).variant(variant).position(position).get();
        if (m && m.values.length > 0) {
          console.log(`  └─ ${variant}.${position}: ${m.values.length} values, ${m.unit || 'no unit'}, dist=${m.distance ?? 'n/a'}m`);
        }
      });
    });
  }
});

// ---------------------------------------------------------------------------
// --- Child Aggregation -----------------------------------------------------
// ---------------------------------------------------------------------------

// ====================================
// AGGREGATION WITH CHILD SERIES (sum)
// ====================================
console.log();
console.log('--- Example X: Aggregation with sum() and child series ---');

// Container where flow is stored internally in m3/h
const aggContainer = new MeasurementContainer({
  windowSize: 10,
  defaultUnits: {
    flow: 'm3/h',
  },
});

// Two pumps both feeding the same inlet position
aggContainer
  .child('pumpA')
  .type('flow')
  .variant('measured')
  .position('inlet')
  .value(10, Date.now(), 'm3/h'); // 10 m3/h

aggContainer
  .child('pumpB')
  .type('flow')
  .variant('measured')
  .position('inlet')
  .value(15, Date.now(), 'm3/h'); // 15 m3/h

// Another position, e.g. outlet, also with two pumps
aggContainer
  .child('pumpA')
  .type('flow')
  .variant('measured')
  .position('outlet')
  .value(8, Date.now(), 'm3/h'); // 8 m3/h

aggContainer
  .child('pumpB')
  .type('flow')
  .variant('measured')
  .position('outlet')
  .value(11, Date.now(), 'm3/h'); // 11 m3/h


// 1) Sum only inlet position (children pumpA + pumpB)
const inletTotal = aggContainer.sum('flow', 'measured', ['inlet']);
console.log(`Total inlet flow: ${inletTotal} m3/h (expected 25 m3/h)`);

// 2) Sum inlet + outlet positions together
const totalAll = aggContainer.sum('flow', 'measured', ['inlet', 'outlet']);
console.log(`Total inlet+outlet flow: ${totalAll} m3/h (expected 44 m3/h)`);

// 3) Same sum but explicitly ask for a target unit (e.g. l/s)
//    This will use convertModule(...) internally.
//    If conversion is not supported, it will fall back to the raw value.
const totalAllLps = aggContainer.sum('flow', 'measured', ['inlet', 'outlet'], 'l/s');
console.log(`Total inlet+outlet flow in l/s: ${totalAllLps} l/s (converted from m3/h)\n`);


console.log('\n✅ All examples complete!\n');


// ====================================
// BEST PRACTICES
// ====================================
console.log('--- Best Practices Summary ---\n');

console.log('SETUP:');
console.log('  • Enable autoConvert for consistent units');
console.log('  • Define defaultUnits for your measurement types');
console.log('  • Set windowSize based on your data retention needs\n');

console.log('STORING DATA:');
console.log('  • Chain methods: type().variant().position().distance().value()');
console.log('  • Set distance once - it persists for that position');
console.log('  • Specify source unit: .value(100, timestamp, "psi")');
console.log('  • Set unit immediately: .value(100).unit("psi")\n');

console.log('RETRIEVING DATA:');
console.log('  • Use .getCurrentValue("unit") for specific units');
console.log('  • Use .getBestUnit() for automatic selection');
console.log('  • Use .difference() for upstream/downstream deltas');
console.log('  • Access .get().distance for physical positioning\n');

console.log('MONITORING:');
console.log('  • Subscribe: .emitter.on("type.variant.position", callback)');
console.log('  • Event data includes: value, unit, timestamp, distance');
console.log('  • Explore data: .getTypes(), .getVariants(), .getPositions()\n');

module.exports = { basicContainer, autoContainer };