# PRD: Multi-Profile for Roku (Durioo+ TV)

## Problem Statement

The Roku Durioo+ app currently provides a single-profile experience — all content, watch history, favorites, and content-age filtering apply globally to the account. Families with multiple children of different ages cannot personalize the experience per child. Parents cannot restrict content by age group per profile.

The Samsung TV version (`duriooplus-web-samsung`) solves this with a full multi-profile system:

1. After login, the user sees a **Profile Selection** screen with their family members' avatars
2. Each profile has its own content-age filtering, watch history, favorites, and settings
3. Profiles are typed as **parents** or **children** — parent profiles can manage content restrictions
4. Users can add/edit/delete profiles, with limits per account tier (free vs VIP)
5. All API requests include the active profile ID header (`duriooplus-subscriber-profile-id`)

This PRD adapts the multi-profile system for Roku, working within SceneGraph/BrighterScript constraints.

---

## User Flow

```
┌─────────────────────────────────────────────────────┐
│  SplashScreen → Login → Subscriber check            │
│                         │                            │
│                         ▼                            │
│  ProfileSelectScreen (NEW)                           │
│  ┌──────────────────────────────────────────────┐   │
│  │         "Who's Watching?"                    │   │
│  │                                              │   │
│  │  [👤 Dad]  [👩 Mom]  [👧 Amina]  [+ Add]    │   │
│  │   PARENT    PARENT    3-5 yrs                 │   │
│  │                                              │   │
│  │  [Parents Area]  [Become VIP]  (footer)      │   │
│  └──────────────────────────────────────────────┘   │
│                         │                            │
│              User selects profile                    │
│                         │                            │
│                         ▼                            │
│              HomeScreen (content filtered)            │
│  ┌──────────────────────────────────────────────┐   │
│  │  [Continue Watching] — specific to Amina     │   │
│  │  [New for 3-5 yrs] — age-filtered            │   │
│  │  [Trending]                                   │   │
│  └──────────────────────────────────────────────┘   │
│                                                     │
│  From Settings / Side Menu:                          │
│  ┌──────────────────────────────────────────────┐   │
│  │  SettingScreen → "Switch Profile" button      │   │
│  │  → returns to ProfileSelectScreen             │   │
│  └──────────────────────────────────────────────┘   │
│                                                     │
│  From ProfileSelectScreen:                           │
│  ┌──────────────────────────────────────────────┐   │
│  │  [+ Add] → AddProfileTypeScreen               │   │
│  │  ┌─────────────────────────────────────┐      │   │
│  │  │ "Who are you adding?"               │      │   │
│  │  │ [Parent]  [Child]                   │      │   │
│  │  └─────────────────────────────────────┘      │   │
│  │              │                                  │   │
│  │              ▼                                  │   │
│  │  ProfileFormScreen                             │   │
│  │  ┌─────────────────────────────────────┐      │   │
│  │  │ Name: [___________]                 │      │   │
│  │  │ Avatar: [🧒] [👦] [👧] [🧑] ...    │      │   │
│  │  │ (if child) Age: Preschool / 3-5 /   │      │   │
│  │  │              6-8 / 9-12 / 13+       │      │   │
│  │  │ [Save]  [Cancel]                    │      │   │
│  │  └─────────────────────────────────────┘      │   │
│  └──────────────────────────────────────────────┘   │
│                                                     │
│  Edit profile: long-press or edit button             │
│  → ProfileFormScreen (mode: edit)                    │
│                                                     │
│  Delete profile: from edit screen / parental gate    │
│  → confirm dialog → delete → refetch profiles        │
└─────────────────────────────────────────────────────┘
```

---

## Architecture: How Multi-Profile Works (Samsung → Roku Adaptation)

### Samsung Flow (reference)

