<?php

namespace Tests\Feature\Api\V1\FullSurface;

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

class ApiDeepDocumentationAndRouteParityContractTest extends FullSurfaceE2EContractCase
{
    public function test_every_named_api_route_has_a_stable_route_name_or_explicit_public_exception(): void
    {
        $unnamed = [];

        foreach ($this->apiRoutes() as $route) {
            $uri = $route->uri();

            if ($route->getName() !== null) {
                continue;
            }

            if ($this->isAllowedUnnamedRoute($uri)) {
                continue;
            }

            $unnamed[] = $this->primaryMethod($route) . ' ' . $uri;
        }

        $this->assertSame([], $unnamed, "API routes should be nameable for docs, client generation, and redirects. Unnamed routes:\n" . implode("\n", $unnamed));
    }

    public function test_openapi_or_docs_catalog_endpoint_is_reachable_and_route_table_remains_documentable(): void
    {
        $docsChecks = [
            ['GET', '/api/documentation'],
            ['GET', '/api/documentation/swagger'],
            ['GET', '/api/documentation/swagger.json'],
            ['GET', '/api/docs/public'],
        ];

        $failures = [];
        foreach ($docsChecks as [$method, $uri]) {
            $response = $this->requestAs($this->admin, $method, $uri);

            if ($response->getStatusCode() >= 500) {
                $failures[] = "$method $uri docs endpoint crashed: " . $response->getContent();
            }
        }

        $this->assertSame([], $failures, "Documentation endpoints must not be decorative rubble:\n" . implode("\n", $failures));
    }

    public function test_route_use_case_catalog_covers_new_deep_contract_domains(): void
    {
        $coverageNeedles = [
            'import' => ['upload', 'import', 'attachment', 'document'],
            'export' => ['download', 'export', 'pdf', 'print'],
            'pagination' => ['students', 'users', 'invoices', 'inventory', 'messages'],
            'state_transition' => ['close', 'reopen', 'approve', 'archive', 'resolve', 'lock', 'unlock'],
            'identity' => ['auth', 'login', 'logout', 'me', 'roles'],
        ];

        foreach ($coverageNeedles as $label => $needles) {
            $this->assertNotSame([], $this->apiRoutesContainingAny($needles), "The $label contract bucket should map to at least one route.");
        }
    }

    private function isAllowedUnnamedRoute(string $uri): bool
    {
        return str_contains($uri, 'api/v1')
            || str_contains($uri, 'login')
            || str_contains($uri, 'register')
            || str_contains($uri, 'proofread')
            || str_contains($uri, 'emails')
            || str_contains($uri, 'compare');
    }
}
