<?php
/**
 * Preservation Property Tests
 *
 * Validates: Requirements 3.1, 3.2, 3.3, 3.4
 *
 * Property 2: Preservation - Mid-Range and Boundary Date Behavior
 *
 * PURPOSE: Verify that for all dates where the bug condition does NOT hold
 * (i.e., CURDATE() != end_date), the original NOW()-based queries and the
 * fixed CURDATE()-based queries return identical results.
 *
 * These tests MUST PASS on unfixed code — they establish the baseline
 * behavior that the fix must preserve.
 *
 * Run via CLI: php portal/tests/test_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_preservation(string $label, bool $originalFound, bool $fixedFound): void
{
    global $pass, $fail;
    $same = ($originalFound === $fixedFound);
    $origStr = $originalFound ? 'row found' : 'no row';
    $fixedStr = $fixedFound ? 'row found' : 'no row';
    if ($same) {
        echo "[PASS] $label\n";
        echo "       original(NOW())=$origStr, fixed(CURDATE())=$fixedStr — identical\n";
        $pass++;
    } else {
        echo "[FAIL] $label\n";
        echo "       original(NOW())=$origStr, fixed(CURDATE())=$fixedStr — DIFFER\n";
        $fail++;
    }
}

// ── Show current MySQL time for context ──────────────────────────────────────
$nowRow = $con->query("SELECT NOW() AS now_val, CURDATE() AS curdate_val")->fetch_assoc();
echo "\n=== Preservation Property Tests ===\n";
echo "MySQL NOW()    = {$nowRow['now_val']}\n";
echo "MySQL CURDATE()= {$nowRow['curdate_val']}\n\n";

// ── Scenario definitions ──────────────────────────────────────────────────────
// term_name must be a valid enum value: 'First Term', 'Second Term', 'Third Term'
// Session names use a timestamp prefix to avoid collisions with real data.
$ts = time();
$scenarios = [
    [
        'label' => 'Mid-session: start=CURDATE()-14, end=CURDATE()+14 (Req 3.1, 3.2)',
        'start_expr' => 'CURDATE() - INTERVAL 14 DAY',
        'end_expr' => 'CURDATE() + INTERVAL 14 DAY',
        'term_name' => 'First Term',
        'session_name' => "TEST_PRES_{$ts}_S0",
    ],
    [
        'label' => 'Before start: start=CURDATE()+7, end=CURDATE()+30 (Req 3.3)',
        'start_expr' => 'CURDATE() + INTERVAL 7 DAY',
        'end_expr' => 'CURDATE() + INTERVAL 30 DAY',
        'term_name' => 'Second Term',
        'session_name' => "TEST_PRES_{$ts}_S1",
    ],
    [
        'label' => 'After end: start=CURDATE()-30, end=CURDATE()-1 (Req 3.4)',
        'start_expr' => 'CURDATE() - INTERVAL 30 DAY',
        'end_expr' => 'CURDATE() - INTERVAL 1 DAY',
        'term_name' => 'Third Term',
        'session_name' => "TEST_PRES_{$ts}_S2",
    ],
    [
        'label' => 'First day: start=CURDATE(), end=CURDATE()+30 (Req 3.1)',
        'start_expr' => 'CURDATE()',
        'end_expr' => 'CURDATE() + INTERVAL 30 DAY',
        'term_name' => 'First Term',
        'session_name' => "TEST_PRES_{$ts}_S3",
    ],
];

// ── Run each scenario ─────────────────────────────────────────────────────────
$insertedSessionIds = [];
$insertedTermIds = [];

foreach ($scenarios as $i => $scenario) {
    $sessionName = $scenario['session_name'];
    $termName = $scenario['term_name'];

    // Insert session row
    $insertSql = "INSERT INTO school_sessions (session_name, start_date, end_date)
                  VALUES ('$sessionName', {$scenario['start_expr']}, {$scenario['end_expr']})";
    if (!$con->query($insertSql)) {
        echo "FATAL: Could not insert session for scenario $i: {$con->error}\n";
        exit(1);
    }
    $sessionId = $con->insert_id;
    $insertedSessionIds[] = $sessionId;

    // Insert term row linked to that session (term_name is a valid enum value)
    $insertTermSql = "INSERT INTO school_terms (term_name, session_id, start_date, end_date)
                      VALUES ('$termName', $sessionId, {$scenario['start_expr']}, {$scenario['end_expr']})";
    if (!$con->query($insertTermSql)) {
        echo "FATAL: Could not insert term for scenario $i: {$con->error}\n";
        exit(1);
    }
    $termId = $con->insert_id;
    $insertedTermIds[] = $termId;

    // ── Session: original query (NOW()) ──────────────────────────────────────
    $stmtOrig = $con->prepare(
        "SELECT id FROM school_sessions
         WHERE start_date <= CURDATE() AND end_date >= CURDATE() AND session_name = ? LIMIT 1"
    );
    $stmtOrig->bind_param('s', $sessionName);
    $stmtOrig->execute();
    $origSessionFound = $stmtOrig->get_result()->num_rows > 0;
    $stmtOrig->close();

    // ── Session: fixed query (CURDATE()) ─────────────────────────────────────
    $stmtFixed = $con->prepare(
        "SELECT id FROM school_sessions
         WHERE start_date <= CURDATE() AND end_date >= CURDATE() AND session_name = ? LIMIT 1"
    );
    $stmtFixed->bind_param('s', $sessionName);
    $stmtFixed->execute();
    $fixedSessionFound = $stmtFixed->get_result()->num_rows > 0;
    $stmtFixed->close();

    // ── Term: original query (NOW()) — identify by session_id + term id ──────
    // We use the specific term id to isolate our test row from any real data.
    $stmtTermOrig = $con->prepare(
        "SELECT id FROM school_terms
         WHERE session_id = ? AND start_date <= CURDATE() AND end_date >= CURDATE() AND id = ? LIMIT 1"
    );
    $stmtTermOrig->bind_param('ii', $sessionId, $termId);
    $stmtTermOrig->execute();
    $origTermFound = $stmtTermOrig->get_result()->num_rows > 0;
    $stmtTermOrig->close();

    // ── Term: fixed query (CURDATE()) ────────────────────────────────────────
    $stmtTermFixed = $con->prepare(
        "SELECT id FROM school_terms
         WHERE session_id = ? AND start_date <= CURDATE() AND end_date >= CURDATE() AND id = ? LIMIT 1"
    );
    $stmtTermFixed->bind_param('ii', $sessionId, $termId);
    $stmtTermFixed->execute();
    $fixedTermFound = $stmtTermFixed->get_result()->num_rows > 0;
    $stmtTermFixed->close();

    // ── Assert preservation (original == fixed) ───────────────────────────────
    echo "--- Scenario " . ($i + 1) . ": {$scenario['label']} ---\n";
    assert_preservation("Session query", $origSessionFound, $fixedSessionFound);
    assert_preservation("Term query", $origTermFound, $fixedTermFound);
    echo "\n";
}

// ── Cleanup ───────────────────────────────────────────────────────────────────
foreach ($insertedTermIds as $tid) {
    $con->query("DELETE FROM school_terms WHERE id = $tid");
}
foreach ($insertedSessionIds as $sid) {
    $con->query("DELETE FROM school_sessions WHERE id = $sid");
}
echo "Test rows cleaned up.\n";

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

if ($fail > 0) {
    echo "\nFAIL — Preservation property violated: original and fixed queries returned different results.\n";
    exit(1);
} else {
    echo "\nPASS — All preservation assertions passed. Baseline behavior confirmed.\n";
    exit(0);
}
