<?php
/**
 * Preservation Property Tests — Active Dates Return Same Session and Term as Original
 *
 * Validates: Requirements 3.1, 3.2, 3.3
 *
 * Property 2: Preservation
 *
 * PURPOSE: Verify that for all dates where isBugCondition(today) = false
 * (i.e., today falls within an active session/term range), the original
 * inline primary queries return the correct session and term data.
 *
 * These tests MUST PASS on unfixed code — they confirm the baseline behavior
 * that must be preserved after the fix is applied.
 *
 * EXPECTED OUTCOME: All tests PASS — confirms that active-date behavior works
 * correctly on unfixed code and must continue to work after the fix.
 *
 * Run via CLI: php portal/tests/test_gap_preservation.php
 */

// ── Bootstrap ────────────────────────────────────────────────────────────────
$root = dirname(__DIR__); // portal/
require_once "{$root}/includes/dbconnection.php";

if (!isset($con) || mysqli_connect_errno()) {
    echo "FATAL: Could not connect to database.\n";
    exit(1);
}

// ── Helpers ──────────────────────────────────────────────────────────────────
$pass = 0;
$fail = 0;

function assert_test(string $label, bool $condition, string $detail = ''): void
{
    global $pass, $fail;
    if ($condition) {
        echo "[PASS] $label\n";
        $pass++;
    } else {
        echo "[FAIL] $label\n";
        if ($detail !== '') {
            echo "       Detail: $detail\n";
        }
        $fail++;
    }
}

// ── Show current date for context ─────────────────────────────────────────────
$dateRow = $con->query("SELECT CURDATE() AS today")->fetch_assoc();
echo "\n=== Preservation Property Tests: Active Dates Return Same Session and Term ===\n";
echo "MySQL CURDATE() = {$dateRow['today']}\n\n";
echo "Validates: Requirements 3.1, 3.2, 3.3\n\n";

// ── Setup: insert a CURRENTLY ACTIVE session and term ────────────────────────
// start_date = CURDATE()-15, end_date = CURDATE()+15 → today is mid-term (active)
// isBugCondition(today) = false for this row — the primary query WILL find it.
$ts = time();
$sessionName = "TEST_PRES_ACTIVE_{$ts}";
$termName = 'First Term'; // valid enum value