| Step | Component | What Happens |
|------|-----------|--------------|
| 1 | App startup → `SplashScreen` | Checks login, gets subscriber |
| 2 | `ProfileMain` page (`/profile`) | Fetches profiles via `GET /me/my-profiles`, renders avatar grid |
| 3 | User selects profile → `ProfileSetProfileData` | Stores `{ profile_id, profile_name, profile_avatar, profile_type, content_category_ages }` in Redux |
| 4 | `router.push("/")` → Home | Home loads, content filtered by profile |
| 5 | All API calls via axios interceptor | Adds `duriooplus-subscriber-profile-id` header from Redux |
| 6 | `ProfileForm` (create/edit) | Fetches avatars (`GET /profile-avatars`), content ages (`GET /content-category-ages`), creates/updates via `POST/PATCH /me/create-profile` |
| 7 | Settings → "Switch Profile" | Navigates back to `/profile` |

### Roku Adaptation

| Step | Component | What Happens |
|------|-----------|--------------|
| 1 | `SplashScreen` (existing) | Login + subscriber check — same as now |
| 2 | `ProfileSelectScreen` (new) | Fetches `GET /me/my-profiles`, renders avatar grid via MarkupGrid/MarkupList |
| 3 | User selects profile → `SetActiveProfile(data)` | Redoku action stores profile in global state + persists to registry |
| 4 | `navigateToHomeScreen` (existing) | Home loads with profile-filtered content |
| 5 | `RequestUtils.addGlobalHeaders` (existing, modified) | Adds `duriooplus-subscriber-profile-id` header from Redoku state |
| 6 | `ProfileFormScreen` (new) | Fetches avatars and content ages, creates/updates profiles |
| 7 | `AddProfileTypeScreen` (new) | Simple parent/child choice screen |
| 8 | `SettingScreen` (existing, modified) | Adds "Switch Profile" menu item |

### Key Differences from Samsung

| Concern | Samsung | Roku |
|---------|---------|------|
| **UI Framework** | React + styled-components | SceneGraph XML + BrighterScript |
| **Profile grid** | Custom flexbox layout with `ButtonTv` | `MarkupGrid` or `PosterGrid` with focus handling |
| **Avatar loading** | `<img>` with URL | `Poster` node with `uri` (limited to PNG/JPG) |
| **Profile form** | React controlled inputs | Keyboard + focus-managed LabelList + PosterGrid |
| **State persistence** | Redux Persist (localStorage) | `RegistryUtils` (registryReadJson/registryWriteJson) |
| **Header injection** | Axios interceptor | `RequestUtils.addGlobalHeaders()` |
| **Feature flag** | `NEXT_PUBLIC_FEATURE_MULTI_PROFILE` env var | App-level config constant or registry-env toggle |
| **Navigate back to profiles** | `router.push("/profile")` via React Router | StackNavigator `resetToScreen("ProfileSelectScreen")` |

---

## Acceptance Criteria

### AC-1: Profile Selection Screen
- [ ] New screen `ProfileSelectScreen` shown after splash/login, before home
- [ ] Fetches profiles via `GET /me/my-profiles` on init
- [ ] Displays profile avatars in a grid (MarkupGrid or equivalent)
- [ ] Each profile card shows: avatar image, profile name, profile type badge ("PARENT" or age range)
- [ ] Parent profiles show a shield/badge icon
- [ ] Child profiles show their age range (e.g., "3-5 years")
- [ ] "Add Family" card (circular "+" button) — shown when profile count < max
- [ ] "Add Family" button is dimmed/disabled when at limit
- [ ] First profile is auto-focused on screen entry
- [ ] Track event: `profile_selection_viewed`
- [ ] Track event: `profile_selected` with `{ profile_id, profile_name, profile_type }`

### AC-2: Profile Switch from Settings
- [ ] `SettingScreen` gains a "Switch Profile" menu item (above "Feedback")
- [ ] Selecting "Switch Profile" navigates to `ProfileSelectScreen`
- [ ] Track event: `setting_switch_profile_clicked`

### AC-3: Active Profile State
- [ ] Redoku state field `profile` added with: `{ profile_id, profile_name, profile_avatar, profile_type, content_category_ages }`
- [ ] Reducers: `SET_ACTIVE_PROFILE`, `CLEAR_ACTIVE_PROFILE`
- [ ] Persisted to registry via `registryWriteJson("profile", state)`
- [ ] Loaded from registry on app cold start in `main.bs`
- [ ] Profile header `duriooplus-subscriber-profile-id` added to all API requests via `RequestUtils.addGlobalHeaders()`
- [ ] Profile-scoped content filtering via `content_category_ages` sent on recommender/content API calls

