<?php

namespace Tests\Feature;

use App\Models\BannedIp;
use App\Models\Setting;
use App\Services\BruteforceGuard;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class BruteforceGuardBanTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush();
    }

    private function guard(): BruteforceGuard
    {
        return app(BruteforceGuard::class);
    }

    public function test_records_until_threshold_then_bans(): void
    {
        $g = $this->guard();
        Setting::put('bruteforce_maxretry', '3');

        $g->record('203.0.113.5', 'login');
        $g->record('203.0.113.5', 'login');
        $this->assertFalse($g->isBanned('203.0.113.5'));

        $g->record('203.0.113.5', 'login'); // 3rd → ban
        $this->assertTrue($g->isBanned('203.0.113.5'));
        $this->assertDatabaseHas('banned_ips', ['ip' => '203.0.113.5', 'reason' => 'login']);
        $this->assertDatabaseHas('audit_events', ['action' => 'auth.ip_banned', 'ip' => '203.0.113.5']);
    }

    public function test_exempt_ip_is_never_banned(): void
    {
        $g = $this->guard();
        Setting::put('bruteforce_maxretry', '1');
        for ($i = 0; $i < 5; $i++) {
            $g->record('192.168.1.50', 'login'); // private = default whitelist
        }
        $this->assertFalse($g->isBanned('192.168.1.50'));
        $this->assertDatabaseCount('banned_ips', 0);
    }

    public function test_disabled_records_nothing(): void
    {
        Setting::put('bruteforce_enabled', '0');
        Setting::put('bruteforce_maxretry', '1');
        $this->guard()->record('203.0.113.9', 'login');
        $this->assertDatabaseCount('banned_ips', 0);
    }

    public function test_unban_clears_row_and_cache(): void
    {
        $g = $this->guard();
        BannedIp::create(['ip' => '203.0.113.5', 'banned_until' => now()->addHour(), 'attempts' => 10]);
        $this->assertTrue($g->isBanned('203.0.113.5')); // populates cache
        $g->unban('203.0.113.5');
        $this->assertFalse($g->isBanned('203.0.113.5')); // no stale positive
        $this->assertDatabaseCount('banned_ips', 0);
    }

    public function test_expired_ban_is_not_active(): void
    {
        BannedIp::create(['ip' => '203.0.113.5', 'banned_until' => now()->subMinute(), 'attempts' => 10]);
        $this->assertFalse($this->guard()->isBanned('203.0.113.5'));
    }

    public function test_ipv4_mapped_ipv6_shares_one_ban(): void
    {
        $g = $this->guard();
        Setting::put('bruteforce_maxretry', '1');
        $g->record('203.0.113.5', 'login');                      // ban via plain IPv4
        $this->assertTrue($g->isBanned('::ffff:203.0.113.5'));   // mapped form is the same ban
    }
}
