<?php
/**
 * Preservation Property Tests for New Student Mid-Session Allocation
 *
 * These tests capture BASELINE behaviour for NON-buggy inputs:
 *   - Old students (no 'School Fees (New Student)' record)
 *   - New students admitted in First Term
 *
 * ALL tests MUST PASS on the UNFIXED code — they confirm the behaviour
 * that must be preserved after the fix is applied.
 *
 * Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7
 *
 * Run: php portal/tests/test_new_student_allocation_preservation.php
 */

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function assert_test(string $name, bool $condition, string $counterexample): bool
{
    if ($condition) {
        echo "✓ PASS: $name\n";
        return true;
    }
    echo "✗ FAIL: $name\n";
    echo "  Counterexample: $counterexample\n";
    return false;
}

/**
 * Simulate the CURRENT (unfixed) allocation logic from pay_tuition.php.
 *
 * Parameters (all optional, sensible defaults provided):
 *   admissionTerm   – 'First Term' | 'Second Term' | 'Third Term'
 *                     NULL means "old student" (no new-student record at all)
 *   class           – e.g. 'SS 1', 'SS 2', 'SS 3', 'JSS 3'
 *   paymentAmount   – amount being approved
 *   termPaid        – already-paid amounts per term
 *   firstTermFee    – generic first-term fee (old student)
 *   secondTermFee   – second-term fee
 *   thirdTermFee    – third-term fee
 *   firstTermSS1Fee – special SS 1 first-term fee (0 = not applicable)
 *
 * Returns: [allocation_order, term_fees, term_inserts, remaining]
 */
