<?php

// Feature: pending-token-email-reminder, Property 1: Pending count correctness

declare(strict_types=1);

namespace Tests;

use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/../send_token_reminder.php';

/**
 * PHPUnit tests for the pending-token-email-reminder feature.
 *
 * Property 1: Pending count correctness
 *   For any collection of token records with a mix of "pending" and non-pending
 *   statuses, the count of records with token_status = "pending" must equal
 *   exactly the number of records whose token_status is "pending".
 *
 * Property 2: Email body contains required content
 *   For any (student_name, token, school_name) triple, build_reminder_email()
 *   must return HTML containing all three values and a registration reference.
 */
class PendingTokenReminderTest extends TestCase
{
    // -----------------------------------------------------------------------
    // Property 1: Pending count correctness
    // Feature: pending-token-email-reminder, Property 1: Pending count correctness
    // Validates: Requirements 1.1
    // -----------------------------------------------------------------------

    /**
     * Helper: count pending tokens in an in-memory array (mirrors the SQL query logic).
     *
     * @param array<array{token_status: string}> $records
     */
    private function countPending(array $records): int
    {
        return count(array_filter($records, fn($r) => $r['token_status'] === 'pending'));
    }

    public function testPendingCountAllPending(): void
    {
        // Feature: pending-token-email-reminder, Property 1: Pending count correctness
        $records = [
            ['token_status' => 'pending'],
            ['token_status' => 'pending'],
            ['token_status' => 'pending'],
        ];
        $this->assertSame(3, $this->countPending($records));
    }

    public function testPendingCountNonePending(): void
    {
        // Feature: pending-token-email-reminder, Property 1: Pending count correctness
        $records = [
            ['token_status' => 'used'],
            ['token_status' => 'expired'],
        ];
        $this->assertSame(0, $this->countPending($records));
    }

    public function testPendingCountMixed(): void
    {
        // Feature: pending-token-email-reminder, Property 1: Pending count correctness
        $records = [
            ['token_status' => 'pending'],
            ['token_status' => 'used'],
            ['token_status' => 'pending'],
            ['token_status' => 'expired'],
            ['token_status' => 'pending'],
        ];
        $this->assertSame(3, $this->countPending($records));
    }

    public function testPendingCountEmpty(): void
    {
        // Feature: pending-token-email-reminder, Property 1: Pending count correctness
        $this->assertSame(0, $this->countPending([]));
    }

    /**
     * Property-based test: run 100 random iterations and assert count correctness.
     * Feature: pending-token-email-reminder, Property 1: Pending count correctness
     */
    public function testPendingCountPropertyBased(): void
    {
        // Feature: pending-token-email-reminder, Property 1: Pending count correctness
        $statuses = ['pending', 'used', 'expired', 'active', 'inactive'];

        for ($i = 0; $i < 100; $i++) {
            $records = [];
            $expectedPending = 0;
            $count = random_int(0, 20);

            for ($j = 0; $j < $count; $j++) {
                $status = $statuses[array_rand($statuses)];
                $records[] = ['token_status' => $status];
                if ($status === 'pending') {
                    $expectedPending++;
                }
            }

            $this->assertSame(
                $expectedPending,
                $this->countPending($records),
                "Iteration {$i}: expected {$expectedPending} pending in " . count($records) . " records"
            );
        }
    }

    // -----------------------------------------------------------------------
    // Property 2: Email body contains required content
    // Feature: pending-token-email-reminder, Property 2: Email body contains required content
    // Validates: Requirements 2.4, 4.1, 4.2, 4.3, 4.4
    // -----------------------------------------------------------------------

    public function testBuildReminderEmailContainsStudentName(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $html = build_reminder_email('John Doe', 'ABC123', 'Test School');
        $this->assertStringContainsString('John Doe', $html);
    }

    public function testBuildReminderEmailContainsToken(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $html = build_reminder_email('Jane Smith', 'XYZ789', 'Test School');
        $this->assertStringContainsString('XYZ789', $html);
    }

    public function testBuildReminderEmailContainsSchoolNameInHeaderAndFooter(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $html = build_reminder_email('Alice', 'TOK001', 'Adesina College Ibadan');
        // School name must appear at least twice (header + footer)
        $this->assertGreaterThanOrEqual(
            2,
            substr_count($html, 'Adesina College Ibadan'),
            'School name should appear in both header and footer'
        );
    }

    public function testBuildReminderEmailContainsRegistrationReference(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $html = build_reminder_email('Bob', 'REG999', 'My School');
        $this->assertStringContainsString('registration', strtolower($html));
    }

    public function testBuildReminderEmailIsHtml(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $html = build_reminder_email('Carol', 'HTM001', 'My School');
        $this->assertStringContainsString('<!DOCTYPE html>', $html);
        $this->assertStringContainsString('<html>', $html);
    }

    /**
     * Property-based test: 100 random (name, token, school) triples.
     * Feature: pending-token-email-reminder, Property 2: Email body contains required content
     */
    public function testBuildReminderEmailPropertyBased(): void
    {
        // Feature: pending-token-email-reminder, Property 2: Email body contains required content
        $names   = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank'];
        $tokens  = ['ABC123', 'XYZ789', 'TOK001', 'REG999', 'HTM001', 'QWE456'];
        $schools = ['Adesina College', 'Test School', 'My Academy', 'Sample High'];

        for ($i = 0; $i < 100; $i++) {
            $name   = $names[array_rand($names)];
            $token  = $tokens[array_rand($tokens)];
            $school = $schools[array_rand($schools)];

            $html = build_reminder_email($name, $token, $school);

            $this->assertStringContainsString($name,  $html, "Iteration {$i}: name missing");
            $this->assertStringContainsString($token, $html, "Iteration {$i}: token missing");
            $this->assertGreaterThanOrEqual(
                2,
                substr_count($html, $school),
                "Iteration {$i}: school name should appear in header and footer"
            );
            $this->assertStringContainsString(
                'registration',
                strtolower($html),
                "Iteration {$i}: registration reference missing"
            );
        }
    }
}
