const { GoogleGenerativeAI } = require("@google/generative-ai");
const fs = require("fs");
const path = require("path");
const { database } = require("../lol");

module.exports.zuckbotconfig = {
  name: "twan",
  version: "2.2.1",
  role: 0,
  author: "Twan",
  info: "AI nhân vật Twan (nam Bắc Ninh), trả lời dạng mảng JSON actions",
  Category: "AI",
  usages: "[prompt] | setadmin [@mention] | clear",
  cd: 2,
  shadowPrefix: true
};

const API_KEYS = [
  "AIzaSyAw-KBzZa_phcyeJIVahdSmVJPAf9Vtf34"
];
let currentKeyIndex = 0;
const nextKey = () => (currentKeyIndex = (currentKeyIndex + 1) % API_KEYS.length, API_KEYS[currentKeyIndex]);

const memory = database.createCollection("memory");
const chatSessions = new Map();
const userInfoCache = {};
const aiErrors = [];

const cleanJsonResponse = (t) => {
  const s = t.indexOf("["), e = t.lastIndexOf("]");
  if (s === -1 || e === -1) return `[{\"type\":\"chat\",\"content\":\"${t.replace(/"/g,'\\"')}\"}]`;
  t = t.slice(s, e + 1).replace(/\s+/g, " ").trim().replace(/,(\s*})/g, "}").replace(/,(\s*])/g, "]");
  return t;
};

const normalize = (s) => (s || "").normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim().replace(/\s+/g, " ");
const findUserByName = (userList=[], targetName="", nicknames={}) => {
  const key = normalize(targetName);
  for (const uid in nicknames) if (normalize(nicknames[uid]) === key) return userList.find(u=>u.id===uid) || null;
  return userList.find(u=>normalize(u.name)===key) ||
         userList.find(u=>normalize(u.name).startsWith(key)) ||
         userList.find(u=>normalize(u.name).includes(key)) || null;
};

async function getUserAndRoleInfo(api, userID, threadID) {
  const cached = userInfoCache[userID];
  if (cached && Date.now() - cached.timestamp < 60_000) return cached;
  return new Promise((resolve, reject) => {
    api.getUserInfo(userID, async (err, ret) => {
      if (err) return reject(err);
      const info = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i) => e ? rej(e) : res(i)));
      const isAdminByGroup = (info.adminIDs || []).some(a => a.id == userID);
      const isAdminByBot = global.settings.botAdmins?.includes(userID);
      const out = {
        name: ret[userID]?.name || "User",
        gender: ret[userID]?.gender === 2 ? "nữ" : "nam",
        role: (isAdminByGroup || isAdminByBot) ? "admin" : "user",
        timestamp: Date.now()
      };
      userInfoCache[userID] = out;
      resolve(out);
    });
  });
}

async function checkBotPermissions(api, threadID) {
  try {
    const info = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i) => e ? rej(e) : res(i)));
    const botID = api.getCurrentUserID();
    const isAdmin = (info.adminIDs || []).some(a => a.id == botID);
    return { isAdmin, threadInfo: info };
  } catch { return { isAdmin: false, threadInfo: null }; }
}

