<?php

namespace Tests\Feature\Api\V1\FullSurface;

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

class ApiOwnerBoundaryAndPrivacyExpansionTest extends FullSurfaceE2EContractCase
{
    public function test_parent_family_endpoints_do_not_expose_other_parent_records(): void
    {
        $other = $this->seedOtherFamily();

        foreach ($this->otherFamilyPaths($other['student_id'], $other['parent_id']) as $path) {
            $response = $this->requestAs($this->parent, 'GET', $path);

            $this->assertStatusIn($response, [200, 302, 401, 403, 404, 405, 419, 422], "parent privacy $path");
            $this->assertNoServerError($response, "parent privacy $path");

            if ($response->getStatusCode() === 200 && $this->isJsonString($response->getContent())) {
                $body = json_encode($response->json(), JSON_THROW_ON_ERROR);
                $this->assertStringNotContainsString((string) $other['student_id'], $body, "$path exposed another family student id.");
                $this->assertStringNotContainsString($other['email'], $body, "$path exposed another family email.");
            }
        }
    }

    public function test_teacher_class_endpoints_do_not_expose_unassigned_class_records(): void
    {
        $other = $this->seedTeacherClassWithStudent(9802);
        $unassignedTeacher = $this->createApiUserWithRole('teacher');

        foreach ($this->teacherClassPaths((int) $other['class_section_id'], (int) $other['student_id']) as $path) {
            $response = $this->requestAs($unassignedTeacher, 'GET', $path);

            $this->assertStatusIn($response, [200, 302, 401, 403, 404, 405, 419, 422], "teacher class isolation $path");
            $this->assertNoServerError($response, "teacher class isolation $path");

            if ($response->getStatusCode() === 200 && $this->isJsonString($response->getContent())) {
                $body = json_encode($response->json(), JSON_THROW_ON_ERROR);
                $this->assertStringNotContainsString((string) $other['student_id'], $body, "$path exposed unassigned student id.");
            }
        }
    }

    public function test_serialized_users_do_not_leak_sensitive_auth_material(): void
    {
        foreach ($this->identityAndDirectoryPaths() as $path) {
            $response = $this->requestAs($this->admin, 'GET', $path);

            $this->assertStatusIn($response, [200, 204, 302, 401, 403, 404, 405, 419, 422], "sensitive serialization $path");
            $this->assertNoServerError($response, "sensitive serialization $path");

            if ($response->getStatusCode() === 200) {
                $content = strtolower($response->getContent());
                $this->assertStringNotContainsString('password_hash', $content, "$path leaked password_hash.");
                $this->assertStringNotContainsString('remember_token', $content, "$path leaked remember_token.");
                $this->assertStringNotContainsString('api_token', $content, "$path leaked api_token.");
                $this->assertStringNotContainsString('two_factor_secret', $content, "$path leaked two_factor_secret.");
            }
        }
    }

    /** @return array{parent_id:int, student_id:int, email:string} */
    private function seedOtherFamily(): array
    {
        $otherParent = $this->createApiUserWithRole('parent', [
            'email' => 'other.family.' . uniqid() . '@example.test',
            'firstname' => 'Other',
            'lastname' => 'Family',
        ]);

        $studentId = DB::table('students')->insertGetId([
            'school_id' => 'S-OTHER-' . random_int(1000, 9999),
            'firstname' => 'Other',
            'lastname' => 'Student',
            'dob' => '2016-01-01',
            'age' => 9,
            'gender' => 'Female',
            'parent_id' => $otherParent->id,
            'year_of_registration' => '2025',
            'school_year' => self::E2E_SCHOOL_YEAR,
            'semester' => self::E2E_SEMESTER,
            'is_active' => 1,
        ]);

        return ['parent_id' => $otherParent->id, 'student_id' => $studentId, 'email' => $otherParent->email];
    }

    /** @return list<string> */
    private function otherFamilyPaths(int $studentId, int $parentId): array
    {
        return [
            "api/v1/parents/students/$studentId",
            "api/v1/parents/students/$studentId/attendance",
            "api/v1/parents/students/$studentId/invoices",
            "api/v1/parents/$parentId/profile",
            "api/v1/families/$parentId",
            "api/v1/emergency-contacts/student/$studentId",
        ];
    }

    /** @return list<string> */
    private function teacherClassPaths(int $classSectionId, int $studentId): array
    {
        return [
            "api/v1/teacher/classes/$classSectionId/roster",
            "api/v1/teacher/classes/$classSectionId/progress",
            "api/v1/teacher/students/$studentId/scores",
            "api/v1/teacher/classes/$classSectionId/attendance",
            "api/v1/class-progress/$classSectionId",
        ];
    }

    /** @return list<string> */
    private function identityAndDirectoryPaths(): array
    {
        return [
            'api/v1/auth/me',
            'api/v1/users',
            'api/v1/parents/profile',
            'api/v1/teachers/classes',
            'api/v1/students',
        ];
    }
}
