# Records and Media — ALGA ARENA Esports Platform

**Date:** 2026-07-20

## Overview

Players publish **gaming records** (achievements, match results, etc.) with optional screenshot attachments. Records go through content moderation before public display.

---

## Data Model

### `player_records`

| Field | Description |
|-------|-------------|
| `record_type` | Enum: `rank_achievement`, `match_result`, `tournament_placement`, `personal_record`, `general_achievement` |
| `title`, `description` | User content |
| `achievement_date` | Optional date |
| `result_text` | Free-text result |
| `rank_id` | Optional FK to game rank |
| `visibility` | `public` / `private` (ProfileVisibility enum) |
| `moderation_status` | `pending`, `approved`, `rejected`, `hidden` |
| `published_at` | Set when approved (or auto-approved) |

Linked to `player_profile_id`, `game_id`, optional `player_game_profile_id`.

### `player_record_media`

Screenshot files stored on **`records` disk** (`storage/app/public/records`):

- `file_path`, `thumbnail_path` (thumbnail optional)
- `original_name`, `mime_type`, `size`, `checksum` (SHA-256)
- `caption`, `sort_order`

---

## Routes & Controller

| Method | Route | Action |
|--------|-------|--------|
| GET | `/profile/records` | List own records |
| POST | `/profile/records` | Create record + upload screenshots |
| DELETE | `/profile/records/{record}` | Soft delete own record |

**Controller:** `PlayerRecordController`  
**Form request:** `StorePlayerRecordRequest`  
**Action:** `CreatePlayerRecordAction`

---

## Create Flow

```
Authenticated user with player profile
    ↓
POST /profile/records (multipart: screenshots[])
    ↓
CreatePlayerRecordAction
    ├── Validate game profile belongs to user + matches game_id
    ├── Validate rank_id belongs to game (if provided)
    ├── ContentModerationService::initialStatus()
    │     └── pending OR approved (if system_settings content_auto_approve)
    ├── INSERT player_records
    └── For each screenshot:
          MediaUploadService::uploadImage(records disk, max 4096 KB)
          INSERT player_record_media
    ↓
Redirect to index (moderation message)
```

### Upload rules (`MediaUploadService`)

- Allowed: `jpg`, `jpeg`, `png`, `webp`
- Max size: 4096 KB per image (configurable in action call)
- Validates actual image via `getimagesize()`

---

## Moderation

**Admin route:** `POST /admin/moderation/records/{record}`  
**Controller:** `Admin\ModerationController@moderateRecord`  
**Action:** `App\Actions\Records\ModerateRecordAction`  
**Gate:** `moderate-records`

### Moderation action

- Updates `moderation_status`, `moderation_reason`
- Sets `published_at` on approval
- Logs to `audit_logs` (`record.moderated`)
- Fires `ContentModerated` event → `RecordModerationResultNotification`

**Note:** `ModerationController` currently imports `App\Actions\Content\ModerateRecordAction` (wrong namespace) — see `TESTING_REPORT.md` / `BACKEND_HANDOFF.md`.

---

## Authorization

| Action | Policy |
|--------|--------|
| List | `PlayerRecordPolicy@viewAny` |
| Create | `PlayerRecordPolicy@create` (needs profile) |
| Delete | `PlayerRecordPolicy@delete` (owner or admin) |
| Moderate | Gate `moderate-records` |

No update endpoint — users delete and recreate.

---

## Public Visibility Rules

A record is shown on public profiles when:

1. `moderation_status = approved`
2. `visibility = public`
3. Not soft-deleted

Pending/rejected records visible only to owner and admins in management views.

---

## Storage Layout

```
storage/app/public/records/
  └── {player_profile_id}/
        └── {uuid}.jpg
```

Public URL via `Storage::disk('records')->url($path)` after `storage:link`.

---

## Deletion

Soft delete on `player_records` (`deleted_at`). Media files are not automatically purged on delete (orphan cleanup deferred).

---

## Related Enums

```php
RecordType: rank_achievement | match_result | tournament_placement | personal_record | general_achievement
ModerationStatus: pending | approved | rejected | hidden
ProfileVisibility: public | private
```
