# API — Đổi trạng thái đối ứng của hội thoại chat (対応状況)

Bản port cho **api-job** của route web `/basic/change_status` (`ChatController@changStatus`).
Dùng để gán / gỡ **trạng thái đối ứng** (`status_chat`) cho một hội thoại chat 1:1.
Thuộc nhóm API job/MCP (`middleware = api-job-auth`), cùng pattern với các API khác trong nhóm (`Api\Mobile\*MobileController`).

---

## Thông tin endpoint

| | |
|---|---|
| **Method** | `POST` |
| **URL** | `/api/mobile/chat/change-handling-status` |
| **Controller** | `App\Http\Controllers\Api\Mobile\ChatMobileController@changeHandlingStatus` |
| **Route** | `routes/api.php` — group `prefix=mobile` → `middleware=api-job-auth` (mục `// Chat 1:1`) |
| **Auth** | Bắt buộc — guard `api-job`, middleware `api-job-auth` |
| **Content-Type** | `application/json` |

---

## Request

### Headers

```
Authorization: Bearer <API_JOB_TOKEN>
Content-Type: application/json
```

### Body

| Field | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| `botId` | int | ✅ | ID bot. Người dùng của token phải có quyền trên bot (kiểm tra `UserStaffBot`). |
| `conversation_id` | int | ✅ | ID hội thoại (`conversation.id`) cần đổi trạng thái. |
| `status_id` | int \| null | ✅ | ID trạng thái (`status_chat.id`) muốn gán. Gửi `0`, `null` hoặc rỗng để **gỡ** trạng thái hiện tại. |

> `botId` viết camelCase (theo `ensureBotContext` dùng chung của nhóm); các field nghiệp vụ (`conversation_id`, `status_id`) viết snake_case.

### Ví dụ

```json
{
  "botId": 123,
  "conversation_id": 45678,
  "status_id": 5
}
```

Gỡ trạng thái:

```json
{
  "botId": 123,
  "conversation_id": 45678,
  "status_id": 0
}
```

---

## Response

Format `status / data / msg` (giống các API khác trong nhóm `api-job-auth`).

### Thành công — HTTP 200

```json
{
  "status": true,
  "data": {
    "status_confirm": 5,
    "last_msg": "...",
    "detail": {
      "is_has_msg": 1,
      "special_status": 5,
      "name_status": "対応中",
      "color": "#FFFFFF",
      "bg_status": "#FF0000",
      "bg_choose": "#CC0000",
      "id_status": 5,
      "total_confirm": 0
    }
  },
  "msg": ""
}
```

| Field | Mô tả |
|---|---|
| `data.status_confirm` | ID trạng thái vừa gán (`null` nếu vừa gỡ). |
| `data.last_msg` | `conversation.status_last_message`. |
| `data.detail.special_status` | = `status_id` vừa gán (`null` nếu gỡ). |
| `data.detail.name_status` | Tên trạng thái (`status_chat.name_status`). Rỗng nếu gỡ / không tìm thấy. |
| `data.detail.color` | Màu chữ (`status_chat.color`). |
| `data.detail.bg_status` | Màu nền (`status_chat.bg_status`). |
| `data.detail.bg_choose` | Màu nền khi chọn (`status_chat.bg_choose`). |
| `data.detail.id_status` | ID trạng thái đang gán (rỗng nếu gỡ). |
| `data.detail.total_confirm` | `conversation.confirm_count`. |

### Khi gỡ trạng thái (`status_id = 0/null`)

`status_confirm = null`; các field trong `detail` (`name_status`, `color`, `bg_status`, `bg_choose`, `id_status`) trả về rỗng/null.

### Lỗi

| Trường hợp | HTTP | Response |
|---|---|---|
| Thiếu `botId` | 400 | `{ "success": false, "message": "botId is required." }` |
| Token không có quyền trên bot | 422 | `{ "success": false, "message": "bot_invalid_setting" }` |
| Hội thoại không tồn tại / không thuộc bot | 404 | `{ "status": false, "msg": "conversation_not_found" }` |
| Exception khi xử lý | 200 | `{ "status": false, "msg": "<message>" }` (đã ghi log) |

---

## Logic xử lý

1. `ensureBotContext`: bắt buộc `botId`, kiểm tra quyền qua `UserStaffBot`, set `current_bot_id` vào session → `getBotId()` dùng được.
2. Chuẩn hoá input: `status_id` rỗng/`0` → `null`.
3. Load hội thoại theo `conversation_id` **và** `bot_id` (chống IDOR). Không thấy → `conversation_not_found` (404).
4. Lấy metadata trạng thái mới (`status_chat` theo `id` + `bot_id`); nếu có → `count + 1`.
5. Trạng thái cũ (`conversation.id_status` trước khi đổi) → `count - 1`, kẹp không âm.
6. Cập nhật `conversation.id_status = status_id` (lưu qua `save()`).
7. Tính lại `Bots.count_user_unconfirm` (`totalUserConfirmMessage`) + set `last_time_count_user_confirm = now()`.
8. Đẩy `SyncElasticsearch` với `type = update`, `data_sync = { status_id }`.
9. Trả `status_confirm` + `last_msg` + `detail`.

---

## Bảng / model liên quan

| Model | Bảng | Vai trò |
|---|---|---|
| `App\Conversation` | `conversation` | Cột `id_status`, `status_last_message`, `confirm_count`, `line_id`, `bot_id`. |
| `App\StatusChat` | `status_chat` | Định nghĩa trạng thái + bộ đếm `count`. |
| `App\Bots` | `bots` | Cập nhật `count_user_unconfirm`, `last_time_count_user_confirm`. |
| `App\UserStaffBot` | `user_staff_bot` | Kiểm tra quyền user ↔ bot trong `ensureBotContext`. |
| `App\SyncElasticsearch` | — | Đồng bộ trạng thái sang Elasticsearch. |

---

## Khác biệt so với route web `/basic/change_status`

| | Web (`changStatus`) | api-job (`changeHandlingStatus`) |
|---|---|---|
| Nhóm / Auth | Session (`basic_access`) | Guard `api-job` (`api-job-auth`) |
| Nguồn `bot_id` | `botIdCurrent` / `getBotId()` (session) | `ensureBotContext(botId)` → `getBotId()` |
| Kiểm tra quyền bot | Không | Có (`UserStaffBot`) |
| Tên field | `conversation`, `status` | `conversation_id`, `status_id` |
| Kiểm tra sở hữu hội thoại | Không (chỉ theo `id`) | Có (`id` + `bot_id`) |
| Format response | `{ success, status_confirm, last_msg, detail }` | `{ status, data:{ status_confirm, last_msg, detail }, msg }` |
| Log | `Log::` facade | Helper `logInfo` / `logError` (`[Mobile][changeHandlingStatus][B{botId}]`) |

---

## Ví dụ gọi (cURL)

```bash
curl -X POST "https://<host>/api/mobile/chat/change-handling-status" \
  -H "Authorization: Bearer <API_JOB_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"botId":123,"conversation_id":45678,"status_id":5}'
```