### AC-4: Add Profile — Type Selection
- [ ] New screen `AddProfileTypeScreen` reached from "Add Family" card
- [ ] Shows two options: "Parent" and "Child"
- [ ] Parent option disabled if parent count = 2 (Varies by tier: free users max 1 parent)
- [ ] Child option always enabled (or disabled if at global max)
- [ ] Selecting an option navigates to `ProfileFormScreen` with `mode: "create", type: "parents"|"children"`
- [ ] Track event: `add_profile_type_selected` with `{ type }`

### AC-5: Profile Form — Create & Edit
- [ ] New screen `ProfileFormScreen` supporting `mode: "create"|"edit"`
- [ ] **Name input**: Text input via Roku keyboard dialog or on-screen keyboard component
- [ ] **Avatar selector**: Horizontal scrollable row of avatar `Poster` nodes fetched from `GET /profile-avatars?isParents={bool}`
- [ ] **Content age (children only)**: List of age categories from `GET /content-category-ages`, selectable via radio-button pattern
- [ ] **Save**: Calls `POST /me/create-profile` (create) or `PATCH /me/update-profile/{id}` (edit)
- [ ] **Cancel/Back**: Returns to previous screen
- [ ] On save success: refetch profiles, update Redoku, navigate to ProfileSelectScreen
- [ ] On save error: show error message, allow retry
- [ ] Track events: `profile_form_viewed`, `profile_created`, `profile_updated`

### AC-6: Profile Deletion
- [ ] From `ProfileSelectScreen`, edit button on each profile leads to edit form
- [ ] Edit form has a "Delete Profile" option at the bottom
- [ ] Delete requires confirmation dialog ("Are you sure you want to delete [name]?")
- [ ] Calls `PATCH /me/delete-profile/{profileId}`
- [ ] On success: refetch profiles, update Redoku
- [ ] Track event: `profile_deleted`

### AC-7: Profile Limits
- [ ] **Free users**: max 3 profiles total, max 1 parent
- [ ] **VIP users**: max 6 profiles total, max 2 parents
- [ ] "Add Family" button disabled/dimmed when at total limit
- [ ] Parent option in AddProfileTypeScreen disabled when at parent limit
- [ ] Profile limits enforced client-side before API call
- [ ] If API returns error (e.g., limit reached), show appropriate message

### AC-8: Guest User Handling
- [ ] Guest users do NOT see profile selection — goes straight to Home (existing behavior)
- [ ] Profile screens are only accessible for logged-in users
- [ ] If guest navigates to a profile screen (deeplink/edge case), redirect to Home

### AC-9: Content Filtering by Profile
- [ ] Recommender API calls include profile_id for personalized content
- [ ] If profile has `content_category_ages`, include in content filtering API calls
- [ ] Watch history, continue watching, favorites are profile-scoped (handled by backend via `duriooplus-subscriber-profile-id` header)

