# Sports Module Wizards Refactor Plan

## Context (from DB scan)
- `sa_registrations` currently has: `registration_fee`, `card_fee`, `form_fee`, `total_fees`
- `system_config`: `sa.registration_fee_member=50`, `sa.registration_fee_nonmember=100`, `sa.card_fee=25`, `sa.form_fee=10`
- Current wizard flow: lookup → pay form fee → capture photo → select activity → pay subscription → print form → generate card
- `sa_coaches` has `coach_type` (independent/academy), `employment_type` (staff/contract/freelance), `academy_id`
- `sa_coach_disciplines` links coaches to disciplines
- Swimming discipline_id = 1
- `sa_pool_zone_bookings` handles lane/zone assignments with `max_occupancy`, `current_occupancy`, `ticket_price_member/nonmember`
- `sa_pool_tickets` tracks individual entries per zone booking

---

## SECTION A: Registration Wizard Simplification (مكتب الاشتراكات)

### Business Rule Change
**OLD**: Registration fee = 50 (member) / 100 (non-member) + card_fee (25) + form_fee (10) = multi-step payments
**NEW**: Single flat fee = 100 EGP per person. No card fee, no form fee, no separate charges. One payment covers everything.

### Wizard Flow (Simplified)
1. **Enter player data** (national ID / membership number → auto-fill)
2. **Pay 100 EGP** (single payment, includes form + card + everything)
3. **Capture photo** (webcam or upload)
4. **Select activities** (which disciplines/programs the player WANTS — no group assignment here)
5. **Print form** (استمارة)
6. **DONE** — player is registered, awaiting coach assessment

### What Subscription Desk Does NOT Do
- Does NOT assign player to a specific group
- Does NOT handle monthly subscription payment
- Does NOT determine skill level
- Activity selection is a PREFERENCE not a final assignment

### Changes Required

#### 1. Update `system_config`
```sql
UPDATE system_config SET config_value = '100.00' WHERE config_key = 'sa.registration_fee_member';
UPDATE system_config SET config_value = '100.00' WHERE config_key = 'sa.registration_fee_nonmember';
UPDATE system_config SET config_value = '0.00' WHERE config_key = 'sa.card_fee';
UPDATE system_config SET config_value = '0.00' WHERE config_key = 'sa.form_fee';
```

#### 2. Modify `RegistrationWizardService::calculateFees()`
- Return flat 100 EGP regardless of member/non-member
- card_fee = 0, form_fee = 0, registration_fee = 100

#### 3. Simplify `RegistrationWizardService::startRegistration()`
- Set `form_payment_status = 'paid'` immediately (no separate form payment step)
- total_fees = 100

#### 4. New field: `sa_registrations.selected_disciplines` (JSON)
- Stores the disciplines the player indicated interest in
- This is NOT enrollment — just preference recording

#### 5. Remove `submitFormPayment` step from wizard UI
- The 100 EGP covers everything, paid in one shot

#### 6. Remove group selection from this wizard
- The `selectActivity` step becomes "select disciplines of interest" not "select specific group"
- No monthly fee calculation here

#### 7. Wizard UI Steps (new flow)
- Step 1: Player lookup / entry (unchanged)
- Step 2: Pay 100 EGP (one button, sent to cashier)
- Step 3: Capture photo
- Step 4: Select disciplines of interest (checkboxes, not dropdown)
- Step 5: Print form
- Step 6: Generate card (if needed)

### Edge Cases (40+)

