# Heat User - Frontend Flow (Updated)

## 🎯 Flow chính xác cho Frontend

### **Vấn đề:**
- User có thể clear localStorage
- User có thể đổi device/browser
- FE cần lấy lại session_id từ server

### **Giải pháp:**
API mới: `POST /api/heat-user/study-session/check-existing`

---

## 🔄 FLOW 1: User vào trang làm bài (On Page Load)

### **Timeline:**

```
[Step 1] User vào trang làm bài

[Step 2] FE check localStorage
         ├─> Có sessionId? → Dùng localStorage (fast path)
         └─> KHÔNG có? → Gọi API check-existing (fallback)

[Step 3A] Nếu có sessionId trong localStorage:
          ├─> Hiển thị [TIẾP TỤC] button
          └─> onClick → gọi continue API với sessionId

[Step 3B] Nếu KHÔNG có sessionId:
          ├─> FE gọi API check-existing
          │   POST /api/heat-user/study-session/check-existing
          │   Body: { 
          │     id_history: 200,  ← Từ URL params hoặc context
          │     student_id: 123 
          │   }
          │
          ├─> Response có has_existing = true?
          │   ├─> YES: Hiển thị [TIẾP TỤC] với session_id từ response
          │   └─> NO: Hiển thị [BẮT ĐẦU]
```

---

## 📋 CASE 1: User có session cũ (localStorage OK)

### **Timeline:**

```
┌──────────────────────────────────────────────────────────────────┐
│  User vào trang làm bài                                          │
└──────────────────────────────────────────────────────────────────┘

[Step 1] Page load
         ├─> Check localStorage
         └─> Found: { sessionId: 1, idHistory: 200, startedAt: "..." }

[Step 2] FE hiển thị UI:
         ┌─────────────────────────────────────┐
         │  Bạn có bài làm chưa hoàn thành     │
         │                                     │
         │  [TIẾP TỤC LÀM BÀI]  [BẮT ĐẦU MỚI] │
         └─────────────────────────────────────┘

[Step 3] User click [TIẾP TỤC]
         ├─> FE gọi: POST /api/heat-user/study-session/continue
         │   Body: { session_id: 1 }  ← Từ localStorage
         │
         └─> Response: { session_id: 2, ... }
         └─> Update localStorage với sessionId: 2
```

**✅ Ưu điểm:** Nhanh, không cần gọi thêm API

---

## 📋 CASE 2: User KHÔNG có localStorage (clear hoặc device mới)

### **Timeline:**

```
┌──────────────────────────────────────────────────────────────────┐
│  User vào trang làm bài (đã clear localStorage)                 │
└──────────────────────────────────────────────────────────────────┘

[Step 1] Page load
         ├─> Check localStorage
         └─> EMPTY (đã clear hoặc device mới)

[Step 2] FE gọi API check-existing ⭐
         ┌────────────────────────────────────────────────┐
         │ POST /api/heat-user/study-session/check-      │
         │      existing                                  │
         │ Body: {                                        │
         │   id_history: 200,        ← Từ URL/context    │
         │   student_id: 123                              │
         │ }                                              │
         │                                                │
         │ ✅ Backend Processing:                         │
         │ 1. Query:                                      │
         │    SELECT * FROM student_study_sessions        │
         │    WHERE student_id = 123                      │
         │      AND id_history = 200                      │
         │      AND status IN ('active', 'auto_ended')    │
         │    ORDER BY created_at DESC                    │
         │    LIMIT 1                                     │
         │                                                │
         │ 2. Found session_id = 1                        │
         │                                                │
         │ Response: {                                    │
         │   success: true,                               │
         │   data: {                                      │
         │     has_existing: true,                        │
         │     session_id: 1,           ← ⭐ KEY INFO     │
         │     id_history: 200,                           │
         │     id_history_contest: 100,                   │
         │     status: "auto_ended",                      │
         │     started_at: "2025-12-04 10:00:00",        │
         │     last_heartbeat_at: "2025-12-04 10:10:00"  │
         │   }                                            │
         │ }                                              │
         └────────────────────────────────────────────────┘

[Step 3] FE xử lý response:
         ├─> has_existing = true
         ├─> Lưu vào localStorage: { sessionId: 1, idHistory: 200 }
         └─> Hiển thị UI:
             ┌─────────────────────────────────────┐
             │  Bạn có bài làm chưa hoàn thành     │
             │  (Bắt đầu lúc: 10:00, Ngày 04/12)   │
             │                                     │
             │  [TIẾP TỤC LÀM BÀI]  [BẮT ĐẦU MỚI] │
             └─────────────────────────────────────┘

[Step 4] User click [TIẾP TỤC]
         ├─> FE gọi: POST /api/heat-user/study-session/continue
         │   Body: { session_id: 1 }  ← Từ response của check-existing
         │
         └─> Response: { session_id: 2, ... }
```

**✅ Ưu điểm:** 
- Recover session_id từ server
- Work across devices
- User không mất session khi clear localStorage

---

## 📋 CASE 3: KHÔNG có session cũ

### **Timeline:**