function simulate_allocation(array $params): array
{
    $admissionTerm   = $params['admissionTerm']   ?? null;   // null = old student
    $class           = $params['class']           ?? 'SS 2';
    $paymentAmount   = $params['paymentAmount']   ?? 55000;
    $termPaid        = $params['termPaid']        ?? [];
    $firstTermFee    = $params['firstTermFee']    ?? 55000;
    $secondTermFee   = $params['secondTermFee']   ?? 55000;
    $thirdTermFee    = $params['thirdTermFee']    ?? 55000;
    $firstTermSS1Fee = $params['firstTermSS1Fee'] ?? 0;

    // -----------------------------------------------------------------------
    // Replicate the UNFIXED logic from pay_tuition.php exactly
    // -----------------------------------------------------------------------

    $isFinalYear = (trim($class) === 'SS 3' || trim($class) === 'JSS 3');

    // Special first-term fee resolution (mirrors the SS 1 / SS 3 / JSS 3 blocks)
    $resolvedFirstTermFee = $firstTermFee;
    if (trim($class) === 'SS 1' && $firstTermSS1Fee > 0) {
        $resolvedFirstTermFee = $firstTermSS1Fee;
    }
    // (SS 3 / JSS 3 special fees are not exercised by these preservation tests,
    //  so we keep the generic firstTermFee for those classes here.)

    // $term_fees — FIXED: uses old-student fees (no override for preservation tests since admissionTerm is null or First Term)
    $term_fees = [
        'First Term'  => $resolvedFirstTermFee,
        'Second Term' => $isFinalYear ? ($secondTermFee + $thirdTermFee) : $secondTermFee,
        'Third Term'  => $isFinalYear ? 0 : $thirdTermFee,
    ];

    // $allocation_order — FIXED: starts from admission term (null/'First Term' = unchanged for preservation tests)
    $full_order = $isFinalYear ? ['First Term', 'Second Term'] : ['First Term', 'Second Term', 'Third Term'];
    $startTerm = $admissionTerm ?? 'First Term';
    $admissionIndex = array_search($startTerm, $full_order);
    $allocation_order = ($admissionIndex !== false) ? array_slice($full_order, $admissionIndex) : $full_order;

    // Merge term_paid with defaults
    $term_paid_defaults = ['First Term' => 0, 'Second Term' => 0, 'Third Term' => 0];
    $term_paid_merged   = array_merge($term_paid_defaults, $termPaid);

    // Allocate payment across terms
    $remaining    = $paymentAmount;
    $term_inserts = [];

    foreach ($allocation_order as $term_name) {
        $needed = $term_fees[$term_name] - $term_paid_merged[$term_name];
        if ($needed <= 0) {
            continue;
        }

        $allocate = min($remaining, $needed);
        if ($allocate <= 0) {
            break;
        }

        $term_inserts[] = ['term' => $term_name, 'amount' => $allocate];
        $remaining -= $allocate;
    }

    return [
        'allocation_order' => $allocation_order,
        'term_fees'        => $term_fees,
        'term_inserts'     => $term_inserts,
        'remaining'        => $remaining,
    ];
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

echo "=== Preservation Property Tests ===\n";
echo "Testing UNFIXED allocation logic with NON-buggy inputs\n";
echo "Expected: All 5 tests should PASS (confirming baseline behaviour)\n\n";

$passed = 0;
$failed = 0;

// ============================================================================
// Test 1: Old student — allocation order unchanged
// ============================================================================
echo "Test 1: Old student — allocation order unchanged\n";
echo "  Scenario: No 'School Fees (New Student)' record; admissionTerm = null\n";

$result1 = simulate_allocation([
    'admissionTerm' => null,   // old student — no new-student record
    'class'         => 'SS 2',
    'paymentAmount' => 55000,
    'firstTermFee'  => 55000,
    'secondTermFee' => 55000,
    'thirdTermFee'  => 55000,
]);

$order1     = $result1['allocation_order'];
$termFees1  = $result1['term_fees'];

$order_ok = ($order1 === ['First Term', 'Second Term', 'Third Term']);
$fees_ok  = (
    $termFees1['First Term']  === 55000 &&
    $termFees1['Second Term'] === 55000 &&
    $termFees1['Third Term']  === 55000
);

if (assert_test(
    "Old student: allocation_order = ['First Term', 'Second Term', 'Third Term']",
    $order_ok,
    "allocation_order = [" . implode(', ', $order1) . "]"
)) { $passed++; } else { $failed++; }

if (assert_test(
    "Old student: term_fees match resolveTermFee() output (old student fees)",
    $fees_ok,
    "term_fees = " . json_encode($termFees1)
)) { $passed++; } else { $failed++; }

echo "\n";

// ============================================================================
// Test 2: New student admitted in First Term — allocation unchanged
// ============================================================================
echo "Test 2: New student admitted in First Term — allocation unchanged\n";
echo "  Scenario: pay_for = 'School Fees (New Student)', Term = 'First Term'\n";

$result2 = simulate_allocation([
    'admissionTerm' => 'First Term',  // new student, but admitted in First Term (not a bug case)
    'class'         => 'SS 2',
    'paymentAmount' => 55000,
    'firstTermFee'  => 55000,
    'secondTermFee' => 55000,
    'thirdTermFee'  => 55000,
]);

$order2    = $result2['allocation_order'];
$termFees2 = $result2['term_fees'];

$starts_first_term = ($order2[0] === 'First Term');
$first_term_fee_ok = ($termFees2['First Term'] === 55000);

if (assert_test(
    "First Term new student: allocation_order starts from 'First Term'",
    $starts_first_term,
    "allocation_order[0] = " . ($order2[0] ?? 'empty')
)) { $passed++; } else { $failed++; }

if (assert_test(
    "First Term new student: term_fees['First Term'] uses standard fee (55000)",
    $first_term_fee_ok,
    "term_fees['First Term'] = {$termFees2['First Term']}"
)) { $passed++; } else { $failed++; }

echo "\n";

// ============================================================================
// Test 3: Final year student (SS 3) — combined Second Term fee unchanged
// ============================================================================
echo "Test 3: Final year student (SS 3) — combined Second Term fee unchanged\n";
echo "  Scenario: class = 'SS 3', secondTermFee = 55000, thirdTermFee = 55000\n";

$result3 = simulate_allocation([
    'admissionTerm' => null,   // old student / First Term — not a bug case
    'class'         => 'SS 3',
    'paymentAmount' => 110000,
    'firstTermFee'  => 55000,
    'secondTermFee' => 55000,
    'thirdTermFee'  => 55000,
]);

$order3    = $result3['allocation_order'];
$termFees3 = $result3['term_fees'];

$combined_second_ok = ($termFees3['Second Term'] === 110000);
$third_zero_ok      = ($termFees3['Third Term']  === 0);
$final_year_order   = ($order3 === ['First Term', 'Second Term']);

if (assert_test(
    "SS 3: term_fees['Second Term'] = 110000 (55000 + 55000)",
    $combined_second_ok,
    "term_fees['Second Term'] = {$termFees3['Second Term']}"
)) { $passed++; } else { $failed++; }

if (assert_test(
    "SS 3: term_fees['Third Term'] = 0",
    $third_zero_ok,
    "term_fees['Third Term'] = {$termFees3['Third Term']}"
)) { $passed++; } else { $failed++; }

if (assert_test(
    "SS 3: allocation_order = ['First Term', 'Second Term']",
    $final_year_order,
    "allocation_order = [" . implode(', ', $order3) . "]"
)) { $passed++; } else { $failed++; }

echo "\n";

// ============================================================================
// Test 4: Partial payment — remaining balance logic unchanged
// ============================================================================
echo "Test 4: Partial payment — remaining balance logic unchanged\n";
echo "  Scenario: old student, term_paid['First Term'] = 30000, firstTermFee = 55000, payment = 40000\n";

$result4 = simulate_allocation([
    'admissionTerm' => null,   // old student
    'class'         => 'SS 2',
    'paymentAmount' => 40000,
    'termPaid'      => ['First Term' => 30000],
    'firstTermFee'  => 55000,
    'secondTermFee' => 55000,
    'thirdTermFee'  => 55000,
]);

$inserts4 = $result4['term_inserts'];

// First Term: needed = 55000 - 30000 = 25000; allocate min(40000, 25000) = 25000
// Second Term: remaining = 40000 - 25000 = 15000; needed = 55000; allocate 15000
$first_insert_ok  = (
    isset($inserts4[0]) &&
    $inserts4[0]['term']   === 'First Term' &&
    $inserts4[0]['amount'] === 25000
);
$second_insert_ok = (
    isset($inserts4[1]) &&
    $inserts4[1]['term']   === 'Second Term' &&
    $inserts4[1]['amount'] === 15000
);

if (assert_test(
    "Partial payment: term_inserts[0] = ['term' => 'First Term', 'amount' => 25000]",
    $first_insert_ok,
    "term_inserts[0] = " . json_encode($inserts4[0] ?? null)
)) { $passed++; } else { $failed++; }

if (assert_test(
    "Partial payment: term_inserts[1] = ['term' => 'Second Term', 'amount' => 15000]",
    $second_insert_ok,
    "term_inserts[1] = " . json_encode($inserts4[1] ?? null)
)) { $passed++; } else { $failed++; }

echo "\n";

// ============================================================================
// Test 5: SS 1 special fee — unchanged
// ============================================================================
echo "Test 5: SS 1 special fee — unchanged\n";
echo "  Scenario: class = 'SS 1', firstTermSS1Fee = 70000, firstTermFee = 55000\n";

$result5 = simulate_allocation([
    'admissionTerm'   => null,   // old student / First Term
    'class'           => 'SS 1',
    'paymentAmount'   => 70000,
    'firstTermFee'    => 55000,
    'secondTermFee'   => 55000,
    'thirdTermFee'    => 55000,
    'firstTermSS1Fee' => 70000,
]);

$termFees5 = $result5['term_fees'];

$ss1_fee_ok = ($termFees5['First Term'] === 70000);

if (assert_test(
    "SS 1: term_fees['First Term'] = 70000 (special SS 1 fee, not generic 55000)",
    $ss1_fee_ok,
    "term_fees['First Term'] = {$termFees5['First Term']}"
)) { $passed++; } else { $failed++; }

echo "\n";

// ---------------------------------------------------------------------------
// Summary
// ---------------------------------------------------------------------------
echo "=== Test Summary ===\n";
echo "Passed: $passed\n";
echo "Failed: $failed\n";
echo "Total:  " . ($passed + $failed) . "\n\n";

$total_assertions = 10; // 2 + 2 + 3 + 2 + 1

if ($failed === 0) {
    echo "✓ EXPECTED OUTCOME: All $passed assertions PASSED\n";
    echo "Baseline behaviour confirmed — these tests must continue to pass after the fix.\n";
    exit(0);
} else {
    echo "✗ UNEXPECTED FAILURES: $failed assertion(s) failed\n";
    echo "The unfixed code should pass all preservation tests.\n";
    echo "Review the simulate_allocation() function or the test logic.\n";
    exit(1);
}