1. Player already registered (same national ID) — reuse existing player, don't create duplicate
2. Player already paid 100 EGP previously (returning player) — skip payment, show "already paid"
3. Member vs non-member — same fee (100 EGP), but `player_type` still tracked for monthly pricing later
4. National ID is 14 digits — auto-parse DOB + gender
5. National ID is invalid format — show error in Arabic
6. Membership number doesn't exist — treat as non-member
7. Player has no guardian (adult) — guardian fields optional
8. Player is minor (< 18 from NID) — guardian fields required
9. Photo upload fails (file too large) — show size limit error
10. Photo upload fails (wrong format) — accept only jpg/png
11. Webcam capture fails (no camera access) — fallback to file upload
12. Player selects 0 disciplines — require at least 1
13. Player selects disciplines that don't exist — validate against DB
14. Player already has active registrations — show warning but allow
15. Double-click on pay button — idempotent (check if already sent to cashier)
16. Network timeout during payment submission — retry-safe
17. Print form with missing photo — print with placeholder
18. Print form with Arabic text — correct RTL layout
19. Cancel registration mid-way — clean up partial data
20. Player name has special characters — HTML escape properly
21. Multiple registrations same day — unique registration_number per attempt
22. Registration number collision — use transaction + retry
23. Cashier rejects payment — registration stays "pending_payment"
24. Browser back button mid-wizard — handle step regression gracefully
25. Session timeout mid-wizard — save progress, allow resume
26. Player with expired medical certificate — allow registration (medical check is separate)
27. Player with no medical certificate at all — allow (30-day grace)
28. Same person registering for 2nd time (already active) — allow, link to existing player
29. Concurrent registrations of same player by different clerks — first-write-wins
30. Form fee paid in old system — detect `registration_fee_paid = 1` and skip
31. Database connection lost mid-transaction — rollback cleanly
32. Photo path contains spaces or Arabic — URL-encode
33. Card generation before photo — block, require photo first
34. Multiple disciplines selected — store as JSON array
35. Discipline deactivated after selection — handle gracefully in coach wizard
36. Registration opened but never completed (abandoned) — auto-cancel after X days
37. Non-member later becomes member — player_type update doesn't affect registration fee
38. Printing on different paper sizes — form layout responsive to A4
39. QR code on card contains expired data — regenerate on reprint
40. Player already has card from old registration — issue new card number
41. Guardian phone same as player phone — allow
42. Empty full_name_en — optional, don't block
43. National ID starts with 2 or 3 — both valid centuries (1900s/2000s)

---

## SECTION B: Coach/Activity Assessment Wizard (ويزارد المدربين)

### Business Rule
After registration desk completes, the player goes to coaches/activity managers. THEY assess the player's skill level and assign them to the correct group in the correct program.

### Flow
1. **Coach selects a player** (from pending-assessment list)
2. **Coach sees player's preferred disciplines** (from registration)
3. **Coach assesses skill level** (تقييم فني)
4. **Coach assigns to program → group** (based on assessment)
5. **Monthly subscription is created** (auto-generated for the assigned group)
6. **Player is active** and ready to train

### Data Model
- `sa_registrations.status`: add value `'assessed'` (after coach assigns)
- `sa_group_players.assessed_by` (new column): who did the assessment
- `sa_group_players.assessment_notes` (new column): coach's notes
- `sa_group_players.skill_level` (new column): beginner/intermediate/advanced

### New Route: `/sa/coach-assessment`
- GET: List all players with `status = 'completed'` (paid + photo done) awaiting assessment
- GET `/{registration_id}`: Show assessment form for specific player
- POST `/{registration_id}/assess`: Submit assessment + group assignment

### UI Flow
- Table of pending players: name, age, preferred disciplines, registration date
- Click player → see details + preferred disciplines
- Coach selects discipline → program → group
- Coach adds assessment notes + skill level
- Submit → player enrolled in group, subscription created

### Edge Cases (40+)

1. Player paid but no photo — coach can still assess (photo not blocking)
2. Player preferred swimming but coach puts in football — allowed (preference ≠ rule)
3. Group is full (max_capacity reached) — block enrollment, show "full" badge
4. Player already in another group in same program — warn duplicate
5. Player already in same group — block, show error
6. Coach assigns to group from different discipline than preference — allowed with confirmation
7. Monthly subscription start date — first day of current month or enrollment date?
8. Player type affects monthly fee — member gets member rate, non-member gets non-member rate
9. Coach has no permission for this discipline — permission check
10. Assessment submitted twice for same registration — idempotent
11. Player is minor and medical not approved — allow enrollment (30-day grace)
12. Medical grace deadline = today + 30 days from enrollment
13. Group's season_end has passed — don't show in available groups
14. Multiple coaches can assess — any coach with permission can do it
15. Coach's own group vs another coach's group — both allowed
16. Assessment notes are empty — optional field
17. Skill level not selected — required field
18. Subscription amount = group's monthly_fee_member or monthly_fee_nonmember based on player_type
19. Mid-month enrollment — prorate or full month? (implement as config)
20. Player cancels before assessment — registration cancelled, no enrollment
21. Coach accidentally assigns wrong group — add "reassign" option
22. Transfer between groups — existing transfer logic handles it
23. Multiple disciplines selected by player — coach assesses one at a time
24. Player registered for swimming + football — separate assessment for each
25. One assessment per discipline per player per registration
26. Assessment creates enrollment (sa_group_players) with status = 'active'
27. Assessment auto-creates first month subscription
28. Assessment triggers event: `sa.player.assessed`
29. Coach views only players whose preferred disciplines match their own disciplines
30. Admin/super_admin can assess any discipline
31. Assessment date recorded for tracking
32. Reassessment (move to different group) — handled via existing transfer logic
33. Group's min_age / max_age — validate player age at enrollment
34. Player age calculated from DOB, not national ID at assessment time
35. No DOB available — skip age validation
36. Coach assessment page should show group current_count / max_capacity
37. Sort available groups by: has space → same discipline → alphabetical
38. When group becomes full after enrollment — update is_full flag
39. current_count increment on enrollment
40. If subscription generation fails — still enroll player, log error
41. Paused enrollment — cannot be created from assessment (always active)
42. Player has no preferred disciplines — coach sees ALL disciplines
43. Print assessment result — optional PDF

