# SDK Message Protocol

Game giao tiếp với SDK thông qua **postMessage** trong hidden iframe. Game chỉ cần biết các message types, payloads, và khi nào gửi/nhận.

---

## 🔌 Architecture

```
┌──────────────────────────────┐
│   Game (React/Vue/etc)       │
│                              │
│  window.parent.postMessage() │
│  window.addEventListener()   │
└──────────────┬───────────────┘
               │ postMessage
               │ (JSON)
               ↓
    ┌──────────────────────┐
    │  Hidden Iframe       │
    │  (sdk-iframe.html)   │
    │                      │
    │  - Sanitize data     │
    │  - Verify answers    │
    │  - Call API          │
    │  - Send responses    │
    └──────────────────────┘
```

---

## 📨 Message Types

### 1️⃣ **SDK_INIT** (Game → SDK)

Game khởi tạo SDK với mode và game_code.

**Gửi:**
```javascript
window.parent.postMessage({
    type: 'SDK_INIT',
    payload: {
        mode: 'dev' | 'preview' | 'live',
        game_code: 'G001' | 'G002' | ... | 'G123',
        
        // LIVE mode only:
        assignment_id?: string,
        student_id?: string,
        api_base_url?: string,
        auth_token?: string
    }
}, '*');
```

**Ví dụ DEV mode:**
```javascript
window.parent.postMessage({
    type: 'SDK_INIT',
    payload: {
        mode: 'dev',
        game_code: 'G001'
    }
}, '*');
```

**Ví dụ LIVE mode:**
```javascript
window.parent.postMessage({
    type: 'SDK_INIT',
    payload: {
        mode: 'live',
        game_code: 'G001',
        assignment_id: 'ASSIGN_123',
        student_id: 'STU_456',
        api_base_url: 'https://api.sena.tech',
        auth_token: 'token_xyz'
    }
}, '*');
```

**Nhận (SDK gửi lại khi ready):**
```javascript
{
    type: 'SDK_READY',
    payload: {
        mode: 'dev' | 'preview' | 'live',
        game_code: 'G001'
    }
}
```

---

### 2️⃣ **SDK_DATA_READY** (SDK → Game)

SDK gửi sanitized data cho game render. Game phải listen sự kiện này.

**Nhận:**
```javascript
window.addEventListener('message', (event) => {
    if (event.data.type === 'SDK_DATA_READY') {
        const {
            items,              // Sanitized items (NO answers!)
            total_questions,
            completed_count,
            resume_data         // Optional: previous results
        } = event.data.payload;
        
        // Render game
        renderGame(items);
    }
});
```

**Payload items (tùy game_code):**

#### Quiz Games (G001-G004):
```json
{
    "id": "q1",
    "question": "What is 2+2?",
    "options": [
        {"text": "5"},
        {"text": "4"},
        {"text": "3"}
    ]
}
```

#### Sequence Games (G110-G123):
```json
{
    "id": "seq1",
    "question": ["H", "", "L", "", "O"],
    "options": ["L", "E"],
    "audio_url": "https://..."  // optional
}
```

---

### 3️⃣ **SDK_CHECK_ANSWER** (Game → SDK)

Game gửi user's answer để SDK verify.

**Gửi:**
```javascript
window.parent.postMessage({
    type: 'SDK_CHECK_ANSWER',
    payload: {
        question_id: 'q1',
        choice: any,        // Index (quiz) hoặc Array (sequence)
        time_spent?: number // Milliseconds
    }
}, '*');
```

**Quiz example (choice = index):**
```javascript
window.parent.postMessage({
    type: 'SDK_CHECK_ANSWER',
    payload: {
        question_id: 'q1',
        choice: 1,          // User clicked option index 1
        time_spent: 5000
    }
}, '*');
```

**Sequence example (choice = reordered array):**
```javascript
window.parent.postMessage({
    type: 'SDK_CHECK_ANSWER',
    payload: {
        question_id: 'seq1',
        choice: ["H", "e", "l", "l", "o"],  // Reordered
        time_spent: 8000
    }
}, '*');
```

---

### 4️⃣ **SDK_ANSWER_RESULT** (SDK → Game)

SDK gửi kết quả verify.

**Nhận:**
```javascript
window.addEventListener('message', (event) => {
    if (event.data.type === 'SDK_ANSWER_RESULT') {
        const {
            question_id,
            correct,    // true/false
            score,      // 0-1 hoặc custom
            synced,     // true = already synced to server
            feedback    // Optional: "✅ Correct!" or "❌ Wrong"
        } = event.data.payload;
        
        if (correct) {
            showCorrectFeedback(question_id);
        } else {
            showWrongFeedback(question_id);
        }
    }
});
```

---

### 5️⃣ **SDK_PUSH_DATA** (Game → SDK, PREVIEW only)

Nếu PREVIEW mode, game có thể push data thay vì SDK fetch.

**Gửi:**
```javascript
window.parent.postMessage({
    type: 'SDK_PUSH_DATA',
    payload: {
        items: [
            {
                id: 'q1',
                question: 'What is 2+2?',
                options: [
                    {text: '3'},
                    {text: '4'},
                    {text: '5'}
                ],
                answer: '4'  // Server data (with answer)
            },
            // ... more items
        ]
    }
}, '*');
```

SDK sẽ sanitize (remove answer, shuffle options) rồi gửi SDK_DATA_READY.

---

### 6️⃣ **SDK_ERROR** (SDK → Game)

SDK gửi error notification.

