<?php

declare(strict_types=1);

namespace App\Tests\Integration\App;

use App\Auth\UserContext;
use App\Tests\Integration\Support\AppTestCase;

final class IpsPageTest extends AppTestCase
{
    protected function setUp(): void
    {
        $this->bootApp();
        // The first /app/* request in a fresh process triggers
        // session_start() and clobbers the $_SESSION values primed
        // here. Hit a public route first so the session is active
        // before any test request fires.
        $this->request('GET', '/healthz');
        $_SESSION['_user'] = (new UserContext(1, 'Admin', 'admin', null, UserContext::SOURCE_LOCAL))->toArray();
        $_SESSION['_last_active'] = time();
        $_SESSION['_authenticated_at'] = time();
    }

    public function testListPageRendersResults(): void
    {
        $this->enqueueApiResponse(200, [
            'page' => 1,
            'page_size' => 25,
            'total' => 2,
            'items' => [
                [
                    'ip' => '203.0.113.10',
                    'is_ipv4' => true,
                    'max_score' => 1.5,
                    'top_category' => 'brute_force',
                    'pair_count' => 1,
                    'last_report_at' => '2026-04-29T10:00:00Z',
                    'status' => 'scored',
                    'enrichment' => null,
                ],
                [
                    'ip' => '2001:db8::1',
                    'is_ipv4' => false,
                    'max_score' => 0.8,
                    'top_category' => 'spam',
                    'pair_count' => 1,
                    'last_report_at' => '2026-04-29T09:55:00Z',
                    'status' => 'scored',
                    'enrichment' => null,
                ],
            ],
        ]);
        // listCountries() — issued after searchIps() by the controller.
        $this->enqueueApiResponse(200, ['items' => []]);

        $response = $this->request('GET', '/app/ips');

        self::assertSame(200, $response->getStatusCode());
        $body = (string) $response->getBody();
        self::assertStringContainsString('203.0.113.10', $body);
        self::assertStringContainsString('2001:db8::1', $body);
        self::assertStringContainsString('brute_force', $body);
        self::assertStringContainsString('2 total', $body);
    }

    public function testListPageRendersEmptyState(): void
    {
        $this->enqueueApiResponse(200, [
            'page' => 1,
            'page_size' => 25,
            'total' => 0,
            'items' => [],
        ]);
        $this->enqueueApiResponse(200, ['items' => []]);

        $response = $this->request('GET', '/app/ips');

        self::assertSame(200, $response->getStatusCode());
        self::assertStringContainsString('No results', (string) $response->getBody());
    }

    public function testListPagePassesFiltersThrough(): void
    {
        $this->enqueueApiResponse(200, ['page' => 1, 'page_size' => 25, 'total' => 0, 'items' => []]);
        $this->enqueueApiResponse(200, ['items' => []]);

        $response = $this->request('GET', '/app/ips?q=2001&category=spam');
        $body = (string) $response->getBody();

        self::assertSame(200, $response->getStatusCode());
        // The filter form preserves the user's selection.
        self::assertMatchesRegularExpression('/value="2001"/', $body);
        self::assertMatchesRegularExpression('/<option value="spam"\s+selected/', $body);
    }

    public function testDetailPageRendersScoresAndHistory(): void
    {
        $this->enqueueApiResponse(200, [
            'ip' => '203.0.113.10',
            'is_ipv4' => true,
            'status' => 'scored',
            'scores' => [
                ['category' => 'brute_force', 'category_id' => 1, 'score' => 1.5, 'last_report_at' => '2026-04-29T10:00:00Z', 'report_count_30d' => 5],
            ],
            'enrichment' => ['country_code' => null, 'asn' => null, 'as_org' => null, 'enriched_at' => null],
            'manual_block' => null,
            'allowlist' => null,
            'history' => [
                ['type' => 'report', 'at' => '2026-04-29T10:00:00Z', 'category' => 'brute_force', 'reporter' => 'web-prod-01', 'weight' => 1.0, 'metadata' => null],
            ],
            'has_more' => false,
        ]);
        $this->enqueueApiResponse(200, [
            'items' => [
                ['slug' => 'brute_force', 'decay_function' => 'exponential', 'decay_param' => 14],
            ],
            'total' => 1,
        ]);

        $response = $this->request('GET', '/app/ips/203.0.113.10');

        self::assertSame(200, $response->getStatusCode());
        $body = (string) $response->getBody();
        self::assertStringContainsString('203.0.113.10', $body);
        self::assertStringContainsString('brute_force', $body);
        self::assertStringContainsString('web-prod-01', $body);
        self::assertStringContainsString('Score per category', $body);
        self::assertStringContainsString('Score over time', $body);
        self::assertStringContainsString('History', $body);
    }

    public function testDetailPageReturnsTwig404OnApiNotFound(): void
    {
        // Use an IP-shaped value so the SEC_REVIEW F43 route regex
        // (`[0-9a-fA-F.:%]+`) accepts it and the request reaches the
        // controller — the test is about the controller's
        // ApiNotFoundException → Twig 404 path, not the route layer.
        $this->enqueueApiResponse(404, ['error' => 'not_found']);

        $response = $this->request('GET', '/app/ips/198.51.100.99');

        self::assertSame(404, $response->getStatusCode());
        self::assertStringContainsString('IP not found', (string) $response->getBody());
    }

    public function testRedirectsToLoginWhenAnonymous(): void
    {
        $_SESSION = []; // wipe seeded user

        $response = $this->request('GET', '/app/ips');

        self::assertSame(302, $response->getStatusCode());
        self::assertSame('/login', $response->getHeaderLine('Location'));
    }
}