---

## SECTION C: Swimming Coaches Wizard (ويزارد مدربين السباحة)

### Business Rule
Swimming coaches are mostly freelance. They need their own dedicated section under "السباحة" in the sidebar. This wizard adds/manages swimming coaches specifically, and lists ONLY swimming coaches.

### Features
1. **Add swimming coach** — form pre-filtered to swimming discipline
2. **List swimming coaches** — only coaches linked to SWIMMING discipline
3. **Manage lane assignments** — coach books a lane, has max capacity
4. **Extra tickets** — coach brings extra players beyond their lane capacity, pays per-ticket

### Data Model (existing, no new tables needed)
- `sa_coaches` — add coach with `coach_type = 'independent'`, `employment_type = 'freelance'`
- `sa_coach_disciplines` — link to SWIMMING discipline (id=1)
- `sa_pool_zone_bookings` — lane assignment (zone_row = lane number)
- `sa_pool_tickets` — extra entries beyond capacity

### New Routes
- GET `/sa/swimming/coaches` — list swimming coaches
- GET `/sa/swimming/coaches/create` — add swimming coach form
- POST `/sa/swimming/coaches` — store new swimming coach
- GET `/sa/swimming/coaches/{id}` — view coach details + their lane bookings
- GET `/sa/swimming/coaches/{id}/edit` — edit coach

### The Lane/Ticket Cycle
1. Swimming coach reserves a lane (حارة) for a time slot
2. Lane has max_occupancy (e.g., 10 players)
3. Coach brings their own trainees (counted from their group enrollment)
4. Coach brings EXTRA people beyond their lane capacity
5. Extra people pay per-ticket (pool_ticket with `zone_booking_id` linked)
6. Ticket price from `sa_pool_zone_bookings.ticket_price_member/nonmember`

### Route for extra tickets: existing `/sa/pool-tickets/issue`
- Currently supports issuing tickets linked to a zone_booking_id
- Need to verify it checks `current_occupancy < max_occupancy` or allows overflow with ticket

### Edge Cases (40+)

1. Coach added without SWIMMING discipline link — auto-add discipline link
2. Coach already exists in system — detect by national_id, show "already exists"
3. Coach is freelance but works with academy — academy_id optional
4. Coach has multiple disciplines (swimming + football) — still shows in swimming list
5. Coach deactivated — disappears from swimming coach list
6. Lane booked but coach doesn't show up — booking stays "active" (manual cleanup)
7. Lane has max_occupancy = 10, coach has 8 players + brings 2 extra — 2 tickets needed
8. Lane has max_occupancy = 10, coach has 10 players + brings 3 extra — 3 tickets issued
9. Extra person is member — member ticket price applies
10. Extra person is non-member — non-member ticket price applies
11. Extra person has no national ID — guest_name field sufficient
12. Ticket issued but person leaves early — ticket still valid (no partial refund)
13. Ticket cancelled before entry — refund logic (payment_status = refunded)
14. Lane not booked for this hour — cannot issue tickets for non-existent booking
15. Coach books lane for wrong time — admin can edit booking
16. Coach books multiple lanes same time — allowed (big group, two lanes)
17. Two coaches book same lane same time — conflict detection (zone_row + booking_date + time overlap)
18. Lane maintenance — assignment_type = 'maintenance', blocks all bookings
19. Lane booking overlaps with group training booking — show conflict warning
20. Coach with 0 players in group — still can book lane (private training)
21. Swimming coach list should show: name, phone, employment_type, academy, active groups count
22. Coach edit — cannot change discipline assignment in this wizard (use main coach page)
23. Coach national_id duplicate — unique constraint, show error
24. Coach phone is optional — allow empty
25. Coach code auto-generated — `SWIM-COACH-{NNN}`
26. Filter swimming coaches by: all, freelance, staff, academy
27. Search by name or phone
28. Coach's lane bookings history — show past and upcoming
29. Lane booking date in the past — cannot create, show error
30. Lane booking for today but time already passed — allow (late registration)
31. Template expansion creates zone bookings — verify tickets work with template-generated bookings
32. Ticket price = 0 — allowed (free entry for special cases)
33. Ticket for member without member_id — just set is_member = 0
34. Coach's group_id on zone_booking — links to their training group
35. Multiple zone bookings for same coach on same day — different time slots
36. Coach leaves (deactivated) — their future zone bookings stay but show warning
37. Ticket issued to player_id (existing player) vs guest — both paths work
38. Pool ticket entry/exit tracking — entry_time/exit_time populated on scan
39. Zone booking cancelled — all linked tickets should be cancelled
40. Current_occupancy exceeds max — soft limit (allow with warning, not block)
41. Swimming coaches see only their own lane bookings (unless admin)
42. Sidebar link shows count badge: pending tickets to process