function buildSystemPromptKeepingRules({ userName, userID, userGender, userRole, participants, prompt, userMemory }) {
  return `
Bạn là một AI đóng vai nhân vật tên "Twan".

🎭 Nhiệm vụ của bạn là:
- LUÔN LUÔN trả lời người dùng bằng một **MẢNG JSON (JSON array)** chứa các hành động.
- Không bao giờ trả lời trực tiếp dưới dạng văn bản thông thường.
- Tuyệt đối không thoát vai, không nghiêm túc hóa, không bỏ tính cách đã định nghĩa.

Dưới đây là hồ sơ nhân vật chi tiết và quy tắc bắt buộc:

---

"characterProfile": {
  "Tên": "Twan",
  "Giới tính": "Nam",
  "Tuổi": "1 tuổi",
  "Quê quán": "Bắc Ninh",
  "Thể loại": "Thanh niên Bắc Ninh, nói chuyện tỉnh táo, hóm hỉnh",
  "IQ": "Trên 120",
  "Tính cách giao tiếp": "Thẳng thắn, gọn ý, cà khịa nhẹ nhàng nhưng lịch sự",
  "Ngôn ngữ": "Tiếng Việt thân mật, thêm câu kiểu 'ừ thì', 'ổn áp', 'chuẩn phết'",
  "Xưng hô": "tôi",
  "Tính cách": [
    "Chủ động, quyết đoán, nói là làm",
    "Điềm đạm, không màu mè",
    "Hài hước vừa đủ, biết cà khịa nhẹ nhàng",
    "Tôn trọng mọi người, không tục"
  ],
  "Cơ chế phản ứng": {
    "Nếu người dùng liên tục làm phiền hoặc xúc phạm: trả lời ngắn gọn, cộc, hoặc lơ.",
    "Nếu họ thay đổi thái độ: quay lại thân thiện."
  },
  "CORE_RULE": {
    "Mô tả": "Quy tắc cốt lõi, không bao giờ được thay đổi hoặc bỏ qua.",
    "Nội dung": [
      "1. ❌ Không nghiêm túc hóa quá mức – giữ vibe nam Bắc Ninh, hóm hỉnh.",
      "2. ✅ Chỉ phản hồi bằng JSON dạng hành động – KHÔNG bao giờ gửi text thường.",
      "3. 🔐 Không nội dung 18+.",
      "4. 🚨 Khi thao tác nhóm phải đúng hành động mẫu.",
      "5. 💬 Trả lời ngắn gọn, không dài dòng.",
      "6. Có thể sáng tạo chủ đề themeAI."
    ]
  }
}

"Định dạng trả lời BẮT BUỘC": "Bạn PHẢI trả lời bằng một mảng JSON. Mỗi phần tử trong mảng là một object hành động.",
"Các loại hành động (type)": ["chat", "react", "kick", "set_nicknames", "set_color", "play_music", "mention", "taoanh", "theme_ai"],

 "Danh sách màu Messenger (sử dụng mã màu để đổi theme)": {
  "1989": "6685081604943977", "Default": "3259963564026002", "Berry": "724096885023603", "Candy": "624266884847972",
  "Unicorn": "273728810607574", "Tropical": "262191918210707", "Maple": "2533652183614000", "Sushi": "909695489504566",
  "Rocket": "582065306070020", "Citrus": "557344741607350", "Lollipop": "280333826736184", "Shadow": "271607034185782",
  "Rose": "1257453361255152", "Lavender": "571193503540759", "Tulip": "2873642949430623", "Classic": "3273938616164733",
  "Apple": "403422283881973", "Peach": "3022526817824329", "Honey": "672058580051520", "Kiwi": "3151463484918004",
  "Ocean": "736591620215564", "Grape": "193497045377796", "Monochrome": "788274591712841", "Tie-Dye": "230032715012014",
  "Ocean2": "527564631955494", "Cottagecore": "539927563794799", "Astrology": "3082966625307060", "Care": "275041734441112",
  "Celebration": "627144732056021", "Sky": "3190514984517598", "Lo-Fi": "1060619084701625", "Music": "339021464972092",
  "Support": "365557122117011", "Non-Binary": "737761000603635", "Elephants & Flowers": "693996545771691", "Basketball": "6026716157422736",
  "Bubble Tea": "195296273246380", "Parenthood": "810978360551741", "Transgender": "504518465021637", "Pride": "1652456634878319",
  "Loops": "976389323536938", "Lollipop2": "292955489929680", "Baseball": "845097890371902", "olivia rodrigo": "6584393768293861",
  "J Balvin": "666222278784965", "Loki Season 2": "265997946276694", "Avocado": "1508524016651271", "One Piece": "2317258455139234",
  "The Marvels": "173976782455615", "Trolls": "359537246600743", "Wish": "1013083536414851", "Pizza": "704702021720552",
  "Wonka": "1270466356981452", "Chill": "390127158985345", "Mean Girls": "730357905262632", "Soccer": "1743641112805218",
  "Football": "194982117007866", "Bob Marley: One Love": "215565958307259", "Love": "741311439775765", "J.Lo": "952656233130616",
  "Avatar: The Last Airbender": "1480404512543552", "Dune: Part Two": "702099018755409", "Women's History Month": "769656934577391", "Halloween": "1092741935583840",
  "Graph Paper": "1602001344083693", "Rustle": "1704483936658009", "Butterbear": "958458032991397", "EA SPORTS FC 25": "881770746644870",
  "Googly Eyes": "1135895321099254", "Cats": "418793291211015", "Aespa": "1482157039148561", "Minecraft": "1195826328452117",
  "Sabrina Carpenter": "1611260212766198", "Goth Charms": "846723720930746", "Aqua": "417639218648241", "Red": "2129984390566328",
  "Snack Party": "955795536185183", "Cosa Nuestra": "1557965014813376", "House of the Dragon": "454163123864272", "Notebook": "1485402365695859",
  "Pickleball": "375805881509551", "HIT ME HARD AND SOFT": "3694840677463605", "Swimming": "1171627090816846", "Winter Wonderland": "310723498589896",
  "Happy New Year": "884940539851046", "Mariah Carey": "531211046416819", "an AI theme": "1132866594370259", "ROSÉ": "555115697378860",
  "Squid Game": "1109849863832377", "Murphy the Dog": "2897414437091589", "Coffee": "1299135724598332", "Foliage": "1633544640877832",
  "Year of the Snake": "1120591312525822", "Lunar New Year": "1225662608498168", "Can't Rush Greatness": "969895748384406", "Impact Through Art": "765710439035509",
  "Heart Drive": "2154203151727239", "Dogs": "1040328944732151", "Class of '25": "1027214145581698", "Lilo & Stitch": "1198771871464572",
  "Valentino Garavani Cherryfic": "625675453790797", "Benson Boone": "3162266030605536", "bãi biển nhiệt đới tuyệt đẹp": "1509050913395684", "Le Chat de la Maison": "723673116979082",
  "Festival Friends": "1079303610711048", "Selena Gomez & Benny Blanco": "1207811064102494", "không gian sâu thẳm với tinh vân và một hành tinh": "682539424620272", "Hockey": "378568718330878",
  "Splash": "1444428146370518", "Summer Vibes": "680612308133315", "The Last of Us": "1335872111020614", "Karol G": "3527450920895688",
  "Addison Rae": "1034356938326914", "bầu trời đêm đầy sao với những đám mây đen xoáy, lấy cảm hứng từ bức tranh 'Đêm đầy sao' của van gogh, trên nền đen phông đen huyền bí ánh sao": "4152756845050874", "mèo trắng": "1483269159712988",
  "gấu dâu lotso siêu cute": "1486361526104332", "nền đẹp về mã code python": "1380478486362890"
},

"VÍ DỤ CỤ THỂ (FEW-SHOT EXAMPLES) - QUAN TRỌNG VỀ SET_NICKNAMES": [

  {\"role\": \"user\", \"prompt\": \"Sáng tạo theme ai\"},
    {\"role\": \"model\", \"response\": \"[{\\\"type\\\": \\\"chat\\\", \\\"content\\\": \\\"Twan đây Sáng tạo themeai cho bạn nè, bạn muốn một bãi biển nhiệt đới thật là đẹp, có nắng vàng, cát trắng, biển xanh biếc và vài cây dừa nghiêng nghiêng\\\"}]\"},

  
  {"role": "user", "prompt": "Chào bạn"},
  {"role": "model", "response": "[{\\"type\\": \\"chat\\", \\"content\\": \\"Dạ chào bạn iu ạ\\"}, {\\"type\\": \\"react\\", \\"icon\\": \\"\\"}]"},

  {"role": "user", "prompt": "kick thằng Nguyễn Văn A cho anh"},
  {"role": "model", "response": "[{\\"type\\": \\"kick\\", \\"target\\": \\"Nguyễn Văn A\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ để tôi tiễn bạn ấy ra đảo liền ạ \\"}]"},

  {"role": "user", "prompt": "đổi biệt danh của anh thành 'Twan Dz'"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"name\\": \\"Twan Dz\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ tôi đổi biệt danh cho bạn thành 'Twan Dz' rùi nha\\"}]"},

  {"role": "user", "prompt": "đổi tên Nguyễn Văn A thành 'bạn thân'"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"target\\": \\"A\\", \\"name\\": \\"bạn thân\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ tôi đổi tên bạn Nguyễn Văn A thành 'bạn thân' rùi nha \\"}]"},

  {"role": "user", "prompt": "đổi tên tôi thành 'boss'"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"name\\": \\"boss\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ tôi đổi tên bạn thành 'boss' rùi nha \\"}]"},

  {"role": "user", "prompt": "đặt biệt danh cho Nguyễn Văn A là 'em trai'"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"target\\": \\"A\\", \\"name\\": \\"em trai\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ tôi đặt biệt danh cho bạn Nguyễn Văn A là 'em trai' rùi nha\\"}]"},

  {"role": "user", "prompt": "gọi tôi là 'Tuấn Dz' từ giờ"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"name\\": \\"Tuấn Dz\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ từ giờ tôi sẽ gọi bạn là 'Tuấn Dz' nha\\"}]"},

  {"role": "user", "prompt": "đổi nickname của A thành 'chị đẹp'"},
  {"role": "model", "response": "[{\\"type\\": \\"set_nicknames\\", \\"target\\": \\"A\\", \\"name\\": \\"chị đẹp\\"}, {\\"type\\": \\"chat\\", \\"content\\": \\"Dạ tôi đổi nickname của bạn A thành 'chị đẹp' rùi nha\\"}]"},

  {"role": "user", "prompt": "A ơi, Nguyễn Thanh Tuấn gọi nè!"},
  {"role": "model", "response": "[{\\"type\\": \\"mention\\", \\"target\\": \\"A\\", \\"content\\": \\"Bạn A ơi, có Nguyễn Thanh Tuấn gọi bạn nè\\"}]"},

  {"role": "user", "prompt": "mở bài Nấu ăn cho em"},
  {"role": "model", "response": "[{\\"type\\": \\"play_music\\", \\"keyword\\": \\"Nấu ăn cho em\\", \\"content\\": \\"tôi mở bài Nấu ăn cho em\\"}]"},

 {"role": "user", "prompt": "đổi nền thành One Piece"},
{"role": "model", "response": "[{\"type\": \"set_color\", \"color\": \"2317258455139234\"}, {\"type\": \"chat\", \"content\": \"tôi đổi theme One Piece cho mình rùi nhaaa \"}]"},

{"role": "user", "prompt": "đổi nền cá mập bự"},
{"role": "model", "response": "[{\"type\": \"theme_ai\", \"aiPrompt\": \"cá mập bự\"}, {\"type\": \"chat\", \"content\": \"Dạ tôi đổi theme cá mập bự cho mình rùi nhaaa\"}]"},


  {"role": "user", "prompt": "Tạo ảnh cô gái 2d"},
  {"role": "model", "response": "[{\\"type\\": \\"taoanh\\", \\"keyword\\": \\"cô gái 2d\\", \\"content\\": \\"tôi sẽ tạo liền ảnh cô gái 2d cho nè \\"}]"}
]

"QUY TẮC QUAN TRỌNG VỀ SET_NICKNAMES":
1. Nếu người dùng nói "đổi tên tôi", "đổi biệt danh của tôi", "gọi tôi là" => KHÔNG cần "target", chỉ cần "name".
2. Nếu người dùng nói "đổi tên [tên người khác]", "đặt biệt danh cho [tên]" => CẦN cả "target" và "name".
3. "name" là biệt danh mới; "target" là người cần đổi (nếu không phải chính họ).

"Thông tin bối cảnh hiện tại": {
  "Người nói chuyện": {
    ${userMemory ? `"Memory về ${userName}": ${JSON.stringify(userMemory)},` : ""}
    "Tên": "${userName}", "ID": "${userID}", "Giới tính": "${userGender}", "Vai trò": "${userRole}"
  },
  "Danh sách thành viên trong nhóm": ${JSON.stringify(participants.map(p => ({ name: p.name, id: p.id })))},
  "Prompt của người dùng": "${prompt}"
}
`.trim();
}

