# Marathon & Competition Workflows — Implementation Roadmap

> **Analysis & Planning Document**
> Compares CURRENT implementation against APPROVED business workflows (docs/business/Marathon_Competition_Workflows.md)
> No code has been modified. This document is the plan for the next development phase.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Current State Assessment](#2-current-state-assessment)
3. [Workflow-by-Workflow Gap Analysis](#3-workflow-by-workflow-gap-analysis)
4. [Implementation Phases](#4-implementation-phases)
5. [Cross-Cutting Concerns](#5-cross-cutting-concerns)
6. [Risk Assessment](#6-risk-assessment)
7. [Appendix: Referenced Source Files](#7-appendix-referenced-source-files)

---

## 1. Executive Summary

### Module Maturity Score: **72%**

| Workflow Area | Score | Notes |
|--------------|-------|-------|
| Marathon System/Program Creation & Lifecycle | 85% | Core flow works, status transitions not enforced |
| Marathon Teacher Creation & Lifecycle | 85% | Same as above |
| Marathon Participation Requests | 90% | System/program scopes fully implemented |
| Marathon Student Enrollment | 80% | Missing payment proof for student requests |
| Competition System/Program Creation & Lifecycle | 75% | Missing auto-close, generic notifications |
| Competition Teacher Creation & Lifecycle | 75% | Missing auto-close, missing participation UI |
| Competition Participation Requests | 80% | Works but missing UI elements |
| Competition Student Enrollment | 70% | Missing payment proof, missing marathon-enrollment check |
| Program Scope Eligibility | 40% | UI tab visible but NOT enforced |
| Marathon-bound Competition Workflow | 30% | Student enrollment bypasses parent marathon check |
| Notifications | 70% | Marathon-specific ✓, Competition uses generic assessment.* |
| Rankings / Leaderboards | 5% | Not implemented |
| Certificates Integration | 0% | Not connected |

### Key Statistics
- **Total workflows defined in spec:** 6 (System Marathon, Program Marathon, Teacher Marathon, System Competition, Program Competition, Teacher Competition)
- **Fully implemented:** 0
- **Partially implemented with gaps:** 6
- **Critical missing features:** 3 (Student payment proof, Program scope eligibility enforcement, Marathon-bound competition enrollment validation)
- **Known bugs/potential issues:** 5 (detailed in gap analysis)

---

## 2. Current State Assessment

### 2.1 What Works Well

| Feature | Files | Status |
|---------|-------|--------|
| Marathon lifecycle (draft→open→closed→completed) | `MarathonService.php` | ✅ With audit logging |
| Teacher participation requests (system scope) | `MarathonResource.php`, `CompetitionResource.php`, `ParticipationService.php` | ✅ With eligibility enforcement |
| Teacher participation approval/rejection | `ParticipationRequestService.php` | ✅ With seat allocation |
| Seat allocation and usage tracking | `SeatAllocationService.php` | ✅ With lock when full |
| Student enrollment (manual mode) | `StudentEnrollmentService.enrollStudent()` | ✅ |
| Student enrollment (request mode – approve/reject) | `StudentEnrollmentService` approveEnrollment / rejectEnrollment | ✅ |
| Duplicate enrollment prevention | `StudentEnrollmentService.hasExistingEnrollment()` | ✅ |
| Payment proof for teacher participation | `MarathonResource`, `CompetitionResource`, `PaymentService` | ✅ |
| Marathon-bound competition detection | `CompetitionParticipationService.isMarathonBound()` | ✅ |
| Polymorphic participation architecture | All `Participation/` models and services | ✅ |
| NotificationEngine integration | All participation services | ✅ |

### 2.2 What Is Missing or Broken

| Feature | Priority | Impact | Files |
|---------|----------|--------|-------|
| Student enrollment payment proof | High | Business workflow incomplete | `StudentEnrollmentService`, `StudentEnrollment` model |
| Program scope eligibility enforcement | High | Security/access control gap | `EligibilityService`, `MarathonResource`, `CompetitionResource` |
| Marathon-bound enrollment validation | Medium | Data integrity risk | `CompetitionParticipationService.submitStudentEnrollment()` |
| Competition auto-close on ends_at | Medium | Events remain open indefinitely | Missing cron job |
| Marathon auto-close on ends_at | Medium | Same issue | Missing cron job |
| Competition-specific notifications | Low | Poor UX for competition users | `Assessment.php` booted() |
| Competition leaderboard | Low | Missing core feature | Not implemented |
| Marathon results/rankings | Low | Missing core feature | Not implemented |
| Feature consumption rollback | Medium | Data inconsistency risk | `Assessment.php` booted() |
| Competition edit/create page registration | Low | Developer friction | `CompetitionResource::getPages()` |

---

## 3. Workflow-by-Workflow Gap Analysis

### 3.1 System Marathon Workflow

**Approved Spec:** Section 3.3 of docs/business/Marathon_Competition_Workflows.md

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Super Admin creates Marathon | ✅ Implemented | MarathonResource form, scope='system', status='draft' |
| 2. Super Admin opens Marathon | ✅ Implemented | MarathonService.openForParticipation() with audit log 'marathon.opened' |
| 3. Super Admin configures eligibility | ✅ Implemented | Eligibility tab visible, event_eligibilities table used |
| 4. Teachers view Marathon (eligibility check) | ✅ Implemented | canView() checks event_eligibilities for system scope |
| 5. Teacher submits participation request | ✅ Implemented | submit_participation_request action with seat count, payment proof |
| 6. Super Admin reviews/approves/rejects | ✅ Implemented | Requests tab with approve/reject, seat allocation, notifications |
| 7. Teacher enrolls students (manual/request) | ✅ Implemented | StudentEnrollmentService with seat consumption |
| Manual enrollment notification | ✅ Implemented | `enrollment.manual` via NotificationEngine |
| Request enrollment notification | ✅ Implemented | `enrollment.submitted`, `enrollment.approved`, `enrollment.rejected` |
| Payment proof for student enrollment | ❌ Missing | Not in StudentEnrollmentService or StudentEnrollment model |

**Gap Summary:** 1 gap — student enrollment payment proof

---

### 3.2 Program Marathon Workflow

**Approved Spec:** Section 3.4

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Portal Admin creates Marathon | ✅ Implemented | scope='program' option available |
| 2. Portal Admin opens Marathon | ✅ Implemented | Same service as system |
| 3. Portal Admin configures eligibility | ⚠️ UI visible, NOT enforced | Tab visible, but isEligibleForProgramEvent() ignores event_eligibilities |
| 4. Teachers view Marathon | ⚠️ No per-teacher filter | canView() does NOT check event_eligibilities for program scope |
| 5. Teacher submits participation request | ⚠️ No eligibility check | submit_participation_request visibility returns true for program scope without eligibility |
| 6. Portal Admin reviews/approves/rejects | ✅ Implemented | Same as system |
| 7. Teacher enrolls students | ✅ Implemented | Same as system |

**Gap Summary:** 2 gaps — eligibility UI exists but is NOT enforced, teacher visibility/requests not filtered

---

### 3.3 Teacher Marathon Workflow

**Approved Spec:** Section 3.5

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Teacher creates Marathon | ✅ Implemented | scope='teacher' forced for portal-teacher role |
| 2. Teacher opens Marathon | ✅ Implemented | Same service |
| 3. Teacher enrolls students (no participation request) | ✅ Implemented | Auto-participation for teacher scope |
| Manual enrollment | ✅ Implemented | enrollStudent() with immediate approval |
| Request enrollment | ✅ Implemented | submitEnrollmentRequest() → pending → approve/reject |
| Payment proof for student enrollment | ❌ Missing | Not in StudentEnrollmentService or StudentEnrollment model |
| Teacher can view payment proof | ❌ Missing | No UI for viewing student payment proof |

**Gap Summary:** 2 gaps — payment proof for student enrollment, payment proof viewing UI

---

### 3.4 Standalone System Competition Workflow

**Approved Spec:** Section 3.6

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Super Admin creates Competition | ✅ Implemented | ListCompetitions → inline create, AssessmentResource form |
| 2. Super Admin configures eligibility | ✅ Implemented | event_eligibilities for competition event type |
| 3. Teachers view Competition | ✅ Implemented | CompetitionResource.canView() checks eligibility for system scope |
| 4. Teacher submits participation request | ✅ Implemented | submit_participation_request action in CompetitionResource |
| 5. Super Admin reviews/approves/rejects | ✅ Implemented | Same ParticipationService |
| 6. Teacher enrolls students | ✅ Implemented | Same StudentEnrollmentService |
| 7. Super Admin unlocks Competition | ✅ Implemented | is_locked toggle |
| 8. Student solves Competition | ✅ Implemented | MentalMathTimer |
| Auto-close when ends_at passes | ❌ Missing | No cron job, no auto-transition |
| Competition-specific notifications | ❌ Missing | Uses generic assessment.created/updated |

**Gap Summary:** 2 gaps — auto-close on ends_at, competition-specific notifications

---

### 3.5 Standalone Program Competition Workflow

**Approved Spec:** Section 3.7

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Portal Admin creates Competition | ✅ Implemented | Same form as system |
| 2. Portal Admin configures eligibility | ⚠️ UI visible, NOT enforced | Same gap as Program Marathon |
| 3. Teachers view Competition | ⚠️ No per-teacher filter | canView() does NOT check event_eligibilities for program scope |
| 4. Teacher submits participation request | ⚠️ No eligibility check | submit_participation_request returns true for program scope |
| 5-8. Remaining steps | ✅ Implemented | Same as System Competition |

**Gap Summary:** Same 3 gaps as Program Marathon + same 2 gaps as System Competition

---

### 3.6 Standalone Teacher Competition Workflow

**Approved Spec:** Section 3.8

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Teacher creates Competition | ✅ Implemented | scope='teacher' forced |
| 2. Teacher views Competition (auto participation) | ✅ Implemented | No participation request needed |
| 3. Teacher unlocks Competition | ✅ Implemented | is_locked toggle |
| 4. Student enrollment | ⚠️ See below | |
| Manual enrollment | ✅ Implemented | Immediate approval |
| Request enrollment | ✅ Implemented | submitEnrollmentRequest |
| Payment proof for student enrollment | ❌ Missing | Not in StudentEnrollmentService or StudentEnrollment model |
| Teacher can view payment proof | ❌ Missing | No UI |
| 5. Student solves Competition | ✅ Implemented | MentalMathTimer |

**Gap Summary:** 2 gaps — same as Teacher Marathon

---

### 3.7 Competition inside Marathon Workflow

**Approved Spec:** Section 3.9

| Step | Implementation Status | Details |
|------|---------------------|---------|
| 1. Marathon created (any scope) | ✅ Implemented | |
| 2. Teacher adds Competitions via RelationManager | ✅ Implemented | Creates assessment with type='marathon-competition' |
| 3. Teacher participation bypassed | ✅ Implemented | isMarathonBound() → RuntimeException on submitTeacherParticipation |
| 4. Student enrollment — marathon enrollment check | ❌ Missing | submitStudentEnrollment() delegates to ParticipationService without checking parent marathon enrollment |
| 5. Students see marathon competition | ✅ Visible in Marathon tab only | Filtered out of CompetitionResource listing |

**Gap Summary:** 1 critical gap — marathon-bound competition enrollment does NOT validate parent marathon enrollment

---

## 4. Implementation Phases

---

### Phase 1: Student Enrollment Payment Proof

**Phase ID:** P1
**Objective:** Enable payment proof upload during student enrollment requests for teacher-scoped competitions and marathons

**Workflows Covered:** Teacher Marathon (3.5), Teacher Competition (3.8), all scope variants

#### Tasks:

**1.1 Add `payment_proof` field to `student_enrollments` table**

- New migration: `add_payment_proof_to_student_enrollments_table.php`
- Column: `payment_proof` (string, nullable) — stores uploaded file path
- Add to `StudentEnrollment` model `$fillable` and casts

**1.2 Modify `StudentEnrollmentService::submitEnrollmentRequest()`**

```php
public function submitEnrollmentRequest(
    string $eventType,
    Model $event,
    User $student,
    ?UploadedFile $paymentProof = null,  // NEW
): StudentEnrollment
```

- If `$event->payment_required && $paymentProof`: store payment proof path on enrollment record
- If `$event->payment_required && !$paymentProof`: require it (throw validation exception)

**1.3 Update enrollment UI in AssessmentResource and MarathonResource**

- Add `FileUpload::make('payment_proof')` to the `join_assessment` / `join_marathon` action form
- Make it visible when `$record->payment_required === true`
- Pass uploaded file to `submitEnrollmentRequest()`

**1.4 Add payment proof viewing UI in Participants tab**

- In the participants Livewire/View component, add a column for payment proof when `enrollment_type = 'request'`
- Show download/view link for the uploaded proof image
- Visible to teacher/admins reviewing pending requests

**Affected Modules:**
- `app/Models/Participation/StudentEnrollment.php`
- `app/Services/Participation/Services/StudentEnrollmentService.php`
- `app/Filament/Resources/AssessmentResource.php` (join_assessment action)
- `app/Filament/Resources/MarathonResource.php` (join_marathon action)
- Participant tab Livewire components (blade views)
- Database migration

**Dependencies:** None

**Acceptance Criteria:**
- Student can upload payment proof when joining a teacher-scoped event with `payment_required = true`
- Teacher can view the payment proof in pending requests
- Enrollment without payment proof is rejected when `payment_required`
- Existing enrollments (no payment proof) remain unaffected

**Required Testing:**
- Unit test: StudentEnrollmentService with payment proof
- Feature test: Full student enrollment flow with payment

**Estimated Complexity:** Medium (3-4 files, 1 migration)
**Risk:** Low

---

### Phase 2: Program Scope Eligibility Enforcement

**Phase ID:** P2
**Objective:** Make the program-scope eligibility configuration actually enforced in visibility and participation checks

**Workflows Covered:** Program Marathon (3.4), Program Competition (3.7)

#### Tasks:

**2.1 Update `EligibilityService::isEligibleForProgramEvent()`**

Replace the simple `$user->program_id === $event->program_id` check with a check against `event_eligibilities` when eligibility records exist:

```php
private function isEligibleForProgramEvent(User $user, Model $event): bool
{
    // Admins bypass
    if ($user->hasAnyRole(['super-admin', 'super-manager', 'portal-admin', 'portal-manager'])) {
        return $user->program_id === $event->program_id;
    }

    // Teachers: check event_eligibilities for this program event
    if ($user->hasAnyRole(['portal-teacher', 'portal-teacher-assistant'])) {
        // First check if any eligibility records exist for this event
        $eligibilitiesExist = EventEligibility::where('event_type', $event->getMorphClass())
            ->where('event_id', $event->getKey())
            ->exists();

        // If no eligibility records, fall back to program membership (open to all)
        if (!$eligibilitiesExist) {
            return $user->program_id === $event->program_id;
        }

        // If eligibilities exist, teacher must be in the eligible list
        return EventEligibility::where('event_type', $event->getMorphClass())
            ->where('event_id', $event->getKey())
            ->where('eligible_teacher_id', $user->id)  // teacher-level eligibility
            ->exists();
    }

    return false;
}
```

**2.2 Update `MarathonResource::canView()` for program scope**

Add eligibility check for program-scoped marathons (currently only system scope checks event_eligibilities):

```php
// In the teacher section, after the system scope check
if ($record->scope === 'program') {
    // Check if event_eligibilities exist for this program event
    // If they do, teacher must be in the eligible list
    // If not, all program teachers can view
    $eligibilitiesExist = EventEligibility::where(...)->exists();
    // ... enforce or fall back
}
```

**2.3 Update `CompetitionResource::canView()` for program scope**

Same pattern as 2.2 for competitions.

**2.4 Update `submit_participation_request` visibility in MarathonResource and CompetitionResource**

Add a call to `EligibilityService::isEligible()` for program scope (currently only checked for system scope):

```php
// After the system scope check, add program scope check
if ($record->scope === 'program') {
    return app(EligibilityServiceInterface::class)->isEligible(
        $user, 'marathon', $record  // or 'competition'
    );
}
```

**Affected Modules:**
- `app/Services/Participation/Services/EligibilityService.php`
- `app/Filament/Resources/MarathonResource.php`
- `app/Filament/Resources/CompetitionResource.php`
- `app/Filament/Resources/AssessmentResource.php`

**Dependencies:** None

**Acceptance Criteria:**
- Portal Admin: can configure per-teacher eligibility for program-scoped events via the existing Eligibility tab
- When eligibility records exist: only configured teachers can see and submit requests
- When no eligibility records: all teachers in the program can see and submit (backward compatible)
- Admins bypass all eligibility checks (can see everything in their program)
- Existing behavior preserved for events without eligibility configuration

**Required Testing:**
- Unit test: EligibilityService -> isEligibleForProgramEvent
- Feature test: Program marathon visibility with and without eligibility config

**Estimated Complexity:** Medium (3-4 files)
**Risk:** Medium — changing authorization logic, must preserve backward compatibility

---

### Phase 3: Marathon-Bound Competition Enrollment Validation

**Phase ID:** P3
**Objective:** Ensure that students enrolling in marathon-bound competitions are validated against the parent Marathon's enrollment

**Workflows Covered:** Competition inside Marathon (3.9)

#### Tasks:

**3.1 Update `CompetitionParticipationService::submitStudentEnrollment()`**

```php
public function submitStudentEnrollment(
    Assessment $competition,
    User $student,
): StudentEnrollment {
    // NEW: If marathon-bound, check parent marathon enrollment
    if ($this->isMarathonBound($competition)) {
        $marathon = $competition->marathon;
        if (!$marathon) {
            throw new \RuntimeException('Marathon-bound competition has no parent marathon.');
        }
        // Check if student is enrolled in the parent marathon
        $hasMarathonEnrollment = StudentEnrollment::query()
            ->where('event_type', 'marathon')
            ->where('event_id', $marathon->id)
            ->where('student_id', $student->id)
            ->where('status', 'approved')
            ->exists();

        if (!$hasMarathonEnrollment) {
            throw new \App\Services\Participation\Exceptions\InvalidScopeException(
                'You must be enrolled in the parent marathon to participate in this competition.'
            );
        }
    }

    return $this->participationService->submitStudentEnrollmentRequest(
        self::EVENT_TYPE,
        $competition,
        $student,
    );
}
```

**3.2 Update `AssessmentResource::canJoin()` for marathon-bound competitions**

Add the same parent marathon enrollment check to the `join_assessment` action's visibility.

**Affected Modules:**
- `app/Services/Participation/Services/CompetitionParticipationService.php`
- `app/Filament/Resources/AssessmentResource.php`

**Dependencies:** None

**Acceptance Criteria:**
- Student cannot submit enrollment request for a marathon-bound competition unless they are enrolled in the parent Marathon
- Clear error message when not enrolled in parent Marathon
- Standalone competitions (not marathon-bound) unaffected

**Required Testing:**
- Unit test: CompetitionParticipationService with marathon-bound enrollment
- Feature test: Full marathon-bound competition enrollment flow

**Estimated Complexity:** Low (2 files)
**Risk:** Low

---

### Phase 4: Competition & Marathon Automated Lifecycle

**Phase ID:** P4
**Objective:** Add scheduled commands to auto-close/completed competitions and marathons when `ends_at` passes

**Workflows Covered:** All competition and marathon workflows

#### Tasks:

**4.1 Create command `notifications:auto-close-events`**

```php
// In routes/console.php
Schedule::command('events:auto-close')
    ->everyFiveMinutes()  // or hourly
    ->withoutOverlapping();

Schedule::command('events:auto-complete')
    ->daily()
    ->withoutOverlapping();
```

**Events:auto-close logic:**
1. Find competitions where `ends_at < now() AND is_locked = false`
2. Set `is_locked = true` (effectively closes the competition)
3. Audit log: `competition.auto-closed`
4. Notification via NotificationEngine

**Events:auto-complete logic:**
1. Find Marathons where `ends_at < now() AND status = 'open'`
2. Transition to `completed` via `MarathonService::completeMarathon()`
3. Audit log: `marathon.auto-completed`
4. Notification via NotificationEngine

**4.2 Add `MarathonStatus` enum (optional improvement)**

Create a backed enum for marathon status transitions:
```php
enum MarathonStatus: string
{
    case Draft = 'draft';
    case Open = 'open';
    case Closed = 'closed';
    case Completed = 'completed';

    public function canTransitionTo(self $target): bool
    {
        return match ($this) {
            self::Draft => $target === self::Open,
            self::Open => $target === self::Closed || $target === self::Completed,
            self::Closed => $target === self::Completed,
            self::Completed => false, // terminal
        };
    }
}
```

Affected Modules:
- `routes/console.php`
- `app/Console/Commands/AutoCloseEvents.php` (new)
- `app/Console/Commands/AutoCompleteEvents.php` (new)
- `app/Models/Marathon.php` (optional enum)
- `app/Services/MarathonService.php` (optional transition validation)

**Dependencies:** None

**Acceptance Criteria:**
- Competitions auto-lock when ends_at passes
- Marathons auto-complete when ends_at passes
- Notifications dispatched on auto-transition
- Audit trails recorded

**Required Testing:**
- Unit test: Auto-close command logic
- Feature test: End-to-end event lifecycle with cron

**Estimated Complexity:** Medium (2 new commands, console schedule)
**Risk:** Low

---

### Phase 5: Competition-Specific Notifications

**Phase ID:** P5
**Objective:** Add dedicated competition participation notifications instead of relying solely on generic `assessment.*` events

**Workflows Covered:** All competition workflows

#### Tasks:

**5.1 Add notification events for competition participation**

Register new notification event names in NotificationEngine config:
- `competition.participation.submitted`
- `competition.participation.approved`
- `competition.participation.rejected`
- `competition.seat.allocated`
- `competition.seat.full`

**5.2 Update `ParticipationService::approveParticipation()` etc.**

The service already dispatches `{$eventType}.participation.approved`. Since `$eventType = 'competition'`, this would already dispatch `competition.participation.approved`. Verify this is configured correctly in the NotificationEngine and create notification templates.

**Note:** This may already work but the notification templates/recipients may not be configured. This task is primarily about verification and configuration.

**5.3 Add competition-specific attempt notifications**

- `competition.attempt.submitted`
- `competition.attempt.timed_out`

These are currently dispatched as `assessment.attempt.submitted` and `assessment.attempt.timed_out`.

**Affected Modules:**
- NotificationEngine configuration
- `app/Notifications/` directory (new templates)
- `app/Models/Assessment.php` booted() (attempt events)

**Dependencies:** NotificationEngine understanding

**Acceptance Criteria:**
- Competition participation triggers `competition.*` notifications
- Competition attempt completion triggers `competition.*` notifications
- Existing assessment.* notifications continue to work for exams/trainings

**Required Testing:**
- Notification dispatch verification

**Estimated Complexity:** Low
**Risk:** Low

---

### Phase 6: Feature Consumption & Rollback Fixes

**Phase ID:** P6
**Objective:** Move `no_of_competition_per_marathon` feature consumption from model boot event to SubscriptionUsageObserver, add rollback

**Workflows Covered:** Marathon competition creation (3.9)

#### Tasks:

**6.1 Remove feature consumption from `Assessment::booted()`**

Remove the manual `FeatureGate::consume()` call for `no_of_competition_per_marathon` from the model's `created` boot event.

**6.2 Add mapping to `config/subscription-resources.php`**

```php
// Add specific mapping for marathon-competition type
Assessment::class => [
    'type' => 'no_of_assessment_resources',
],
'marathon-competition' => [
    'type' => 'no_of_competition_per_marathon',
],
```

**6.3 Verify `SubscriptionUsageObserver` handles marathon-competition type**

Ensure the observer correctly identifies `marathon-competition` assessments and consumes the `no_of_competition_per_marathon` feature.

**6.4 Add rollback in `Assessment::deleted()` boot event**

The deleted event already notifies about usage release via SubscriptionUsageObserver — verify this works for `marathon-competition` type.

**Affected Modules:**
- `app/Models/Assessment.php`
- `config/subscription-resources.php`
- `app/Observers/SubscriptionUsageObserver.php`

**Dependencies:** Understanding of SubscriptionUsageObserver

**Acceptance Criteria:**
- Creating a marathon-competition consumes the `no_of_competition_per_marathon` feature
- Deleting a marathon-competition releases the feature usage
- Works in queue/CLI context (no dependency on `auth()->user()`)
- No double-consumption (old + new)

**Required Testing:**
- Unit test: Feature consumption for marathon-competition
- Integration test: Full create → delete lifecycle

**Estimated Complexity:** Medium (understanding observer, 2-3 files)
**Risk:** Medium — changing feature consumption mechanism

---

### Phase 7: Rankings, Leaderboards & Certificates

**Phase ID:** P7
**Objective:** Add basic ranking/leaderboard for competitions and integration with certificate module

**Workflows Covered:** Results & Rankings (Section 9)

#### Tasks:

**7.1 Create `AssessmentRanking` model and migration**

```php
Schema::create('assessment_rankings', function (Blueprint $table) {
    $table->id();
    $table->morphs('event'); // competition or marathon
    $table->foreignId('student_id')->constrained('users');
    $table->integer('rank');
    $table->decimal('score', 8, 2);
    $table->integer('total_correct');
    $table->integer('total_incorrect');
    $table->integer('total_timeout');
    $table->integer('total_time_seconds');
    $table->timestamps();
});
```

**7.2 Create ranking calculation service**

```php
class RankingService
{
    public function calculateCompetitionRankings(Assessment $competition): void
    {
        // Get all attempts for this competition
        // Calculate score per student
        // Order by score descending, time ascending
        // Assign rank
        // Store in assessment_rankings
    }
}
```

**7.3 Add leaderboard UI to competition view page**

- New Filament widget or Livewire component
- Display ranked list of students with scores
- Filterable by level classification
- Visible to teacher/admin

**7.4 Marathon ranking aggregation**

- Aggregate rankings across all competitions in a marathon
- Calculate total score per student across all competitions
- Display marathon-level leaderboard

**7.5 Certificate integration**

- When marathon is completed → trigger certificate generation for top-ranked students
- Integrate with existing Certificate module
- Add configuration: what rank threshold gets a certificate

**Affected Modules:**
- `app/Models/AssessmentRanking.php` (new)
- `app/Services/RankingService.php` (new)
- `app/Filament/Widgets/CompetitionLeaderboard.php` (new)
- `database/migrations/`

**Dependencies:** Phase 4 (auto-complete)

**Acceptance Criteria:**
- Competition leaderboard shows ranked students with scores
- Marathon leaderboard aggregates all competitions
- Certificates generated for top students on marathon completion
- Leaderboard visible to teachers and admins

**Required Testing:**
- Unit test: RankingService calculation
- Feature test: Leaderboard display

**Estimated Complexity:** High (new model, service, UI, certificate integration)
**Risk:** Medium

---

## 5. Cross-Cutting Concerns

### 5.1 Work That Spans Multiple Phases

| Concern | Phases Affected | Description |
|---------|----------------|-------------|
| Docs updates | All | Every phase must update docs/business/Marathon_Competition_Workflows.md and module docs |
| Translation keys | All | Any new UI strings need en/ar translation entries |
| Test coverage | All | Each phase must include tests for the new behavior |
| Audit | All | Verify existing audit patterns are maintained |

### 5.2 Testing Strategy

| Test Type | Coverage | Tools |
|-----------|----------|-------|
| Unit tests | All service methods | PHPUnit |
| Feature tests | Full workflow end-to-end | PHPUnit + Laravel test helpers |
| Browser tests | UI workflows (payment proof, leaderboard) | Optional (manual for now) |

**Existing test files to extend:**
- `tests/Feature/Participation/MarathonWorkflowTest.php`
- `tests/Feature/Participation/CompetitionWorkflowTest.php`
- `tests/Unit/Services/Participation/MarathonParticipationServiceTest.php`
- `tests/Unit/Services/Participation/CompetitionParticipationServiceTest.php`
- `tests/Unit/Services/Participation/ParticipationServiceTest.php`
- `tests/Unit/Services/Participation/EligibilityServiceTest.php`

### 5.3 Database Migration Checklist

| Migration | Phase | Purpose |
|-----------|-------|---------|
| `add_payment_proof_to_student_enrollments` | P1 | Add payment_proof column |
| `create_assessment_rankings_table` | P7 | Rankings/leaderboards |
| (optional) `create_marathon_status_enum` | P4 | If using DB enum |

### 5.4 Permission Review

| Gate | Used By | Phase Impact |
|------|---------|--------------|
| `participation.manage-eligibility` | Eligibility UI | P2 (program scope) |
| `participation.student-can-join` | Student enrollment | P1 (payment proof), P3 (marathon validation) |
| `participation.manage-event` | Edit/delete | No change |
| `participation.enroll-student` | Manual enrollment | No change |

---

## 6. Risk Assessment

### 6.1 High Priority Risks

| Risk | Phase | Impact | Mitigation |
|------|-------|--------|------------|
| Changing EligibilityService could break existing behavior for events without eligibility config | P2 | Medium | Use "fallback to program membership" pattern when no eligibilities exist |
| Feature consumption migration could double-count or miss consumption | P6 | Medium | Add monitoring/logging, verify with manual testing |
| Student payment proof migration could break existing student records | P1 | Low | Make column nullable, default null |

### 6.2 Decision Table

| Question | Context | Recommendation |
|----------|---------|---------------|
| Should student payment proof be in StudentEnrollment model or PaymentSubmission model? | Teacher payment uses PaymentSubmission (linked to EventParticipation) | Use a `payment_proof` column on `student_enrollments` for simplicity — avoids adding another polymorphic relationship |
| Should program-scope eligibility be enforced retroactively for existing events? | Events created without eligibility config | No — use fallback: if no eligibilities exist, all program teachers can participate |
| Should marathon-bound competitions validate marathon enrollment? | This was flagged as a gap | Yes — this is a data integrity issue |
| Should we add rankings/leaderboard now or defer? | Not in current workflows | Defer to Phase 7 — low priority for core workflow completion |

---

## 7. Appendix: Referenced Source Files

### Models
- `app/Models/Marathon.php` — Core marathon model
- `app/Models/Assessment.php` — Competition (type discriminator)
- `app/Models/AssessmentProblems.php` — Competition problems
- `app/Models/AssessmentAttempt.php` — Student attempt
- `app/Models/AssessmentAttemptAnswer.php` — Per-problem answer
- `app/Models/Participation/EventParticipation.php` — Teacher participation
- `app/Models/Participation/StudentEnrollment.php` — Student enrollment
- `app/Models/Participation/SeatAllocation.php` — Seat allocation
- `app/Models/Participation/PaymentSubmission.php` — Payment proof
- `app/Models/Participation/EventEligibility.php` — Eligibility config
- `app/Models/Participation/ParticipationAudit.php` — Audit trail

### Services
- `app/Services/MarathonService.php` — Marathon lifecycle
- `app/Services/Participation/Services/ParticipationService.php` — Central facade
- `app/Services/Participation/Services/MarathonParticipationService.php` — Marathon entry point
- `app/Services/Participation/Services/CompetitionParticipationService.php` — Competition entry point
- `app/Services/Participation/Services/EligibilityService.php` — Eligibility checks
- `app/Services/Participation/Services/ParticipationRequestService.php` — Request management
- `app/Services/Participation/Services/SeatAllocationService.php` — Seat management
- `app/Services/Participation/Services/StudentEnrollmentService.php` — Student enrollment
- `app/Services/Participation/Services/PaymentService.php` — Payment management
- `app/Services/Participation/Services/ParticipationAuditService.php` — Audit logging
- `app/Services/Participation/ScopeConfig.php` — Scope configuration
- `app/Services/FeatureEntitlement/FeatureGate.php` — Feature consumption

### Filament Resources
- `app/Filament/Resources/MarathonResource.php` — Marathon CRUD
- `app/Filament/Resources/MarathonResource/Pages/ListMarathons.php`
- `app/Filament/Resources/MarathonResource/Pages/ViewMarathon.php`
- `app/Filament/Resources/MarathonResource/RelationManagers/CompetitionsRelationManager.php`
- `app/Filament/Resources/CompetitionResource.php` — Competition CRUD
- `app/Filament/Resources/CompetitionResource/Pages/ListCompetitions.php`
- `app/Filament/Resources/CompetitionResource/Pages/ViewCompetition.php`
- `app/Filament/Resources/AssessmentResource.php` — Shared assessment forms/columns
- `app/Filament/Resources/BaseResource.php` — Shared helper methods

### Policies
- `app/Policies/ParticipationPolicy.php` — Authorization gates

### Pages
- `app/Filament/Pages/MentalMathTimer.php` — Competition solving
- `app/Filament/Pages/AssessmentClassification.php` — Results classification

### Configuration
- `config/participation.php` — Approval authorities, event types
- `config/features.php` — Feature definitions
- `config/subscription-resources.php` — Usage tracking mapping

### Console
- `routes/console.php` — Scheduled commands

### Migrations
- `database/migrations/2026_05_04_223500_create_assessments_table.php`
- `database/migrations/2026_06_13_103524_create_marathons_table.php`
- `database/migrations/2026_06_13_104054_add_marathon_id_to_assessments_table.php`
- `database/migrations/2026_07_15_000001_add_participation_columns_to_marathons.php`
- `database/migrations/2026_07_15_000002_add_participation_columns_to_assessments.php`
- `database/migrations/2026_07_15_000001_create_event_eligibilities_table.php`
- `database/migrations/2026_07_15_000002_create_event_participations_table.php`
- `database/migrations/2026_07_15_000003_create_seat_allocations_table.php`
- `database/migrations/2026_07_15_000004_create_student_enrollments_table.php`
- `database/migrations/2026_07_15_000005_create_payment_submissions_table.php`
- `database/migrations/2026_07_15_000006_create_participation_audits_table.php`
- `database/migrations/2026_07_17_000001_add_marathon_competition_to_assessment_type_enum.php`

### Tests
- `tests/Feature/Participation/MarathonWorkflowTest.php`
- `tests/Feature/Participation/CompetitionWorkflowTest.php`
- `tests/Unit/Services/Participation/MarathonParticipationServiceTest.php`
- `tests/Unit/Services/Participation/CompetitionParticipationServiceTest.php`
- `tests/Unit/Services/Participation/ParticipationServiceTest.php`
- `tests/Unit/Services/Participation/EligibilityServiceTest.php`

### Documentation (Approved Spec)
- `docs/business/Marathon_Competition_Workflows.md` — Primary business workflow reference
- `docs/modules/Marathons.md` — Marathon module documentation
- `docs/modules/Competitions.md` — Competition module documentation
