<?php

namespace Tests\Feature\Api\V1\FullSurface;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Routing\Route as LaravelRoute;
use Illuminate\Support\Facades\Route;
use Tests\TestCase;

/**
 * Explicit use-case catalog for the complete API surface.
 *
 * This is intentionally stricter than a domain matrix. Each route must map to a
 * named user journey, not merely a controller folder. Controller folders are not
 * product behavior. They are where behavior goes to hide.
 */
class ApiRouteUseCaseCatalogCompletenessTest extends TestCase
{
    use RefreshDatabase;

    public function test_every_api_route_is_owned_by_an_explicit_user_journey(): void
    {
        $unowned = [];

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

            if ($this->journeyFor($signature) === null) {
                $unowned[] = $signature;
            }
        }

        $this->assertSame(
            [],
            $unowned,
            "API routes without an explicit E2E user journey owner:\n" . implode("\n", $unowned) .
            "\n\nAdd the route to the journey catalog and add or extend the scenario that proves it."
        );
    }

    public function test_each_declared_journey_has_at_least_one_registered_route(): void
    {
        $signatures = array_map(fn (LaravelRoute $route): string => $this->routeSignature($route), $this->apiRoutes());
        $missing = [];

        foreach ($this->journeyCatalog() as $journey => $patterns) {
            $matched = false;
            foreach ($signatures as $signature) {
                if ($this->matchesAny($signature, $patterns)) {
                    $matched = true;
                    break;
                }
            }

            if (! $matched) {
                $missing[] = $journey;
            }
        }

        $this->assertSame([], $missing, "Declared E2E journeys with no registered API routes:\n" . implode("\n", $missing));
    }

    /**
     * @return array<string, list<string>>
     */
    private function journeyCatalog(): array
    {
        return [
            'public visitor reads policies, pages, health, docs, winners, certificates, kiosk forms' => [
                '#^(GET|POST) api/(?:documentation|docs|docs/public|access_denied|certificates/verify|timeoff/notify|winners|confirm_authorized_user|set_authorized_user_password)#',
                '#^(GET|POST) api/v1/(?:policies|pages|contact|frontend|health|system/db-check|badge_scan|scanner)#',
            ],
            'guest registers and authenticates through legacy and versioned auth aliases' => [
                '#^POST api/(?:login|register)$#',
                '#^(GET|POST) api/v1/(?:login|register|auth)(?:/|$)#',
            ],
            'user resolves dashboard, landing, preferences, nav, ui, utility, settings context' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:preferences|nav-builder|landing|info-icon|dashboard|utilities|ui|settings|settings/events|settings/school-calendar|configuration|system|stats|role-switcher)(?:/|$)#',
            ],
            'administrator manages users, roles, permissions, school years, enrollment, promotions, alerts' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:administrator|users|role-permissions|school-years|ip-bans|notifications)(?:/|$)#',
            ],
            'administrator manages classes, class sections, assignments, staff, teacher-class relationships' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:class-sections|classes|assignments|staff|teachers)(?:/|$)#',
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/administrator/(?:teacher-class|staff)(?:/|$)#',
            ],
            'teacher runs classroom roster, preparation, progress, submissions, legacy teacher screens' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:teacher|teachers|class-prep|class-progress|subject-curriculum|exam-drafts)(?:/|$)#',
            ],
            'student, parent, family, guardian, authorized-user self-service' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:students|parents|families|family-admin|emergency-contacts|authorized-users)(?:/|$)#',
            ],
            'attendance day operations, safety exceptions, slips, tracking, templates, incidents' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/(?:v1/)?(?:attendance|attendance-tracking|attendance-comment-templates|attendance-templates|administrator/attendance-templates|incidents)(?:/|$)#',
            ],
            'grading, score entry, score comments, report cards, printables, badges, certificates' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:scores|grading|reports|reports/report-cards|reports/slips|reports/stickers|badges|certificates|print-requests|competition-scores|comments|final|homework|midterm|participation|projects|quizzes|exams/drafts|subjects/curriculum)(?:/|$)#',
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/administrator/(?:grading|printables|certificates|trophy)(?:/|$)#',
            ],
            'finance billing, invoices, payments, discounts, refunds, reimbursements, reports, charges' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:finance|invoices|payments|manual-pay|payment-notifications|payment-transactions|paypal|paypal-transactions|purchase-orders|refunds|reimbursements|discounts|expenses|extra-charges|event-charges|fees/charges)(?:/|$)#',
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/administrator/(?:expenses|reports|invoices|paypal-transactions|reimbursements)(?:/|$)#',
            ],
            'inventory, suppliers, supply categories, movements, procurement support' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:inventory|suppliers|supply-categories|files)(?:/|$)#',
            ],
            'communications, broadcast email, extracted email, messages, WhatsApp, support' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/(?:emails|compare|proofread)(?:/|$)#',
                '#^(GET|POST|PUT|PATCH|DELETE) api/v1/(?:messages|broadcast-email|email|email-extractor|communications|support|whatsapp|payment-notifications)(?:/|$)#',
            ],
            'legacy compatibility aliases continue to resolve to canonical behavior' => [
                '#^(GET|POST|PUT|PATCH|DELETE) api/(?:attendance-templates|attendance-comment-templates|administrator/attendance-templates|proofread|emails|compare)(?:/|$)#',
                '#^(GET|POST) api/v1/teacher/(?:homework-list|add-homework|add-quiz|add-quiz-column-form|add-midterm-exam|add-final-exam|add-participation|add-project|teacher-assignment|exam-drafts|drafts|inventory/books/distribute|print-requests/context|class-view|no-classes|select-semester|absence-vacation|showupdate-attendance|showupdate_attendance|scores|calendar|class-progress-submit|class-progress-history|class-progress-view|competition-scores)(?:/|$)#',
            ],
        ];
    }

    private function journeyFor(string $signature): ?string
    {
        foreach ($this->journeyCatalog() as $journey => $patterns) {
            if ($this->matchesAny($signature, $patterns)) {
                return $journey;
            }
        }

        return null;
    }

    /**
     * @param list<string> $patterns
     */
    private function matchesAny(string $signature, array $patterns): bool
    {
        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $signature) === 1) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return list<LaravelRoute>
     */
    private function apiRoutes(): array
    {
        return array_values(array_filter(Route::getRoutes()->getRoutes(), function (LaravelRoute $route): bool {
            return str_starts_with($route->uri(), 'api/');
        }));
    }

    private function routeSignature(LaravelRoute $route): string
    {
        $methods = array_values(array_diff($route->methods(), ['HEAD', 'OPTIONS']));
        sort($methods);

        return implode(',', $methods) . ' ' . $route->uri();
    }
}
