<?php

namespace Tests;

use PHPUnit\Framework\TestCase;

// Include helper functions
require_once __DIR__ . '/../helpers/term_allocation_helpers.php';
require_once __DIR__ . '/../includes/dbconnection.php';

/**
 * Bug Condition Exploration Test
 * 
 * Property 1: Payment Allocation to Unpaid Terms in Current Session
 * 
 * This test MUST FAIL on unfixed code - failure confirms the bug exists.
 * When the fix is implemented, this test will PASS, confirming the bug is fixed.
 */
class TermAllocationBugTest extends TestCase
{
    private $con;
    private $currentSession = '2025/2026';
    private $currentTerm = 'Third Term';

    protected function setUp(): void
    {
        // Use global database connection
        global $con;
        
        if (!isset($con) || !$con) {
            // Try to create connection if not available
            $con = mysqli_connect('localhost', 'root', '', 'sms');
            if (!$con) {
                $this->markTestSkipped('Database connection failed: ' . mysqli_connect_error());
            }
        }
        
        $this->con = $con;
    }

    /**
     * Bug Scenario 1: OLD Student with unpaid Third Term in current session
     * 
     * Student: SS 1, admitted First Term 2025/2026
     * Current Session: 2025/2026
     * Paid: First Term (NGN 50,000), Second Term (NGN 50,000)
     * Unpaid: Third Term (NGN 50,000)
     * Payment Made: NGN 50,000
     * 
     * EXPECTED: Allocates to Third Term 2025/2026 (current session)
     * BUGGY: Allocates to First Term 2026/2027 (next session)
     */
    public function testOldStudentUnpaidThirdTermAllocation()
    {
        // Setup: Create test student
        $studentId = $this->createTestStudent('SS 1', '2025/2026', 'First Term');
        
        // Setup: Record payments for First and Second Term
        $this->recordPayment($studentId, 'First Term', '2025/2026', 50000, 'School Fees');
        $this->recordPayment($studentId, 'Second Term', '2025/2026', 50000, 'School Fees');
        
        // Action: Process payment for Third Term
        $paymentAmount = 50000;
        $allocations = $this->allocatePayment($studentId, $paymentAmount, '2025/2026', 'SS 1', 'Science');
        
        // Assert: Payment should be allocated to Third Term 2025/2026, NOT First Term 2026/2027
        $this->assertNotEmpty($allocations, 'Allocations should not be empty');
        
        // Find allocation for Third Term 2025/2026
        $thirdTermAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'Third Term';
        });
        
        $this->assertNotEmpty($thirdTermAllocation, 
            'Payment should be allocated to Third Term 2025/2026 (current session), not next session');
        
        $allocation = array_values($thirdTermAllocation)[0];
        $this->assertEquals(50000, $allocation['amount'], 
            'Full payment amount should be allocated to Third Term 2025/2026');
        
        // Verify NO allocation to next session
        $nextSessionAllocations = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2026/2027';
        });
        $this->assertEmpty($nextSessionAllocations, 
            'Payment should NOT be allocated to next session when current session has unpaid terms');
    }

    /**
     * Bug Scenario 2: NEW Student (mid-session admission) with unpaid Third Term
     * 
     * Student: SS 1, admitted Second Term 2025/2026 (NEW student)
     * Current Session: 2025/2026
     * Paid: School Fees (New Student) for Second Term (NGN 60,000)
     * Unpaid: Third Term (NGN 50,000)
     * Payment Made: NGN 50,000
     * 
     * EXPECTED: Allocates to Third Term 2025/2026 (current session)
     * BUGGY: Allocates to First Term 2026/2027 (next session)
     */
    public function testNewStudentMidSessionUnpaidTermAllocation()
    {
        // Setup: Create test NEW student (admitted Second Term)
        $studentId = $this->createTestStudent('SS 1', '2025/2026', 'Second Term');
        
        // Setup: Record new student fee payment for Second Term
        $this->recordPayment($studentId, 'Second Term', '2025/2026', 60000, 'School Fees (New Student)');
        
        // Action: Process payment for Third Term
        $paymentAmount = 50000;
        $allocations = $this->allocatePayment($studentId, $paymentAmount, '2025/2026', 'SS 1', 'Science');
        
        // Assert: Payment should be allocated to Third Term 2025/2026, NOT First Term 2026/2027
        $this->assertNotEmpty($allocations, 'Allocations should not be empty');
        
        // Find allocation for Third Term 2025/2026
        $thirdTermAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'Third Term';
        });
        
        $this->assertNotEmpty($thirdTermAllocation, 
            'Payment should be allocated to Third Term 2025/2026 (current session), not next session');
        
        $allocation = array_values($thirdTermAllocation)[0];
        $this->assertEquals(50000, $allocation['amount'], 
            'Full payment amount should be allocated to Third Term 2025/2026');
    }

    /**
     * Bug Scenario 3: OLD Student with previous session debt
     * 
     * Student: SS 2, admitted First Term 2024/2025 (OLD student)
     * Current Session: 2025/2026
     * Previous Session Outstanding: Second Term 2024/2025 (NGN 50,000 unpaid)
     * Current Session Unpaid: First Term (NGN 50,000)
     * Payment Made: NGN 100,000
     * 
     * EXPECTED: Allocates NGN 50,000 to Second Term 2024/2025, then NGN 50,000 to First Term 2025/2026
     * BUGGY: May allocate to current session only, ignoring previous session debt
     */
    public function testOldStudentPreviousSessionDebtPrioritization()
    {
        // Setup: Create test OLD student
        $studentId = $this->createTestStudent('SS 2', '2024/2025', 'First Term');
        
        // Setup: Record unpaid Second Term 2024/2025
        // (no payment recorded = unpaid)
        
        // Setup: Record First Term 2025/2026 as unpaid
        // (no payment recorded = unpaid)
        
        // Action: Process payment for current session
        $paymentAmount = 100000;
        $allocations = $this->allocatePayment($studentId, $paymentAmount, '2025/2026', 'SS 2', 'Science');
        
        // Assert: Payment should be allocated to previous session debt FIRST
        $this->assertNotEmpty($allocations, 'Allocations should not be empty');
        
        // Find allocation for Second Term 2024/2025 (previous session)
        $previousSessionAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2024/2025' && $alloc['term'] === 'Second Term';
        });
        
        $this->assertNotEmpty($previousSessionAllocation, 
            'Payment should prioritize previous session debt (Second Term 2024/2025)');
        
        $prevAlloc = array_values($previousSessionAllocation)[0];
        $this->assertEquals(50000, $prevAlloc['amount'], 
            'NGN 50,000 should be allocated to previous session debt');
        
        // Find allocation for First Term 2025/2026 (current session)
        $currentSessionAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'First Term';
        });
        
        $this->assertNotEmpty($currentSessionAllocation, 
            'Remaining payment should be allocated to current session');
        
        $currAlloc = array_values($currentSessionAllocation)[0];
        $this->assertEquals(50000, $currAlloc['amount'], 
            'NGN 50,000 should be allocated to First Term 2025/2026');
    }

    /**
     * Bug Scenario 4: Final Year Student with combined Second/Third Term
     * 
     * Student: SS 3, admitted First Term 2023/2024 (OLD student)
     * Current Session: 2025/2026
     * Paid: First Term (NGN 50,000)
     * Unpaid: Second Term (combined with Third Term = NGN 110,000)
     * Payment Made: NGN 120,000
     * 
     * EXPECTED: Allocates NGN 110,000 to Second Term, creates refund for NGN 10,000
     * BUGGY: May skip Second Term and allocate to next session
     */
    public function testFinalYearStudentCombinedTermAllocation()
    {
        // Setup: Create test final year student (SS 3)
        $studentId = $this->createTestStudent('SS 3', '2025/2026', 'First Term');
        
        // Setup: Record First Term payment
        $this->recordPayment($studentId, 'First Term', '2025/2026', 50000, 'School Fees');
        
        // Action: Process payment for Second Term (combined with Third)
        $paymentAmount = 120000;
        $allocations = $this->allocatePayment($studentId, $paymentAmount, '2025/2026', 'SS 3', 'Science');
        
        // Assert: Payment should be allocated to Second Term (combined), not next session
        $this->assertNotEmpty($allocations, 'Allocations should not be empty');
        
        // Find allocation for Second Term 2025/2026
        $secondTermAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'Second Term';
        });
        
        $this->assertNotEmpty($secondTermAllocation, 
            'Payment should be allocated to Second Term 2025/2026 (combined with Third Term)');
        
        $allocation = array_values($secondTermAllocation)[0];
        $this->assertEquals(110000, $allocation['amount'], 
            'NGN 110,000 should be allocated to Second Term (combined)');
        
        // Verify refund is created for excess
        $refunds = $this->getRefundsForStudent($studentId);
        $this->assertNotEmpty($refunds, 'Refund should be created for excess payment');
        
        $refund = $refunds[0];
        $this->assertEquals(10000, $refund['amount'], 
            'Refund should be NGN 10,000 (excess amount)');
    }

    /**
     * Bug Scenario 5: Chronological allocation order
     * 
     * Student: SS 1, admitted First Term 2025/2026
     * Current Session: 2025/2026
     * Paid: First Term (NGN 50,000)
     * Unpaid: Second Term (NGN 50,000), Third Term (NGN 50,000)
     * Payment Made: NGN 75,000
     * 
     * EXPECTED: Allocates NGN 50,000 to Second Term, NGN 25,000 to Third Term (chronological order)
     * BUGGY: May allocate to Third Term first or skip to next session
     */
    public function testChronologicalAllocationOrder()
    {
        // Setup: Create test student
        $studentId = $this->createTestStudent('SS 1', '2025/2026', 'First Term');
        
        // Setup: Record First Term payment
        $this->recordPayment($studentId, 'First Term', '2025/2026', 50000, 'School Fees');
        
        // Action: Process payment for remaining terms
        $paymentAmount = 75000;
        $allocations = $this->allocatePayment($studentId, $paymentAmount, '2025/2026', 'SS 1', 'Science');
        
        // Assert: Payment should be allocated in chronological order (Second Term first, then Third Term)
        $this->assertNotEmpty($allocations, 'Allocations should not be empty');
        
        // Find allocations
        $secondTermAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'Second Term';
        });
        $thirdTermAllocation = array_filter($allocations, function($alloc) {
            return $alloc['session'] === '2025/2026' && $alloc['term'] === 'Third Term';
        });
        
        $this->assertNotEmpty($secondTermAllocation, 'Second Term should be allocated first');
        $this->assertNotEmpty($thirdTermAllocation, 'Third Term should be allocated second');
        
        $secondAlloc = array_values($secondTermAllocation)[0];
        $thirdAlloc = array_values($thirdTermAllocation)[0];
        
        $this->assertEquals(50000, $secondAlloc['amount'], 
            'NGN 50,000 should be allocated to Second Term');
        $this->assertEquals(25000, $thirdAlloc['amount'], 
            'NGN 25,000 should be allocated to Third Term');
    }

    // ============ Helper Methods ============

    private function createTestStudent($class, $academicSession, $admissionTerm)
    {
        $stmt = $this->con->prepare(
            "INSERT INTO students (class, academic_session, admission_term, section, stream, studentno, studentName) 
             VALUES (?, ?, ?, 'Day', 'Science', ?, ?)"
        );
        
        $studentNo = 'TEST_' . uniqid();
        $studentName = 'Test Student ' . uniqid();
        
        $stmt->bind_param('sssss', $class, $academicSession, $admissionTerm, $studentNo, $studentName);
        $stmt->execute();
        
        return $this->con->insert_id;
    }

    private function recordPayment($studentId, $term, $session, $amount, $payFor)
    {
        $stmt = $this->con->prepare(
            "INSERT INTO payment (student_id, Term, session, amount, pay_for, method) 
             VALUES (?, ?, ?, ?, ?, 'Wallet')"
        );
        
        $stmt->bind_param('issds', $studentId, $term, $session, $amount, $payFor);
        $stmt->execute();
    }

    private function allocatePayment($studentId, $amount, $currentSession, $class, $stream)
    {
        // Get admission term for the student
        $admissionTerm = getAdmissionTerm($this->con, $studentId, $currentSession);
        
        // Call the actual allocation function from helpers
        return allocatePaymentAcrossTerms($this->con, $studentId, $amount, $currentSession, $class, $stream, $admissionTerm);
    }

    private function getRefundsForStudent($studentId)
    {
        $stmt = $this->con->prepare("SELECT * FROM refund WHERE student_id = ?");
        $stmt->bind_param('i', $studentId);
        $stmt->execute();
        
        $result = $stmt->get_result();
        return $result->fetch_all(MYSQLI_ASSOC);
    }

    protected function tearDown(): void
    {
        // Don't close the connection - it's managed globally
        // Clean up test data if needed
    }
}
