<?php
/**
 * Tests for MLF_Rate_Limiter class.
 *
 * @package Maple_Local_Fonts
 */

/**
 * Class Test_MLF_Rate_Limiter
 *
 * @covers MLF_Rate_Limiter
 */
class Test_MLF_Rate_Limiter extends WP_UnitTestCase {

    /**
     * Rate limiter instance.
     *
     * @var MLF_Rate_Limiter
     */
    private $rate_limiter;

    /**
     * Set up test fixtures.
     */
    public function set_up() {
        parent::set_up();
        $this->rate_limiter = new MLF_Rate_Limiter(3, 60); // 3 requests per 60 seconds
    }

    /**
     * Clean up after tests.
     */
    public function tear_down() {
        // Clear any transients
        $this->rate_limiter->clear('test_action');
        parent::tear_down();
    }

    /**
     * Test that first request is not limited.
     */
    public function test_first_request_not_limited() {
        $this->assertFalse($this->rate_limiter->is_limited('test_action'));
    }

    /**
     * Test that remaining count starts at limit.
     */
    public function test_remaining_starts_at_limit() {
        $this->assertEquals(3, $this->rate_limiter->get_remaining('test_action'));
    }

    /**
     * Test that check_and_record allows requests within limit.
     */
    public function test_check_and_record_allows_within_limit() {
        $this->assertTrue($this->rate_limiter->check_and_record('test_action'));
        $this->assertTrue($this->rate_limiter->check_and_record('test_action'));
        $this->assertTrue($this->rate_limiter->check_and_record('test_action'));
    }

    /**
     * Test that check_and_record blocks after limit exceeded.
     */
    public function test_check_and_record_blocks_after_limit() {
        // Use up all allowed requests
        $this->rate_limiter->check_and_record('test_action');
        $this->rate_limiter->check_and_record('test_action');
        $this->rate_limiter->check_and_record('test_action');

        // Next request should be blocked
        $this->assertFalse($this->rate_limiter->check_and_record('test_action'));
    }

    /**
     * Test that remaining count decreases correctly.
     */
    public function test_remaining_decreases() {
        $this->rate_limiter->record_request('test_action');
        $this->assertEquals(2, $this->rate_limiter->get_remaining('test_action'));

        $this->rate_limiter->record_request('test_action');
        $this->assertEquals(1, $this->rate_limiter->get_remaining('test_action'));

        $this->rate_limiter->record_request('test_action');
        $this->assertEquals(0, $this->rate_limiter->get_remaining('test_action'));
    }

    /**
     * Test that is_limited returns true after limit exceeded.
     */
    public function test_is_limited_after_exceeding() {
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');

        $this->assertTrue($this->rate_limiter->is_limited('test_action'));
    }

    /**
     * Test that clear resets the rate limit.
     */
    public function test_clear_resets_limit() {
        // Use up all requests
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');

        $this->assertTrue($this->rate_limiter->is_limited('test_action'));

        // Clear and verify reset
        $this->rate_limiter->clear('test_action');
        $this->assertFalse($this->rate_limiter->is_limited('test_action'));
        $this->assertEquals(3, $this->rate_limiter->get_remaining('test_action'));
    }

    /**
     * Test that different actions are tracked separately.
     */
    public function test_different_actions_tracked_separately() {
        // Use up all requests for action1
        $this->rate_limiter->record_request('action1');
        $this->rate_limiter->record_request('action1');
        $this->rate_limiter->record_request('action1');

        // action1 should be limited
        $this->assertTrue($this->rate_limiter->is_limited('action1'));

        // action2 should not be limited
        $this->assertFalse($this->rate_limiter->is_limited('action2'));
        $this->assertEquals(3, $this->rate_limiter->get_remaining('action2'));

        // Clean up
        $this->rate_limiter->clear('action1');
        $this->rate_limiter->clear('action2');
    }

    /**
     * Test that logged in user uses user ID for tracking.
     */
    public function test_logged_in_user_tracking() {
        // Create and log in a user
        $user_id = $this->factory->user->create(['role' => 'administrator']);
        wp_set_current_user($user_id);

        // Use up requests
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');
        $this->rate_limiter->record_request('test_action');

        $this->assertTrue($this->rate_limiter->is_limited('test_action'));

        // Clean up
        wp_set_current_user(0);
        $this->rate_limiter->clear('test_action');
    }

    /**
     * Test constructor with custom limits.
     */
    public function test_custom_limits() {
        $custom_limiter = new MLF_Rate_Limiter(5, 120);

        $this->assertEquals(5, $custom_limiter->get_remaining('custom_action'));

        // Use 5 requests
        for ($i = 0; $i < 5; $i++) {
            $this->assertTrue($custom_limiter->check_and_record('custom_action'));
        }

        // 6th should be blocked
        $this->assertFalse($custom_limiter->check_and_record('custom_action'));

        // Clean up
        $custom_limiter->clear('custom_action');
    }
}