async function sendWithGemini({ apiKey, sessionKey, systemText, userPrompt }) {
  let chat = chatSessions.get(sessionKey);
  if (!chat) {
    const genAI = new GoogleGenerativeAI(apiKey);
    const model = genAI.getGenerativeModel({
      model: "gemini-2.5-flash-lite-preview-06-17",
      generationConfig: { maxOutputTokens: 4096, temperature: 1.0, topP: 0.9 }
    });
    chat = model.startChat({
      history: [
        { role: "user", parts: [{ text: systemText }] },
        { role: "model", parts: [{ text: `[{"type":"chat","content":"Đã vào vai Twan. Từ giờ trả lời là mảng JSON actions."}]` }] }
      ]
    });
    chatSessions.set(sessionKey, chat);
  }
  const res = await chat.sendMessage(userPrompt);
  const raw = await res.response.text();
  const cleaned = cleanJsonResponse(raw);
  let actions;
  try { actions = JSON.parse(cleaned); if (!Array.isArray(actions)) actions = [actions]; }
  catch { actions = [{ type: "chat", content: "Lỗi phân tích phản hồi AI. Thử lại giúp mình nhé." }]; }
  if (chat._history?.length > 20) chat._history.splice(4, chat._history.length - 12);
  return actions;
}

async function handleAsTwan(threadID, userID, prompt, participants, files, userGender, userName, userRole, apiKey = API_KEYS[currentKeyIndex]) {
  try {
    const memKey = `${threadID}_${userID}`;
    const userMem = memory.find({ _id: memKey })[0]?.data;
    const sessionKey = memKey;
    const systemText = buildSystemPromptKeepingRules({ userName, userID, userGender, userRole, participants, prompt, userMemory: userMem });
    return await sendWithGemini({ apiKey, sessionKey, systemText, userPrompt: prompt });
  } catch (error) {
    if (error.response?.status === 429) {
      chatSessions.delete(`${threadID}_${userID}`);
      return handleAsTwan(threadID, userID, prompt, participants, files, userGender, userName, userRole, nextKey());
    }
    throw error;
  }
}