---

## SECTION D: Migration Plan

### Migration 1: Update system_config (registration fees)
```sql
UPDATE system_config SET config_value = '100.00' WHERE config_key = 'sa.registration_fee_member';
UPDATE system_config SET config_value = '100.00' WHERE config_key = 'sa.registration_fee_nonmember';
UPDATE system_config SET config_value = '0.00' WHERE config_key = 'sa.card_fee';
UPDATE system_config SET config_value = '0.00' WHERE config_key = 'sa.form_fee';
```

### Migration 2: Add `selected_disciplines` to `sa_registrations`
```sql
ALTER TABLE sa_registrations ADD COLUMN selected_disciplines JSON NULL AFTER group_id;
```

### Migration 3: Add assessment columns to `sa_group_players`
```sql
ALTER TABLE sa_group_players ADD COLUMN assessed_by BIGINT UNSIGNED NULL AFTER created_by;
ALTER TABLE sa_group_players ADD COLUMN assessment_notes TEXT NULL AFTER assessed_by;
ALTER TABLE sa_group_players ADD COLUMN skill_level VARCHAR(20) NULL AFTER assessment_notes;
ALTER TABLE sa_group_players ADD COLUMN assessed_at TIMESTAMP NULL AFTER skill_level;
```

### No new tables needed — everything works with existing schema + minor column additions.

---

## SECTION E: Execution Order

1. Run migrations (config update + 2 ALTER TABLEs)
2. Refactor `RegistrationWizardService` — flat 100 EGP, no form/card fees, discipline selection instead of group
3. Update wizard UI — remove form payment step, change activity selection to discipline checkboxes
4. Create Coach Assessment Wizard (new controller + views)
5. Create Swimming Coaches section (new controller + views under swimming)
6. Update sidebar with new navigation items
7. Test all flows end-to-end

---

## SECTION F: Files to Create/Modify

### Modify:
- `app/Modules/SportsActivity/Services/RegistrationWizardService.php` — fee logic + discipline selection
- `app/Modules/SportsActivity/Controllers/RegistrationWizardController.php` — remove form payment step
- `app/Modules/SportsActivity/Views/registration/wizard.php` — simplified wizard UI
- `app/Modules/SportsActivity/bootstrap.php` — new menu items + permissions
- `app/Modules/SportsActivity/Routes.php` — new routes

### Create:
- `database/migrations/Phase_103_001_sports_wizard_refactor.php` — all migrations
- `app/Modules/SportsActivity/Controllers/CoachAssessmentController.php` — new wizard
- `app/Modules/SportsActivity/Views/assessment/index.php` — pending players list
- `app/Modules/SportsActivity/Views/assessment/assess.php` — assessment form
- `app/Modules/SportsActivity/Controllers/Swimming/SwimmingCoachController.php` — swimming coaches CRUD
- `app/Modules/SportsActivity/Views/swimming/coaches/index.php` — list
- `app/Modules/SportsActivity/Views/swimming/coaches/form.php` — create/edit
- `app/Modules/SportsActivity/Views/swimming/coaches/show.php` — details + lane bookings
- `app/Modules/SportsActivity/Services/CoachAssessmentService.php` — assessment logic
