<?php

declare(strict_types=1);

namespace App\Tests\Integration\Search;

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

/**
 * `/app/search` — global IP lookup behind the topnav search box.
 *
 * The controller fans out to three admin endpoints in this order:
 *   1. GET /api/v1/admin/ips
 *   2. GET /api/v1/admin/manual-blocks
 *   3. GET /api/v1/admin/allowlist
 *
 * Tests queue mock responses for that exact sequence.
 */
final class SearchPageTest extends AppTestCase
{
    protected function setUp(): void
    {
        $this->bootApp();
        // The first /app/* request in a process triggers session_start(),
        // which would clobber any $_SESSION values set here. Hit a public
        // route first so the session is already active by the time our
        // 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 testRendersEmptyFormWithoutQuery(): void
    {
        $resp = $this->request('GET', '/app/search');

        self::assertSame(200, $resp->getStatusCode());
        $body = (string) $resp->getBody();
        self::assertStringContainsString('Enter an IP address or prefix', $body);
        self::assertStringContainsString('name="q"', $body);
    }

    public function testRendersResultsForQuery(): void
    {
        // 1) IPs
        $this->enqueueApiResponse(200, [
            'page' => 1,
            'page_size' => 25,
            'total' => 1,
            'items' => [
                [
                    'ip' => '203.0.113.42',
                    'is_ipv4' => true,
                    'max_score' => 4.5,
                    'top_category' => 'brute_force',
                    'pair_count' => 1,
                    'last_report_at' => '2026-04-29T09:00:00Z',
                    'status' => 'scored',
                    'enrichment' => null,
                ],
            ],
        ]);
        // 2) Manual blocks
        $this->enqueueApiResponse(200, [
            'items' => [
                ['id' => 1, 'kind' => 'subnet', 'cidr' => '203.0.113.0/24', 'reason' => 'edge', 'expires_at' => null, 'created_at' => '2026-04-29T10:00:00Z', 'created_by_user_id' => null],
                ['id' => 2, 'kind' => 'ip', 'ip' => '198.51.100.7', 'reason' => 'unrelated', 'expires_at' => null, 'created_at' => '2026-04-29T10:00:00Z', 'created_by_user_id' => null],
            ],
            'total' => 2,
        ]);
        // 3) Allowlist
        $this->enqueueApiResponse(200, [
            'items' => [
                ['id' => 9, 'kind' => 'ip', 'ip' => '203.0.113.42', 'reason' => 'office', 'created_at' => '2026-04-28T10:00:00Z', 'created_by_user_id' => null],
            ],
            'total' => 1,
        ]);

        $resp = $this->request('GET', '/app/search?q=203.0.113');

        self::assertSame(200, $resp->getStatusCode());
        $body = (string) $resp->getBody();

        // IP result row + link to filtered IPs page.
        self::assertStringContainsString('203.0.113.42', $body);
        self::assertStringContainsString('/app/ips?q=203.0.113', $body);
        // Manual block row that matched the query.
        self::assertStringContainsString('203.0.113.0/24', $body);
        // Unrelated manual-block entry must not render.
        self::assertStringNotContainsString('198.51.100.7', $body);
        // Section links to the index pages.
        self::assertStringContainsString('href="/app/manual-blocks"', $body);
        self::assertStringContainsString('href="/app/allowlist"', $body);
    }

    public function testHandlesPartialApiFailureGracefully(): void
    {
        // IPs succeeds, manual-blocks 500s (twice — ApiClient retries on 5xx),
        // allowlist succeeds.
        $this->enqueueApiResponse(200, ['page' => 1, 'page_size' => 25, 'total' => 0, 'items' => []]);
        $this->enqueueApiResponse(500, ['error' => 'boom']);
        $this->enqueueApiResponse(500, ['error' => 'boom']);
        $this->enqueueApiResponse(200, ['items' => [], 'total' => 0]);

        $resp = $this->request('GET', '/app/search?q=10.0.0.1');

        self::assertSame(200, $resp->getStatusCode());
        $body = (string) $resp->getBody();
        self::assertStringContainsString('Failed to load manual blocks', $body);
        // The other sections still render their no-results state.
        self::assertStringContainsString('No IPs match this query.', $body);
        self::assertStringContainsString('No allowlist entries match this query.', $body);
    }

    public function testRedirectsAnonymousToLogin(): void
    {
        $_SESSION = [];
        $resp = $this->request('GET', '/app/search?q=foo');
        self::assertSame(302, $resp->getStatusCode());
        self::assertSame('/login', $resp->getHeaderLine('Location'));
    }
}