### AC-10: Back Navigation
- [ ] From `ProfileSelectScreen`: Back → Exit app (or no-op, since it's the root after login)
- [ ] From `AddProfileTypeScreen`: Back → ProfileSelectScreen
- [ ] From `ProfileFormScreen`: Back → appropriate previous screen (AddProfileTypeScreen or ProfileSelectScreen)
- [ ] Screens properly clean up tasks and observers on teardown

### AC-11: Analytics (Mixpanel)
| Event | Trigger | Properties |
|-------|---------|------------|
| `profile_selection_viewed` | ProfileSelectScreen shown | `{ profile_count }` |
| `profile_selected` | User selects a profile | `{ profile_id, profile_name, profile_type }` |
| `add_profile_tapped` | User taps "Add Family" | `{ current_profile_count }` |
| `add_profile_type_selected` | User selects parent/child | `{ type }` |
| `profile_form_viewed` | ProfileFormScreen shown | `{ mode }` |
| `profile_created` | Profile created successfully | `{ profile_type, has_content_age }` |
| `profile_updated` | Profile updated successfully | `{ profile_type }` |
| `profile_deleted` | Profile deleted | `{ profile_id }` |
| `setting_switch_profile_clicked` | "Switch Profile" selected in settings | `{}` |
| `profile_limit_reached` | "Add Family" tapped at limit | `{ limit, current_count }` |

---

## API Endpoints

### New APIs (Backend — Already Implemented for Samsung)

```
GET /me/my-profiles
  Headers: Authorization: Bearer {token}
  Response: { data: [{
    _id: "profile_uuid",
    profile_name: "Amina",
    profile_avatar: "https://...",
    profile_avatar_id: "avatar_uuid",
    profile_type: "children",
    gender: "female",
    content_category_ages: ["preschool", "younger"],
    blocked_collections: [],
    screen_time_limit: 3600,
    daily_limit: true,
    duminis: true,
    duminis_type: "less frequent",
    duminis_skip_button: true,
    default_speed: 1,
    default_subtitle: "",
    is_premium: false
  }, ...] }

GET /profile-avatars?isParents=true|false
  Response: { data: [{
    _id: "avatar_uuid",
    avatar: "https://...",
    profile_type: "parents"|"children"
  }, ...] }

GET /content-category-ages
  Response: { data: [{
    _id: "age_uuid",
    age_range: "3-5",
    label: "3-5 years"
  }, ...] }

POST /me/create-profile
  Body: {
    profile_avatar_id: "avatar_uuid",
    profile_name: "Amina",
    profile_type: "children",
    content_category_ages: ["age_uuid_1", "age_uuid_2"],
    gender: "female",
    birth_month: 3,
    birth_year: 2020
  }
  Response: { data: { ...profile } }

PATCH /me/update-profile/{profileId}
  Body: { profile_name: "Amina Updated", ... }
  Response: { data: { ...profile } }

PATCH /me/delete-profile/{profileId}
  Response: { success: true }

POST /me/set-active-profile/{profileId}
  Response: { success: true }
```

### Existing APIs — Modified Usage

The `duriooplus-subscriber-profile-id` header is added to all existing API requests when a profile is active:

| API | Change |
|-----|--------|
| `GET /recommender/home-screen?section=...` | Profile ID header → personalized content |
| `GET /recommendations/sections` | Profile ID header → profile-specific sections |
| `GET /browse?...` | Profile ID header → age-filtered results |
| `GET /watch/{id}` | Profile ID header → profile-scoped watch |
| `POST /video-analytics` | Profile ID header → profile-scoped analytics |
| All other endpoints | Profile ID header for consistency |

---

## Files to Create

| File | Purpose |
|------|---------|
| `src/components/screens/profile-select/ProfileSelectScreen.xml` | Profile grid layout |
| `src/components/screens/profile-select/ProfileSelectScreen.bs` | Profile fetching, selection logic |
| `src/components/screens/profile-select/components/ProfileCard.xml` | Individual profile card (avatar + name + badge) |
| `src/components/screens/profile-select/components/ProfileCard.bs` | Profile card logic |
| `src/components/screens/profile-select/components/AddProfileCard.xml` | Add Family button card |
| `src/components/screens/profile-select/components/AddProfileCard.bs` | Add card logic |
| `src/components/screens/profile-form/ProfileFormScreen.xml` | Profile create/edit form layout |
| `src/components/screens/profile-form/ProfileFormScreen.bs` | Form logic, API calls |
| `src/components/screens/profile-form/AddProfileTypeScreen.xml` | Parent/Child choice screen |
| `src/components/screens/profile-form/AddProfileTypeScreen.bs` | Type selection logic |
| `src/components/services/profile/GetProfilesTask.xml` | Task for `GET /me/my-profiles` |
| `src/components/services/profile/GetProfilesTask.bs` | Task logic |
| `src/components/services/profile/CreateProfileTask.xml` | Task for `POST /me/create-profile` |
| `src/components/services/profile/CreateProfileTask.bs` | Task logic |
| `src/components/services/profile/UpdateProfileTask.xml` | Task for `PATCH /me/update-profile/{id}` |
| `src/components/services/profile/UpdateProfileTask.bs` | Task logic |
| `src/components/services/profile/DeleteProfileTask.xml` | Task for `PATCH /me/delete-profile/{id}` |
| `src/components/services/profile/DeleteProfileTask.bs` | Task logic |
| `src/components/services/profile/GetProfileAvatarsTask.xml` | Task for `GET /profile-avatars` |
| `src/components/services/profile/GetProfileAvatarsTask.bs` | Task logic |
| `src/components/services/profile/GetContentAgeCategoriesTask.xml` | Task for `GET /content-category-ages` |
| `src/components/services/profile/GetContentAgeCategoriesTask.bs` | Task logic |

## Files to Modify

| File | Change |
|------|--------|
| `src/source/main.bs` | Add `profile` to `initialState`; load profile from registry on startup |
| `src/source/ActionTypes.bs` | Add `SET_ACTIVE_PROFILE`, `CLEAR_ACTIVE_PROFILE` action types |
| `src/source/Actions.bs` | Add `SetActiveProfile()`, `ClearActiveProfile()` action dispatchers |
| `src/source/Reducers.bs` | Add `Redoku_ProfileReducer`; register profile reducers |
| `src/components/utils/navigation/StackNavigator.bs` | Register `ProfileSelectScreen`, `ProfileFormScreen`, `AddProfileTypeScreen` |
| `src/components/utils/request/RequestUtils.bs` | Add `duriooplus-subscriber-profile-id` header when profile is active |
| `src/components/screens/splash/SplashScreen.bs` | After subscriber check, if multi-profile enabled → go to ProfileSelectScreen instead of Home |
| `src/components/screens/setting/SettingScreen.xml` | Add "Switch Profile" menu item |
| `src/components/screens/setting/SettingScreen.bs` | Handle "Switch Profile" navigation |
| `src/source/SideMenu.bs` | Optionally add profile-related menu items |

---

## Risks & Mitigations

| Risk | Impact | Mitigation |
|------|--------|------------|
| Avatar images fail to load on Roku `Poster` | Blank avatars on profile screen | Use fallback colored placeholder; ensure CDN serves PNG/JPG |
| Profile creation API fails mid-flow | Orphaned profile state | Validate all fields before API call; rollback on error |
| Registry corruption on profile switch | Wrong profile active | Validate profile data on load from registry; fallback to re-fetch |
| Performance: too many API calls with profile header | Slower load times | Profile header is just one string — negligible overhead |
| Content age filtering breaks existing recommender | Empty home screen | Graceful fallback: if no age-filtered content, show unfiltered |
| Profile limits differ between free/VIP | Incorrect limits shown | Fetch subscriber status before profile list; determine limits from subscriber data |
| Guest users don't have `me/my-profiles` endpoint | API 401 error | Skip profile fetch for guest users; go straight to Home |

---

## Test Plan

1. **Happy Path — Profile Selection**: Login → ProfileSelectScreen shown → select profile → Home loads with filtered content
2. **Happy Path — Add Child Profile**: ProfileSelectScreen → Add Family → Child → fill form (name, avatar, age) → Save → new profile appears → select it → Home
3. **Happy Path — Add Parent Profile**: ProfileSelectScreen → Add Family → Parent → fill form (name, avatar) → Save → new profile appears
4. **Edit Profile**: ProfileSelectScreen → edit button on profile → change name → Save → name updated
5. **Delete Profile**: ProfileSelectScreen → edit button → Delete → confirm → profile removed
6. **Profile Limits — Free**: Switch to free account → max 3 profiles → "Add Family" disabled → Parent option limited to 1
7. **Profile Limits — VIP**: VIP account → max 6 profiles → all options available
8. **Switch Profile from Settings**: Home → Side Menu → Settings → "Switch Profile" → ProfileSelectScreen
9. **Guest User**: Logout → guest flow → no profile selection → straight to Home
10. **Content Filtering**: Select child profile with "preschool" age → Home shows only preschool-appropriate content
11. **Registry Persistence**: Select profile → force quit app → reopen → same profile is active
12. **Back Navigation**: On each profile screen, Back navigates correctly to previous screen
