"use strict";
// 把 Claude Pet 的 hook 合併進 ~/.claude/settings.json（保留使用者原本的設定與其他 hooks）
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { unpacked, isPortable } = require("./paths");

const APP_DIR = path.resolve(__dirname, "..");
const CLAUDE_DIR = path.join(os.homedir(), ".claude");
const SETTINGS_PATH = path.join(CLAUDE_DIR, "settings.json");
const BACKUP_DIR = path.join(CLAUDE_DIR, "backups");
// node 執行不了 asar 內的檔案，打包後要指到解開的那份
const BUNDLED_HOOK_SCRIPT = unpacked(path.join(APP_DIR, "hook", "claude-pet-hook.js"));
// portable 版整包解在 %TEMP%、關掉程式就被刪，hook 路徑不能指在那裡（寵物沒開時
// Claude Code 會找不到檔案而每次噴錯），所以複製一份到不會被清掉的地方。
const STABLE_HOOK_SCRIPT = path.join(os.homedir(), ".claude-pet", "hook", "claude-pet-hook.js");

function hookScript() {
  return isPortable() ? STABLE_HOOK_SCRIPT : BUNDLED_HOOK_SCRIPT;
}

// 只有 portable 需要；每次安裝 hook 都重新複製，版本才不會走鐘
function syncHookScript() {
  if (!isPortable()) return;
  fs.mkdirSync(path.dirname(STABLE_HOOK_SCRIPT), { recursive: true });
  fs.copyFileSync(BUNDLED_HOOK_SCRIPT, STABLE_HOOK_SCRIPT);
}

const MARKER = "claude-pet-hook";

// Claude Code 2.1.x 支援的 hook 事件（對應到寵物狀態）
const EVENTS = [
  "SessionStart",
  "SessionEnd",
  "UserPromptSubmit",
  "PreToolUse",
  "PostToolUse",
  "PostToolUseFailure",
  "PermissionRequest",
  "Notification",
  "Stop",
  "StopFailure",
  "SubagentStart",
  "SubagentStop",
  "PreCompact",
];

function hookCommand() {
  const p = hookScript().replace(/\\/g, "/");
  return /\s/.test(p) ? `node "${p}"` : `node ${p}`;
}

function readSettings() {
  try {
    const text = fs.readFileSync(SETTINGS_PATH, "utf8");
    return text.trim() ? JSON.parse(text) : {};
  } catch (err) {
    if (err.code === "ENOENT") return {};
    throw new Error(`無法解析 ${SETTINGS_PATH}：${err.message}`);
  }
}

function writeSettings(settings) {
  fs.mkdirSync(CLAUDE_DIR, { recursive: true });
  const tmp = `${SETTINGS_PATH}.${process.pid}.tmp`;
  fs.writeFileSync(tmp, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
  fs.renameSync(tmp, SETTINGS_PATH);
}

function backup() {
  if (!fs.existsSync(SETTINGS_PATH)) return null;
  fs.mkdirSync(BACKUP_DIR, { recursive: true });
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
  const dest = path.join(BACKUP_DIR, `settings.claude-pet.${stamp}.json`);
  fs.copyFileSync(SETTINGS_PATH, dest);
  return dest;
}

function isPetHook(entry) {
  try { return JSON.stringify(entry).includes(MARKER); } catch { return false; }
}

function normalize(list) {
  if (Array.isArray(list)) return list;
  if (list && typeof list === "object") return [list];
  return [];
}

function install() {
  syncHookScript();
  const settings = readSettings();
  const backupPath = backup();
  settings.hooks = settings.hooks && typeof settings.hooks === "object" ? settings.hooks : {};
  for (const event of EVENTS) {
    const kept = normalize(settings.hooks[event]).filter((e) => !isPetHook(e));
    kept.push({ hooks: [{ type: "command", command: hookCommand(), timeout: 5 }] });
    settings.hooks[event] = kept;
  }
  writeSettings(settings);
  return { events: EVENTS, settingsPath: SETTINGS_PATH, backupPath, command: hookCommand() };
}

function uninstall() {
  const settings = readSettings();
  if (!settings.hooks || typeof settings.hooks !== "object") return { removed: 0 };
  const backupPath = backup();
  let removed = 0;
  for (const event of Object.keys(settings.hooks)) {
    const before = normalize(settings.hooks[event]);
    const after = before.filter((e) => !isPetHook(e));
    removed += before.length - after.length;
    if (after.length) settings.hooks[event] = after;
    else delete settings.hooks[event];
  }
  if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
  writeSettings(settings);
  return { removed, settingsPath: SETTINGS_PATH, backupPath };
}

function isInstalled() {
  try {
    const settings = readSettings();
    return normalize(settings.hooks?.Stop).some(isPetHook);
  } catch {
    return false;
  }
}

module.exports = { install, uninstall, isInstalled, hookCommand, hookScript, EVENTS, SETTINGS_PATH };
