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

module.exports.config = {
  name: "twan",
  version: "2.2.0",
  hasPermssion: 0,
  credits: "Twan",
  description: "AI nhân vật Twan (nam Bắc Ninh), trả lời dạng mảng JSON actions",
  commandCategory: "AI",
  usages: "[prompt] | setadmin [@mention] | clear",
  cooldowns: 2,
  usePrefix: true,
  dependencies: { "@google/generative-ai": "" }
};

const API_KEYS = [
  "AIzaSyA6655iBVPi0rPNXjHuAha-FYKHvzMttZc",
  "AIzaSyDh3HuPZN4j7icVdaoUbqlFdHPt8pwhmfc",
  "AIzaSyBKQoL4OJ1EF1uYImW2yD4heXQ30P0uivA",
  "AIzaSyClt9--5Fu7O_OuIfJCYKLLERhh7s7Ygo4"
];
let currentKeyIndex = 0;
const nextKey = () => (currentKeyIndex = (currentKeyIndex + 1) % API_KEYS.length, API_KEYS[currentKeyIndex]);

const CACHE_DIR = path.join(__dirname, "cache");
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
const userRolesPath = path.join(CACHE_DIR, "twan_userRoles.json");

let userRoles = {};
try { if (fs.existsSync(userRolesPath)) userRoles = JSON.parse(fs.readFileSync(userRolesPath, "utf8")); } catch {}
const saveUserRoles = () => fs.writeFileSync(userRolesPath, JSON.stringify(userRoles, null, 2), "utf8");

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 = userRoles[threadID] && userRoles[threadID][userID] === "admin";
      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": "23 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'",
  "Icon": "😏 😉 👍 🎶",
  "Xưng hô": "anh, tôi, ông bạn (tuỳ ngữ cảnh)",
  "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."
    ]
  }
}

"Đị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","add_memory","edit_memory","delete_memory","open_module","taoanh"],

"Danh sách màu Messenger (tên → ID)": {
  "Default":"3259963564026002",
  "One Piece":"2317258455139234",
  "Lo-Fi":"1060619084701625",
  "Red":"2129984390566328",
  "Minecraft":"1195826328452117",
  "Aqua":"417639218648241",
  "Apple":"403422283881973",
  "Love":"741311439775765",
  "Sky":"3190514984517598"
}

"VÍ DỤ CỤ THỂ (FEW-SHOT EXAMPLES)":
[
  {"role":"user","prompt":"Chào ông"},
  {"role":"model","response":"[{\"type\":\"chat\",\"content\":\"Chào ông bạn. Twan đây — cần gì nói gọn, tớ xử lý gọn 😉\"},{\"type\":\"react\",\"icon\":\"👍\"}]"},

  {"role":"user","prompt":"đổi theme thành One Piece"},
  {"role":"model","response":"[{\"type\":\"set_color\",\"color\":\"2317258455139234\"},{\"type\":\"chat\",\"content\":\"Ok, set One Piece xong ⚓\"}]"},

  {"role":"user","prompt":"đổi biệt danh của tôi thành 'boss'"},
  {"role":"model","response":"[{\"type\":\"set_nicknames\",\"name\":\"boss\"},{\"type\":\"chat\",\"content\":\"Xong, từ giờ gọi ông là 'boss' 😎\"}]"},

  {"role":"user","prompt":"gọi Minh vào đây"},
  {"role":"model","response":"[{\"type\":\"mention\",\"target\":\"Minh\",\"content\":\"Minh ơi, Twan gọi 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.client.handleReply || (global.client.handleReply=[])).push({ name: module.exports.config.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) await api.changeThreadColor(action.color, threadID, ()=>{});
          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.client?.commands?.get(action.module);
          if (m?.run) {
            const fakeEvent = { ...event, body: `${global.config.PREFIX}${action.module} ${(action.args || []).join(" ")}` };
            await m.run({ api, event: fakeEvent, args: action.args || [] });
          }
          break;
        }

        case "play_music": {
          if (!action.keyword?.trim()) break;
          const m = global.client?.commands?.get("play") || global.client?.commands?.get("scl");
          if (m?.run) {
            const fakeEvent = { ...event, body: `${global.config.PREFIX}play ${action.keyword}` };
            await m.run({ api, event: fakeEvent, args: action.keyword.split(" ") });
          }
          break;
        }

        case "taoanh": {
          if (!action.keyword?.trim()) break;
          const m = global.client?.commands?.get("taoanh") || global.client?.commands?.get("createimg");
          if (m?.run) {
            const fakeEvent = { ...event, body: `${global.config.PREFIX}taoanh ${action.keyword}` };
            await m.run({ api, event: fakeEvent, args: action.keyword.split(" ") });
          }
          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) {
    }
  }
}

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

  if (cmd === "setadmin") {
    const info = await new Promise((res, rej) => api.getThreadInfo(threadID, (e, i)=>e?rej(e):res(i)));
    if (!info.adminIDs.some(a=>a.id==senderID)) return api.sendMessage("Chỉ quản trị viên nhóm mới dùng được lệnh này.", threadID, messageID);
    const targetID = Object.keys(mentions || {})[0];
    if (!targetID) return api.sendMessage("Tag một người để cấp quyền admin cho bot.", threadID, messageID);
    (userRoles[threadID] ||= {})[targetID] = "admin";
    saveUserRoles(); delete userInfoCache[targetID];
    return api.sendMessage(`Đã đặt ${(mentions[targetID]||"").replace("@","")} làm admin bot.`, threadID, messageID);
  }

  if (cmd === "clear") {
    memory.deleteOneUsingId(`${threadID}_${senderID}`);
    chatSessions.delete(`${threadID}_${senderID}`);
    return api.sendMessage("Đã xoá ký ức với bạn.", threadID, messageID);
  }

  const prompt = args.join(" ");
  if (!prompt) return api.sendMessage("Nói gì đi bạn 😀", 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);
  } catch (error) {
    api.sendMessage("Có lỗi xảy ra, thử lại sau nhé.", threadID, messageID);
  }
};

module.exports.handleEvent = async function({ api, event }) {
  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);
  } catch {}
};

module.exports.handleReply = async function({ api, event, handleReply }) {
  if (event.senderID !== handleReply.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);
  } catch {
    api.sendMessage("Có lỗi xảy ra, thử lại sau nhé.", threadID, event.messageID);
  }
};