# `ParentController` plan

## Scope
- **Controller**: `app/Http/Controllers/Api/Parents/ParentController.php`
- **Purpose**: parent portal endpoints for attendance, invoices, enrollments, registration, emergency contacts, profile, and event participation.
- **Primary dependencies**:
  - `App\Services\Parents\ParentAttendanceService`
  - `App\Services\Parents\ParentInvoiceService`
  - `App\Services\Parents\ParentEnrollmentService`
  - `App\Services\Parents\ParentRegistrationService`
  - `App\Services\Parents\ParentEmergencyContactService`
  - `App\Services\Parents\ParentProfileService`
  - `App\Services\Parents\ParentEventParticipationService`

## Routes
All routes are under `v1` + `parents` group:
- Base prefix: `GET/POST/PATCH/DELETE /api/v1/parents/...`
- Group middleware: `parent.access`

Endpoints:
- `GET /api/v1/parents/attendance` → `attendance`
- `GET /api/v1/parents/invoices` → `invoices`
- `GET /api/v1/parents/enrollments` → `enrollments`
- `POST /api/v1/parents/enrollments` → `updateEnrollments`
- `GET /api/v1/parents/registration` → `registration`
- `POST /api/v1/parents/registration` → `storeRegistration`
- `PATCH /api/v1/parents/students/{studentId}` → `updateStudent`
- `DELETE /api/v1/parents/students/{studentId}` → `deleteStudent`
- `GET /api/v1/parents/emergency-contacts` → `emergencyContacts`
- `POST /api/v1/parents/emergency-contacts` → `storeEmergencyContact`
- `PATCH /api/v1/parents/emergency-contacts/{contactId}` → `updateEmergencyContact`
- `GET /api/v1/parents/profile` → `profile`
- `PATCH /api/v1/parents/profile` → `updateProfile`
- `GET /api/v1/parents/events` → `events`
- `POST /api/v1/parents/events/participation` → `updateParticipation`

## Authentication & authorization
- Uses `parent.access` middleware which resolves “secondary”/“authorized” users to a primary parent id and stores it in the request attribute `primary_parent_id`.
- Controller helper `parentIdOrUnauthorized()`:
  - Prefer `request()->attributes->get('primary_parent_id')`
  - Fallback to `auth()->id()`
  - On failure returns JSON `{ ok:false, message:"Unauthorized." }` with `401`

## Requests/validation
Validated via `FormRequest` classes per endpoint:
- `attendance`: `ParentAttendanceRequest` (optional `school_year`)
- `invoices`: `ParentInvoiceRequest` (optional `school_year`)
- `enrollments`: `ParentEnrollmentRequest` (optional `school_year`)
- `updateEnrollments`: `ParentEnrollmentActionRequest` (`enroll[]`, `withdraw[]`)
- `storeRegistration`: `ParentRegistrationRequest` (`students[]`, `emergency_contacts[]`)
- `updateStudent`: `ParentStudentUpdateRequest`
- `store/update emergency contact`: `ParentEmergencyContactRequest`
- `updateProfile`: `ParentProfileUpdateRequest`
- `updateParticipation`: `ParentEventParticipationRequest` (`participation[]`)

## Response shapes (legacy `{ ok: ... }`)
This controller largely returns a legacy parent-portal shape rather than the Base API envelope.

- `attendance`:
  - `{ ok:true, attendance:[...], schoolYears:[...], selectedYear }`
  - `attendance` uses `ParentAttendanceResource::collection(...)`
- `invoices`:
  - `{ ok:true, invoices:[...] }` (`InvoiceResource::collection(...)`)
- `enrollments`:
  - `{ ok:true, students:[...], schoolYears:[...], selectedYear, withdrawalDeadline, lastDayOfRegistration, schoolStartDate }`
- `updateEnrollments`:
  - `{ ok:true, enrolled:[...], withdrawn:[...] }`
- `registration`:
  - `{ ok:true, parent, existingKids:[...], emergencies:[...], maxChilds, maxEmergency, lastDayOfRegistration, registrationAgeDeadline }`
- `storeRegistration`:
  - `{ ok:true, created_student_ids:[...], skipped:[...] }` with status `201` if created_count>0 else `200`
- `updateStudent` / `deleteStudent` / `updateParticipation`:
  - `{ ok:true }`
- `emergencyContacts`:
  - `{ ok:true, contacts:[...] }`
- `store/update emergency contact`:
  - `{ ok:true, contact:{...} }` (store returns `201`)
- `profile` / `updateProfile`:
  - `{ ok:true, profile:{...} }`
- `events`:
  - `{ ok:true, activeEvents, charges, yourStudents }`

## Error handling expectations
- Unauthorized: `{ ok:false, message:"Unauthorized." }` with `401` (from `parentIdOrUnauthorized()`).
- Service-layer exceptions are generally allowed to bubble unless handled in the service; controller does minimal try/catch.

## Test plan (feature)
- Auth/parent.access:
  - Without auth → 401 `{ ok:false }`.
  - With secondary/authorized user → resolved to primary parent id and returns correct data.
- Each endpoint:
  - Valid request returns `ok:true` and expected keys.
  - Validation errors are returned by FormRequest (422).
- Registration:
  - Returns 201 when at least one student is created, 200 when all entries skipped.