```
[Step 1] Page load → Check localStorage → EMPTY

[Step 2] FE gọi check-existing
         Response: {
           success: true,
           data: {
             has_existing: false,  ← Không tìm thấy
             session_id: null,
             message: "No existing session found"
           }
         }

[Step 3] FE hiển thị UI:
         ┌─────────────────────────────────────┐
         │  Bài thi mới                        │
         │                                     │
         │  [BẮT ĐẦU LÀM BÀI]                  │
         └─────────────────────────────────────┘

[Step 4] User click [BẮT ĐẦU]
         ├─> FE gọi EMS APIs (Step 1 & 2)
         └─> FE gọi Heat User start API
```

---

## 📋 CASE 4: Multi-part Contest (check by idHistoryContest)

### **Timeline:**

```
Scenario: IELTS có 4 parts, user đã làm xong Part 1 & 2

[Step 1] User vào làm Part 3 (idHistory: 203)

[Step 2] FE gọi check-existing với idHistory: 203
         Response: {
           has_existing: false  ← Part 3 chưa start
         }

[Step 3] FE cho user start Part 3

─── Hoặc ───

[Step 2B] FE gọi check-existing với idHistoryContest: 100
          ┌────────────────────────────────────────────────┐
          │ POST /api/heat-user/study-session/check-      │
          │      existing                                  │
          │ Body: {                                        │
          │   id_history_contest: 100,  ← Check contest   │
          │   student_id: 123                              │
          │ }                                              │
          │                                                │
          │ Response: {                                    │
          │   has_existing: true,                          │
          │   session_id: 5,                               │
          │   id_history: 202,        ← Part 2 chưa xong  │
          │   status: "auto_ended"                         │
          │ }                                              │
          └────────────────────────────────────────────────┘

[Step 3B] FE nhận biết:
          ├─> User đang ở giữa contest (Part 2 chưa xong)
          └─> Hiển thị: "Bạn cần hoàn thành Part 2 trước"
```

---

## 🎯 Best Practice cho Frontend

### **Recommended Flow:**

```javascript
// On page load
async function initExamPage(idHistory) {
  // 1. Try localStorage first (fast path)
  const stored = localStorage.getItem('study_session');
  if (stored) {
    const session = JSON.parse(stored);
    if (session.idHistory === idHistory) {
      // Match! Show continue button
      showContinueButton(session.sessionId);
      return;
    }
  }
  
  // 2. Fallback: Check with server
  const response = await fetch('/api/heat-user/study-session/check-existing', {
    method: 'POST',
    body: JSON.stringify({
      id_history: idHistory,
      student_id: getCurrentStudentId()
    })
  });
  
  const result = await response.json();
  
  if (result.data.has_existing) {
    // Found existing session on server
    const sessionId = result.data.session_id;
    
    // Save to localStorage for next time
    localStorage.setItem('study_session', JSON.stringify({
      sessionId: sessionId,
      idHistory: result.data.id_history,
      idHistoryContest: result.data.id_history_contest,
      startedAt: result.data.started_at
    }));
    
    // Show continue button
    showContinueButton(sessionId);
  } else {
    // No existing session
    showStartButton();
  }
}
```

---

## 📊 API Reference

### **POST /api/heat-user/study-session/check-existing**

**Purpose:** Check if student has existing session that can be continued

**Request (Option 1 - by idHistory):**
```json
{
  "id_history": 200,
  "student_id": 123
}
```

**Request (Option 2 - by idHistoryContest):**
```json
{
  "id_history_contest": 100,
  "student_id": 123
}
```

**Response (Has existing):**
```json
{
  "success": true,
  "data": {
    "has_existing": true,
    "session_id": 1,
    "id_history": 200,
    "id_history_contest": 100,
    "status": "auto_ended",
    "started_at": "2025-12-04T10:00:00Z",
    "last_heartbeat_at": "2025-12-04T10:10:00Z",
    "message": "Found existing session that can be continued"
  }
}
```

**Response (No existing):**
```json
{
  "success": true,
  "data": {
    "has_existing": false,
    "session_id": null,
    "status": null,
    "message": "No existing session found"
  }
}
```

---

## 🔑 Key Points

1. **Always check server first** nếu không có localStorage
2. **LocalStorage is cache**, không phải source of truth
3. **Server is source of truth** cho session state
4. **Can query by idHistory OR idHistoryContest**
5. **Response includes all info needed** để hiển thị UI

---

## ⚠️ Edge Cases

### **Case A: User làm 2 devices cùng lúc**

```
Device 1: Start session 1 (10:00)
Device 2: Check-existing → Found session 1
Device 2: Continue → Create session 2
Device 1: Heartbeat fails? (session 1 đã expired)

Solution: Heartbeat API should return error if session not active
→ FE refresh và gọi check-existing lại
```

### **Case B: idHistoryContest query trả về session nào?**

```
Contest có 4 parts:
- Part 1 (idHistory: 201): completed
- Part 2 (idHistory: 202): auto_ended  ← Most recent
- Part 3 (idHistory: 203): not started
- Part 4 (idHistory: 204): not started

Query by idHistoryContest → Returns Part 2 session
(Most recent session with status IN ['active', 'auto_ended'])
```

---

## 📖 Summary

**Problem solved:**
- ✅ FE có thể recover session_id từ server
- ✅ Work across devices/browsers
- ✅ Không mất session khi clear localStorage
- ✅ Source of truth là server, không phải localStorage

**Flow:**
1. Try localStorage first (optimization)
2. Fallback to check-existing API (reliability)
3. Show appropriate UI (continue or start)
4. Save session_id to localStorage for next time

---

**Document này dành cho Frontend developers.**

