/**
 * Tests for Student Loan Matching Calculator
 * Run with: node calculations.test.js
 */

const {
    calculatePayoffYears,
    calculateRemainingBalance,
    calculateRetirementValue,
    calculateNetWorthComparison
} = require('./calculations.js');

// Test utilities
let testsPassed = 0;
let testsFailed = 0;

function assert(condition, message) {
    if (condition) {
        console.log(`✓ ${message}`);
        testsPassed++;
    } else {
        console.error(`✗ ${message}`);
        testsFailed++;
    }
}

function assertClose(actual, expected, tolerance = 0.01, message = '') {
    const diff = Math.abs(actual - expected);
    const pass = diff <= tolerance;
    if (pass) {
        console.log(`✓ ${message} (${actual} ≈ ${expected})`);
        testsPassed++;
    } else {
        console.error(`✗ ${message} (${actual} != ${expected}, diff: ${diff})`);
        testsFailed++;
    }
}

// Test calculatePayoffYears
console.log('\n=== Testing calculatePayoffYears ===');

assert(
    calculatePayoffYears(30000, 500) === 6,
    'Should calculate ~6 years for $30k debt at $500/month (5% interest)'
);

assert(
    calculatePayoffYears(30000, 0) === 0,
    'Should return 0 for zero payment'
);

assert(
    calculatePayoffYears(30000, 100) === 50,
    'Should return 50 (max) for payment below interest'
);

// Test calculateRemainingBalance
console.log('\n=== Testing calculateRemainingBalance ===');

const balance5Years = calculateRemainingBalance(30000, 500, 60, 84);
assertClose(
    calculateRemainingBalance(30000, 500, 60, 6 * 12),
    4498,
    100,
    'After 5 years of $500 payments on $30k, should have ~$4.5k remaining (5% interest)'
);

assert(
    calculateRemainingBalance(30000, 500, 0, 84) === 30000,
    'At month 0, balance should equal original'
);

assert(
    calculateRemainingBalance(30000, 500, 84, 84) === 0,
    'At final month, balance should be 0'
);

assert(
    calculateRemainingBalance(30000, 500, 100, 84) === 0,
    'After loan term, balance should be 0'
);

// Test calculateRetirementValue
console.log('\n=== Testing calculateRetirementValue ===');

const value10Years = calculateRetirementValue(5000, 10, 10);
assertClose(
    value10Years,
    73918,
    100,
    '$5k/year for 10 years at 7% should grow to ~$74k'
);

const value30Years = calculateRetirementValue(5000, 10, 30);
assertClose(
    value30Years,
    286039,
    100,
    '$5k/year for 10 years, growing for 30 total years should be ~$286k'
);

// Test full calculation scenario
console.log('\n=== Testing Full Scenario ===');

const scenario1 = calculateNetWorthComparison({
    salary: 60000,
    totalDebt: 30000,
    monthlyPayment: 500,
    matchRate: 5,
    currentAge: 25
});

console.log('\nScenario 1 Results:');
console.log(`  Payoff years: ${scenario1.payoffYears}`);
console.log(`  Annual match: $${scenario1.annualMatch}`);
console.log(`  Retirement with program: $${scenario1.totalRetirementWith.toLocaleString()}`);
console.log(`  Retirement without program: $${scenario1.totalRetirementWithout.toLocaleString()}`);
console.log(`  Difference: $${scenario1.retirementDifference.toLocaleString()}`);

assert(
    scenario1.payoffYears === 6,
    'Should take 6 years to pay off $30k at $500/month (5% interest)'
);

assert(
    scenario1.annualMatch === 3000,
    'Annual match should be $3000 (5% of $60k salary)'
);

assert(
    scenario1.retirementDifference > 0,
    'With program should result in more retirement savings'
);

// Test edge case: very high payment
const scenario2 = calculateNetWorthComparison({
    salary: 100000,
    totalDebt: 20000,
    monthlyPayment: 2000,
    matchRate: 4,
    currentAge: 30
});

console.log('\nScenario 2 Results (High Payment):');
console.log(`  Payoff years: ${scenario2.payoffYears}`);
console.log(`  Annual match: $${scenario2.annualMatch}`);

assert(
    scenario2.payoffYears <= 2,
    'High payment should result in quick payoff'
);

assert(
    scenario2.annualMatch === 4000,
    'Annual match should be capped at 4% of salary'
);

// Test the growth rates after payoff
console.log('\n=== Testing Growth Rates After Payoff ===');

const scenario3 = calculateNetWorthComparison({
    salary: 70000,
    totalDebt: 40000,
    monthlyPayment: 600,
    matchRate: 5,
    currentAge: 28
});

// Find the index right after payoff
const payoffIndex = Math.ceil((scenario3.payoffYears / 2));
if (payoffIndex < scenario3.withProgram.length - 1) {
    const withGrowth = scenario3.withProgram[payoffIndex + 1] - scenario3.withProgram[payoffIndex];
    const withoutGrowth = scenario3.withoutProgram[payoffIndex + 1] - scenario3.withoutProgram[payoffIndex];
    
    console.log(`\nGrowth rate comparison after payoff:`);
    console.log(`  With program growth: $${withGrowth}`);
    console.log(`  Without program growth: $${withoutGrowth}`);
    
    // After payoff, both should be contributing the same amount, so growth should be similar
    // The "with" program will have a larger base, but the rate of change should be close
    const growthRatio = withoutGrowth > 0 ? withGrowth / withoutGrowth : 0;
    assertClose(
        growthRatio,
        1.5,
        1.0,
        'Growth rates after payoff should be relatively similar'
    );
}

// Summary
console.log('\n=== Test Summary ===');
console.log(`Tests passed: ${testsPassed}`);
console.log(`Tests failed: ${testsFailed}`);

if (testsFailed === 0) {
    console.log('\n✅ All tests passed!');
    process.exit(0);
} else {
    console.log('\n❌ Some tests failed');
    process.exit(1);
}