<?php

namespace Tests\Feature\Api\V1\FullSurface;

use Illuminate\Routing\Route as LaravelRoute;
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;

class ApiSecurityAndTransportContractTest extends FullSurfaceE2EContractCase
{
    public function test_authenticated_api_routes_do_not_accept_plain_browser_session_assumptions(): void
    {
        foreach ($this->representativeProtectedRoutes() as $route) {
            $method = $this->primaryMethod($route);
            $uri = $route->uri();

            $response = $this->json($method, $this->materializePath($uri), $this->payloadFor($method, $uri));

            $this->assertStatusIn($response, [200, 302, 401, 403, 404, 405, 419, 422], "$method $uri unauthenticated transport contract");
            $this->assertNoServerError($response, "$method $uri unauthenticated transport contract");
        }
    }

    public function test_state_changing_api_routes_reject_wrong_http_method_cleanly(): void
    {
        foreach ($this->stateChangingRoutes() as $route) {
            $uri = $route->uri();

            $response = $this->requestAs($this->actorFor($uri), 'GET', $uri);

            $this->assertStatusIn($response, [200, 302, 401, 403, 404, 405, 409, 419, 422], "GET against mutating route $uri");
            $this->assertNoServerError($response, "GET against mutating route $uri");
        }
    }

    public function test_options_preflight_for_major_domains_is_controlled(): void
    {
        foreach ($this->majorDomainPaths() as $path) {
            $response = $this->json('OPTIONS', $path, [], [
                'Origin' => 'https://frontend.example.test',
                'Access-Control-Request-Method' => 'POST',
                'Access-Control-Request-Headers' => 'Authorization, Content-Type',
            ]);

            $this->assertStatusIn($response, [200, 204, 301, 302, 401, 403, 404, 405, 419], "OPTIONS $path");
            $this->assertNoServerError($response, "OPTIONS $path");
        }
    }

    /** @return list<LaravelRoute> */
    private function representativeProtectedRoutes(): array
    {
        return array_slice(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
            $uri = $route->uri();

            return ! str_contains($uri, 'login')
                && ! str_contains($uri, 'register')
                && ! str_contains($uri, 'captcha')
                && ! str_contains($uri, 'docs')
                && ! str_contains($uri, 'health')
                && ! str_contains($uri, 'public');
        }), 0, 60);
    }

    /** @return list<LaravelRoute> */
    private function stateChangingRoutes(): array
    {
        return array_slice(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
            return in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH', 'DELETE'], true);
        }), 0, 60);
    }

    /** @return list<string> */
    private function majorDomainPaths(): array
    {
        return [
            '/api/v1/auth/login',
            '/api/v1/users',
            '/api/v1/students',
            '/api/v1/classes',
            '/api/v1/attendance',
            '/api/v1/finance/reports',
            '/api/v1/inventory/items',
            '/api/v1/messages',
            '/api/v1/support',
            '/api/v1/parents/profile',
        ];
    }
}
