<?php

namespace Tests\Feature\Api\V1\FullSurface;

use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;

class ApiQueryParameterFuzzingContractTest extends FullSurfaceE2EContractCase
{
    public function test_collection_endpoints_handle_hostile_query_parameters_without_5xx(): void
    {
        foreach ($this->collectionPaths() as $path) {
            foreach ($this->hostileQueries() as $query) {
                $response = $this->requestAs($this->actorFor($path), 'GET', $path . '?' . http_build_query($query));

                $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], "hostile query $path");
                $this->assertNoServerError($response, "hostile query $path");
            }
        }
    }

    public function test_date_range_endpoints_reject_inverted_or_impossible_ranges_cleanly(): void
    {
        foreach ($this->dateRangePaths() as $path) {
            foreach ($this->badDateRanges() as $query) {
                $response = $this->requestAs($this->actorFor($path), 'GET', $path . '?' . http_build_query($query));

                $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], "bad date range $path");
                $this->assertNoServerError($response, "bad date range $path");
            }
        }
    }

    public function test_sorting_contract_does_not_allow_raw_sql_fragments(): void
    {
        foreach ($this->collectionPaths() as $path) {
            $response = $this->requestAs($this->actorFor($path), 'GET', $path . '?' . http_build_query([
                'sort' => 'id desc; drop table users; --',
                'direction' => 'sideways',
            ]));

            $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], "SQL-ish sort $path");
            $this->assertNoServerError($response, "SQL-ish sort $path");
            $this->assertStringNotContainsString('SQLSTATE', $response->getContent(), "$path leaked SQLSTATE for malicious sort.");
        }
    }

    /** @return list<string> */
    private function collectionPaths(): array
    {
        return [
            'api/v1/users',
            'api/v1/students',
            'api/v1/classes',
            'api/v1/attendance',
            'api/v1/finance/invoices',
            'api/v1/finance/payments',
            'api/v1/inventory/items',
            'api/v1/messages',
            'api/v1/support',
            'api/v1/report-cards',
        ];
    }

    /** @return list<array<string,mixed>> */
    private function hostileQueries(): array
    {
        return [
            ['page' => -1, 'per_page' => 0],
            ['page' => 'abc', 'per_page' => '999999999999'],
            ['search' => str_repeat('x', 5000)],
            ['status' => ['nested' => ['bad' => true]]],
            ['include' => '../../../.env'],
        ];
    }

    /** @return list<string> */
    private function dateRangePaths(): array
    {
        return [
            'api/v1/attendance',
            'api/v1/finance/reports',
            'api/v1/finance/payments',
            'api/v1/report-cards',
            'api/v1/messages',
        ];
    }

    /** @return list<array<string,string>> */
    private function badDateRanges(): array
    {
        return [
            ['start_date' => '2026-12-31', 'end_date' => '2025-01-01'],
            ['from' => 'not-a-date', 'to' => 'also-not-a-date'],
            ['date' => '0000-00-00'],
            ['school_year' => '../../2025', 'semester' => '<script>alert(1)</script>'],
        ];
    }
}