**Nhận:**
```javascript
window.addEventListener('message', (event) => {
    if (event.data.type === 'SDK_ERROR') {
        const {
            code,       // Error code
            message,    // Error message
            details     // Optional: more info
        } = event.data.payload;
        
        console.error(`[SDK Error] ${code}: ${message}`);
    }
});
```

---

## 🎮 Data Structures by Game Type

### Quiz Games (G001-G004)

#### Sanitized (Game receives):
```json
{
    "id": "q1",
    "question": "Audio URL or text",
    "image_url": "Image URL (optional)",
    "options": [
        {"text": "Option A"} or {"audio": "Audio URL"}
    ]
}
```

#### User Answer Format:
```javascript
choice = 0  // Index of selected option
```

---

### Sequence Word (G110-G113)

#### Sanitized (Game receives):
```json
{
    "id": "seq_word_1",
    "question": ["H", "", "L", "", "O"],
    "options": ["L", "E"],
    "audio_url": "URL (optional)"
}
```

#### User Answer Format:
```javascript
choice = ["H", "e", "l", "l", "o"]  // Reordered array
```

---

### Sequence Sentence (G120-G123)

#### Sanitized (Game receives):
```json
{
    "id": "seq_sent_1",
    "question": ["I", "", "reading", ""],
    "options": ["love", "books"],
    "audio_url": "URL (optional)"
}
```

#### User Answer Format:
```javascript
choice = ["I", "love", "reading", "books"]  // Reordered
```

---

## 💻 Complete Game Implementation Example

```javascript
// ============ INITIALIZE ============

// Listen for SDK messages
window.addEventListener('message', (event) => {
    handleSdkMessage(event.data);
});

// Initialize SDK
function initGame() {
    const mode = 'live';
    const gameCode = 'G001';
    
    window.parent.postMessage({
        type: 'SDK_INIT',
        payload: {
            mode,
            game_code: gameCode,
            assignment_id: 'ASSIGN_123',
            student_id: 'STU_456',
            api_base_url: 'https://api.sena.tech'
        }
    }, '*');
}

// Handle all SDK messages
function handleSdkMessage(data) {
    switch (data.type) {
        case 'SDK_READY':
            console.log('SDK initialized:', data.payload);
            break;
            
        case 'SDK_DATA_READY':
            const items = data.payload.items;
            renderGameItems(items);
            break;
            
        case 'SDK_ANSWER_RESULT':
            const result = data.payload;
            if (result.correct) {
                showCorrectFeedback(result.question_id);
            } else {
                showWrongFeedback(result.question_id);
            }
            break;
            
        case 'SDK_ERROR':
            console.error('SDK Error:', data.payload);
            break;
    }
}

// ============ RENDER GAME ============

function renderGameItems(items) {
    items.forEach(item => {
        if (item.options) {
            // Quiz game
            renderQuizQuestion(item);
        } else if (Array.isArray(item.question)) {
            // Sequence game
            renderSequenceGame(item);
        }
    });
}

// ============ HANDLE USER ANSWER ============

function submitAnswer(questionId, userChoice) {
    window.parent.postMessage({
        type: 'SDK_CHECK_ANSWER',
        payload: {
            question_id: questionId,
            choice: userChoice,  // Index for quiz, array for sequence
            time_spent: 5000
        }
    }, '*');
}

// ============ GAME START ============

// Start when page loads
window.addEventListener('load', () => {
    initGame();
});
```

---

## 🔄 Message Flow Examples

### DEV Mode (Local Verify)
```
Game: SDK_INIT
  ↓
SDK: SDK_READY
  ↓
SDK: SDK_DATA_READY (mock items)
  ↓
Game: SDK_CHECK_ANSWER
  ↓
SDK: SDK_ANSWER_RESULT (instantly)
```

### LIVE Mode (Server Verify)
```
Game: SDK_INIT
  ↓
SDK: SDK_READY
  ↓
SDK: fetch from API → SDK_DATA_READY
  ↓
Game: SDK_CHECK_ANSWER
  ↓
SDK: POST to server
  ↓
SDK: SDK_ANSWER_RESULT (wait for server)
```

### PREVIEW Mode (Push Data)
```
Game: SDK_INIT (mode='preview')
  ↓
SDK: SDK_READY
  ↓
Game: SDK_PUSH_DATA (items with answers)
  ↓
SDK: sanitize → SDK_DATA_READY
  ↓
Game: SDK_CHECK_ANSWER
  ↓
SDK: SDK_ANSWER_RESULT (local verify)
```

---

## ⚠️ Important Notes

✅ **SDK Always Sanitizes**
- Removes `answer` field
- Removes `word`, `sentence`, `parts`, `missing_letter_count`
- Shuffles options (for quiz)

✅ **Game Never Gets Answer**
- Game receives only question + options
- Answer is stored server-side

✅ **Shuffled Options**
- Game receives shuffled options array
- Game user clicks index
- SDK internally resolves index → text

✅ **Sequence Games**
- Random positions are blanked (based on `missing_letter_count`)
- Game receives question with blanks + missing items
- User reorders missing items

---

## 🚀 Testing

Test locally without SDK:
```javascript
// Simulate SDK messages for testing
function simulateSdkMessage(type, payload) {
    const event = new MessageEvent('message', {
        data: { type, payload }
    });
    window.dispatchEvent(event);
}

// Simulate SDK_DATA_READY
simulateSdkMessage('SDK_DATA_READY', {
    items: [
        {
            id: 'q1',
            question: 'What is 2+2?',
            options: [{text: '4'}, {text: '5'}, {text: '3'}]
        }
    ]
});
```
