<?php

namespace Tests\Feature\Api\V1\FullSurface;

use App\Models\User;
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;

class ApiRoleWorkflowNegativeMatrixContractTest extends FullSurfaceE2EContractCase
{
    public function test_each_non_admin_actor_is_blocked_from_administrative_mutations(): void
    {
        $adminMutationRoutes = array_filter($this->apiRoutesContainingAny([
            'users', 'roles', 'permissions', 'school-years', 'configuration', 'settings', 'inventory', 'finance', 'reimbursement', 'suppliers',
        ]), fn ($route) => in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH', 'DELETE'], true));

        $actors = [
            'teacher' => $this->teacher,
            'parent' => $this->parent,
            'student' => $this->studentUser,
        ];

        foreach ($actors as $label => $actor) {
            foreach (array_slice($adminMutationRoutes, 0, 60) as $route) {
                $method = $this->primaryMethod($route);
                $uri = $route->uri();
                $response = $this->requestAs($actor, $method, $uri, $this->payloadFor($method, $uri));

                $this->assertNoServerError($response, "$label $method $uri");
                $this->assertStatusIn($response, [302, 400, 401, 403, 404, 405, 409, 419, 422], "$label must not perform admin mutation $method $uri");
            }
        }
    }

    public function test_actor_specific_read_routes_do_not_collapse_into_admin_visible_data(): void
    {
        $expectations = [
            'parent' => [$this->parent, ['admin', 'roles', 'permissions', 'finance/reports', 'inventory']],
            'teacher' => [$this->teacher, ['families', 'parent/invoices', 'school-years/close', 'permissions']],
            'student' => [$this->studentUser, ['teacher', 'parents', 'admin', 'finance', 'users']],
        ];

        foreach ($expectations as $label => [$actor, $needles]) {
            foreach ($this->apiRoutesContainingAny($needles) as $route) {
                $method = $this->primaryMethod($route);
                if ($method !== 'GET') {
                    continue;
                }

                $uri = $route->uri();
                $response = $this->requestAs($actor, $method, $uri);

                $this->assertNoServerError($response, "$label $method $uri");
                $this->assertStatusIn($response, [302, 400, 401, 403, 404, 405, 409, 419, 422], "$label should not read unrelated domain $uri");
            }
        }
    }

    public function test_suspended_disabled_or_unverified_users_do_not_gain_api_access(): void
    {
        $blockedUsers = [
            'inactive' => $this->blockedUser(['status' => 'Inactive']),
            'suspended' => $this->blockedUser(['status' => 'Suspended']),
            'unverified' => $this->blockedUser(['is_verified' => false]),
        ];

        foreach ($blockedUsers as $label => $user) {
            foreach (['api/v1/auth/me', 'api/v1/users', 'api/v1/parent/profile', 'api/v1/teacher/classes'] as $uri) {
                $response = $this->requestAs($user, 'GET', $uri);
                $this->assertNoServerError($response, "$label GET $uri");
                $this->assertStatusIn($response, [200, 302, 400, 401, 403, 404, 419, 422], "$label GET $uri");
            }
        }
    }

    private function blockedUser(array $overrides): User
    {
        return $this->createApiUserWithRole('parent', array_merge([
            'firstname' => 'Blocked',
            'lastname' => 'User',
            'email' => 'blocked.' . uniqid() . '@example.test',
        ], $overrides));
    }
}