// Clean up any leftover rows from a previous run
$con->query("DELETE st FROM school_terms st
             INNER JOIN school_sessions ss ON st.session_id = ss.id
             WHERE ss.session_name = '$sessionName'");
$con->query("DELETE FROM school_sessions WHERE session_name = '$sessionName'");

// Insert session: started 15 days ago, ends 15 days from now — ACTIVE today
$insertSession = $con->prepare(
    "INSERT INTO school_sessions (session_name, start_date, end_date)
     VALUES (?, DATE_SUB(CURDATE(), INTERVAL 15 DAY), DATE_ADD(CURDATE(), INTERVAL 15 DAY))"
);
if (!$insertSession) {
    echo "FATAL: Could not prepare session insert: {$con->error}\n";
    exit(1);
}
$insertSession->bind_param('s', $sessionName);
$insertSession->execute();
$sessionId = $con->insert_id;
$insertSession->close();

if (!$sessionId) {
    echo "FATAL: Failed to insert test session row.\n";
    exit(1);
}

// Insert term linked to that session with the same active date range
$insertTerm = $con->prepare(
    "INSERT INTO school_terms (term_name, session_id, start_date, end_date)
     VALUES (?, ?, DATE_SUB(CURDATE(), INTERVAL 15 DAY), DATE_ADD(CURDATE(), INTERVAL 15 DAY))"
);
if (!$insertTerm) {
    echo "FATAL: Could not prepare term insert: {$con->error}\n";
    exit(1);
}
$insertTerm->bind_param('si', $termName, $sessionId);
$insertTerm->execute();
$termId = $con->insert_id;
$insertTerm->close();

if (!$termId) {
    echo "FATAL: Failed to insert test term row.\n";
    exit(1);
}

echo "Test session inserted: id=$sessionId, session_name='$sessionName'\n";
echo "Test term inserted:    id=$termId,    term_name='$termName'\n";
echo "Both rows: start_date = CURDATE()-15, end_date = CURDATE()+15\n";
echo "Today is MID-TERM — the session and term are ACTIVE.\n";
echo "isBugCondition(today) = false for this scenario.\n\n";

// ── Run the ORIGINAL inline primary session query (no fallback) ───────────────
// This is the exact query pattern used in payschoolfees.php and all 14 affected files.
// Scoped to our test session ID to isolate from any other active sessions in the DB.
$primarySessionQuery = "SELECT id, session_name FROM school_sessions
                        WHERE id = ?
                        AND start_date <= CURDATE() AND end_date >= CURDATE()
                        ORDER BY start_date ASC LIMIT 1";
$stmtSession = $con->prepare($primarySessionQuery);
$stmtSession->bind_param('i', $sessionId);
$stmtSession->execute();
$sessionResult = $stmtSession->get_result();
$sessionRow = ($sessionResult && $sessionResult->num_rows > 0)
    ? $sessionResult->fetch_assoc()
    : null;
$stmtSession->close();

// Simulate the original code: populate variables only if a row was found
$currentSessionId = 0;
$currentSessionName = null;
if ($sessionRow !== null) {
    $currentSessionId = (int) $sessionRow['id'];
    $currentSessionName = $sessionRow['session_name'];
}

// ── Run the ORIGINAL inline primary term query (no fallback) ──────────────────
// Only runs when a session was found — same as the original nested if-block.
$currentTermId = 0;
$currentTermName = null;

if ($currentSessionName !== null) {
    $primaryTermQuery = "SELECT id, term_name FROM school_terms
                         WHERE session_id = ? AND id = ?
                         AND start_date <= CURDATE() AND end_date >= CURDATE()
                         ORDER BY start_date ASC LIMIT 1";
    $stmtTerm = $con->prepare($primaryTermQuery);
    $stmtTerm->bind_param('ii', $currentSessionId, $termId);
    $stmtTerm->execute();
    $termResult = $stmtTerm->get_result();
    $termRow = ($termResult && $termResult->num_rows > 0)
        ? $termResult->fetch_assoc()
        : null;
    $stmtTerm->close();

    if ($termRow !== null) {
        $currentTermId = (int) $termRow['id'];
        $currentTermName = $termRow['term_name'];
    }
}

// ── Assertions ────────────────────────────────────────────────────────────────

// Test 1: Primary session query DOES return the test session (Requirement 3.1)
assert_test(
    "Primary session query returns a row for active date (Requirement 3.1)",
    $currentSessionName !== null,
    "Expected primary query to find session '$sessionName' (active: CURDATE()-15 to CURDATE()+15). " .
    "Got: currentSessionName=" . var_export($currentSessionName, true)
);

// Test 2: Primary term query DOES return the test term (Requirement 3.2)
assert_test(
    "Primary term query returns a row for active date (Requirement 3.2)",
    $currentTermName !== null,
    "Expected primary query to find term '$termName' for session_id=$sessionId. " .
    "Got: currentTermName=" . var_export($currentTermName, true)
);

// Test 3: session_id matches what was inserted (Requirement 3.1)
assert_test(
    "session_id matches inserted test session (Requirement 3.1)",
    $currentSessionId === $sessionId,
    "Expected session_id=$sessionId, got session_id=$currentSessionId"
);

// Test 4: session_name matches what was inserted (Requirement 3.1)
assert_test(
    "session_name matches inserted test session (Requirement 3.1)",
    $currentSessionName === $sessionName,
    "Expected session_name='$sessionName', got session_name=" . var_export($currentSessionName, true)
);

// Test 5: term_id matches what was inserted (Requirement 3.2)
assert_test(
    "term_id matches inserted test term (Requirement 3.2)",
    $currentTermId === $termId,
    "Expected term_id=$termId, got term_id=$currentTermId"
);

// Test 6: term_name matches what was inserted (Requirement 3.2)
assert_test(
    "term_name matches inserted test term (Requirement 3.2)",
    $currentTermName === $termName,
    "Expected term_name='$termName', got term_name=" . var_export($currentTermName, true)
);

// Test 7: Fallback was NOT needed — primary query succeeded (Requirement 3.3)
// On an active date, the primary query must succeed without any fallback.
// We verify this by confirming the primary query returned the correct row directly.
$primarySucceeded = ($currentSessionName !== null && $currentTermName !== null);
assert_test(
    "Primary queries succeeded without needing fallback (Requirement 3.3)",
    $primarySucceeded,
    "Primary queries should resolve session and term on active dates without fallback. " .
    "session_name=" . var_export($currentSessionName, true) .
    ", term_name=" . var_export($currentTermName, true)
);

// ── Cleanup ───────────────────────────────────────────────────────────────────
$con->query("DELETE FROM school_terms WHERE id = $termId");
$con->query("DELETE FROM school_sessions WHERE id = $sessionId");
echo "\nTest rows cleaned up.\n";

// ── Summary ───────────────────────────────────────────────────────────────────
echo "\n=== Results: $pass passed, $fail failed ===\n";

if ($fail > 0) {
    echo "\nFAIL — Preservation baseline violated: primary queries did not return expected values.\n";
    echo "This should not happen on unfixed code for active dates.\n";
    exit(1);
} else {
    echo "\nPASS — All preservation assertions passed.\n";
    echo "Baseline confirmed: active-date behavior works correctly on unfixed code.\n";
    echo "This behavior MUST be preserved after the fix is applied.\n";
    exit(0);
}