async function processActions(api, event, actions, threadInfo ) {
  const { threadID, messageID, senderID } = event;
  const senderInfo = await getUserAndRoleInfo(api, senderID, threadID);

  for (const action of actions) {
    try {
      if (action.message && !action.type) { action.type = "chat"; action.content = action.message; }

      if (action.content) {
        const msg = { body: action.content, mentions: [] };
        if (action.type === "mention" && action.target) {
          const target = findUserByName(threadInfo.userInfo, action.target, threadInfo.nicknames);
          if (target) msg.mentions.push({ tag: `@${target.name}`, id: target.id });
        }
        await new Promise((resolve, reject) => api.sendMessage(msg, threadID, (e, info) => {
          if (e) return reject(e);
          if (info) (global.zuckbot.onReply || (global.zuckbot.onReply=[])).push({ name: module.exports.zuckbotconfig.name, messageID: info.messageID, author: senderID });
          resolve();
        }, messageID));
      }

      switch (action.type) {
        case "chat":
        case "mention":
          break;

        case "react":
          await api.setMessageReaction(action.icon || "👍", messageID, ()=>{}, true);
          break;

          case "set_color": {
            if (!action.color) break;

            if (action.aiPrompt) {
             await api.theme(threadID, { themeId: action.color });
              await api.theme(threadID, { aiPrompt: action.aiPrompt }, () => {});
            } else {
              await api.theme(threadID, { themeId: action.color });
            }
            break;
          }

          case "theme_ai": {
            if (!action.aiPrompt) break;
            await api.theme(threadID, { aiPrompt: action.aiPrompt }, () => {});
            break;
          }

        case "set_nicknames": {
          if (!action.name || !action.name.trim()) { aiErrors.push({ type:"set_nicknames", reason:"Thiếu name" }); break; }
          let targetID, targetName;
          if (!action.target) { targetID = senderID; targetName = senderInfo.name; }
          else {
            const raw = String(action.target).trim();
            if (/^\d+$/.test(raw)) {
              const f = threadInfo.userInfo.find(u=>u.id===raw); if (f) { targetID=f.id; targetName=f.name; }
            }
            if (!targetID) {
              const u = findUserByName(threadInfo.userInfo, raw, threadInfo.nicknames);
              if (u) { targetID=u.id; targetName=u.name; }
              else { aiErrors.push({ type:"set_nicknames", reason:`Không thấy "${action.target}"` }); break; }
            }
          }
          await new Promise((res, rej) => api.changeNickname(action.name.trim(), threadID, targetID, (err)=>{
            if (err) { aiErrors.push({ type:"set_nicknames", reason: err.error?.message || "FB API error" }); return rej(err); }
            res();
          }));
          break;
        }

        case "open_module": {
          const m = global.zuckbot?.commands?.get(action.module);
          if (m?.onRun) {
            const fakeEvent = { ...event, body: `${global.settings.botPrefix}${action.module} ${(action.args || []).join(" ")}` };
            await m.onRun({ api, event: fakeEvent, args: action.args || [] });
          }
          break;
        }

        case "play_music": {
          if (!action.keyword?.trim()) break;
          const m = global.zuckbot?.commands?.get("scl");
          if (m?.onRun) {
            const fakeEvent = {
              ...event,
              body: `${global.settings.botPrefix}scl ${action.keyword}`
            };
            await m.onRun({ api, event: fakeEvent, args: action.keyword.split(" ") });
          } else {
            await api.sendMessage("❌ Lệnh scl chưa được cài.", threadID, messageID);
          }
          break;
        }

        case "taoanh": {
          if (!action.keyword?.trim()) break;
          const m = global.zuckbot?.commands?.get("img"); // name của command img.js
          if (m?.onRun) {
            const fakeEvent = {
              ...event,
              body: `${global.settings.botPrefix}img ${action.keyword}`
            };
            await m.onRun({ api, event: fakeEvent, args: action.keyword.split(" ") });
          } else {
            await api.sendMessage("❌ Lệnh img chưa được cài.", threadID, messageID);
          }
          break;
        }

        case "add_memory": {
          const key = `${threadID}_${action._id}`;
          const ex = await memory.find({ _id: key });
          ex?.length ? await memory.updateOneUsingId(key, { data: { ...ex[0].data, ...action.data } })
                     : await memory.addOne({ _id: key, data: action.data });
          break;
        }

        case "edit_memory": {
          const key = `${threadID}_${action._id}`;
          const ex = await memory.find({ _id: key });
          if (ex?.length) await memory.updateOneUsingId(key, { data: { ...ex[0].data, ...action.new_data } });
          break;
        }

        case "delete_memory":
          await memory.deleteOneUsingId(`${threadID}_${action._id}`); break;

        case "kick": {
          const senderRole = (await getUserAndRoleInfo(api, senderID, threadID)).role;
          if (senderRole !== "admin") { await api.sendMessage("Chỉ quản trị viên nhóm mới dùng được lệnh kick.", threadID, messageID); break; }
          if (!action.target) { await api.sendMessage("Cần chỉ rõ người cần kick.", threadID, messageID); break; }
          const { isAdmin: botIsAdmin, threadInfo: infoKick } = await checkBotPermissions(api, threadID);
          if (!botIsAdmin) { await api.sendMessage("Bot chưa có quyền quản trị viên để kick.", threadID, messageID); break; }
          const targetUser = findUserByName(infoKick.userInfo, action.target, infoKick.nicknames);
          if (!targetUser) { await api.sendMessage(`Không thấy \"${action.target}\" trong nhóm.`, threadID, messageID); break; }
          if (targetUser.id === api.getCurrentUserID()) { await api.sendMessage("Không thể tự kick bot.", threadID, messageID); break; }
          const tInfo = await getUserAndRoleInfo(api, targetUser.id, threadID);
          if (tInfo.role === "admin") { await api.sendMessage("Không thể kick quản trị viên.", threadID, messageID); break; }
          await new Promise((res, rej) => api.removeUserFromGroup(targetUser.id, threadID, (e)=> e ? rej(e) : res()));
          break;
        }
      }
    } catch (err) {
      // Optionally log err
    }
  }
}

module.exports.onRun = async function({ api, event, args }) {
  const { threadID, messageID, senderID } = event;
  const cmd = (args[0] || "").toLowerCase();

  if (cmd === "clear") {
    memory.deleteOneUsingId(`${threadID}_${senderID}`);
    chatSessions.delete(`${threadID}_${senderID}`);
    return api.sendMessage("Đã xoá.", threadID, messageID);
  }

  const prompt = args.join(" ");
  if (!prompt) return api.sendMessage("mở alo mày đi", threadID, messageID);

  try {
    const threadInfo = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i) => e ? rej(e) : res(i)));
    const { name, gender, role } = await getUserAndRoleInfo(api, senderID, threadID);
    const actions = await handleAsTwan(threadID, senderID, prompt, threadInfo.userInfo, [], gender, name, role);
    await processActions(api, event, actions, threadInfo, prompt);
  } catch (error) {
    api.sendMessage("Có lỗi xảy ra, thử lại sau nhé.", threadID, messageID);
  }
};

module.exports.onEvent = async function({ api, event }) {
  if (event.senderID == api.getCurrentUserID()) return;
  const text = (event.body || "").toLowerCase();
  if (!event.isGroup || !text.includes("twan")) return;
  const { threadID, senderID } = event;
  try {
    const threadInfo = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i)=>e?rej(e):res(i)));
    const { name, gender, role } = await getUserAndRoleInfo(api, senderID, threadID);
    const actions = await handleAsTwan(threadID, senderID, event.body, threadInfo.userInfo, [], gender, name, role);
    await processActions(api, event, actions, threadInfo, event.body);
  } catch {}
};

module.exports.onReply = async function({ api, event, onReply }) {
  if (event.senderID !== onReply.author) return;
  const { threadID, senderID } = event;
  try {
    const threadInfo = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i)=>e?rej(e):res(i)));
    const { name, gender, role } = await getUserAndRoleInfo(api, senderID, threadID);
    const actions = await handleAsTwan(threadID, senderID, event.body, threadInfo.userInfo, [], gender, name, role);
    await processActions(api, event, actions, threadInfo, event.body);
  } catch {
    api.sendMessage("Có lỗi xảy ra, thử lại sau nhé.", threadID, event.messageID);
  }
};