#!/bin/bash
# Claude Key Tool (Mac) — double-click hoặc: bash ClaudeKeyTool.command sk-...
set -euo pipefail
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

VERSION="1.2.4"
CONFIG_ID="7c4a2f91-3b8e-4d6a-9f21-a1b2c3d4e5f6"
GATEWAY="https://api.nghimmo.com"
LIB="$HOME/Library/Application Support/Claude-3p/configLibrary"
MARKER="$HOME/Library/Application Support/Claude-3p/.claude-key-tool.json"
SETTINGS="$HOME/.claude/settings.json"
CLAUDE_JSON="$HOME/.claude.json"
LOG="$HOME/Library/Logs/claude-key-tool.log"

log() {
  mkdir -p "$(dirname "$LOG")"
  printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >>"$LOG"
}

say() {
  echo "[Claude Key Tool] $*" >&2
  log "$*"
}

alert() {
  say "$1"
  osascript -e "display alert \"Claude Key Tool\" message \"$1\" as ${2:-informational}" >/dev/null 2>&1 || true
}

gui_activate() {
  osascript -e 'tell application "Terminal" to activate' >/dev/null 2>&1 || true
}

pause_if_needed() {
  if [ -t 0 ] && [ -r /dev/tty ]; then
    read -r -p "Nhấn Enter để đóng..." _ </dev/tty 2>/dev/null || true
  fi
}

confirm() {
  local msg="$1"
  gui_activate
  if osascript -e "display dialog \"$msg\" buttons {\"Huỷ\", \"OK\"} default button \"OK\" with icon caution" 2>/dev/null | grep -q "OK"; then
    return 0
  fi
  read -r -p "$msg [y/N]: " ans </dev/tty 2>/dev/null || ans=""
  case "${ans:-N}" in
    y|Y|yes|Yes) return 0 ;;
    *) return 1 ;;
  esac
}

enable_dev_mode() {
  local body='{
  "allowDevTools": true
}'
  for p in \
    "$HOME/Library/Application Support/Claude/developer_settings.json" \
    "$HOME/Library/Application Support/Claude-3p/developer_settings.json"
  do
    mkdir -p "$(dirname "$p")"
    printf '%s\n' "$body" >"$p"
  done
}

clear_extension_cache() {
  local rel="User/globalStorage/anthropic.claude-code"
  for base in "$HOME/Library/Application Support/Code" \
              "$HOME/Library/Application Support/Cursor" \
              "$HOME/Library/Application Support/Code - Insiders"
  do
    [ -d "$base/$rel" ] && rm -rf "$base/$rel"
  done
}

clear_claude_session() {
  local base rel
  for base in \
    "$HOME/Library/Application Support/Claude-3p" \
    "$HOME/Library/Application Support/Claude"
  do
    [ -d "$base" ] || continue
    for rel in Cookies "Local Storage" "Session Storage" Cache "Code Cache"; do
      if [ -e "$base/$rel" ]; then
        rm -rf "$base/$rel"
        log "cleared session: $base/$rel"
      fi
    done
  done
}

write_desktop_config() {
  local key="$1"
  mkdir -p "$LIB" "$(dirname "$MARKER")"
  if command -v python3 >/dev/null 2>&1; then
    LIB="$LIB" MARKER="$MARKER" CONFIG_ID="$CONFIG_ID" GATEWAY="$GATEWAY" KEY="$key" VERSION="$VERSION" python3 <<'PY'
import json, os
from pathlib import Path
lib = Path(os.environ["LIB"])
cid = os.environ["CONFIG_ID"]
key = os.environ["KEY"]
gw = os.environ["GATEWAY"]
ver = os.environ["VERSION"]
cfg = {
    "inferenceProvider": "gateway",
    "inferenceGatewayBaseUrl": gw,
    "inferenceGatewayApiKey": key,
    "inferenceGatewayAuthScheme": "x-api-key",
    "inferenceCredentialKind": "static",
    "modelDiscoveryEnabled": True,
    "inferenceModels": [
        {"name": "nghi/claude-opus-5[1m]"},
        {"name": "nghi/claude-opus-5[1m]-thinking"},
        {"name": "nghi/claude-opus-4.8[1m]"},
        {"name": "nghi/claude-opus-4.8[1m]-thinking"},
        {"name": "nghi/claude-opus-4.7[1m]"},
        {"name": "nghi/claude-opus-4.7[1m]-thinking"},
        {"name": "nghi/claude-opus-4.6[1m]"},
        {"name": "nghi/claude-opus-4.6[1m]-thinking"},
        {"name": "nghi/claude-opus-4.5[1m]"},
        {"name": "nghi/claude-opus-4.5[1m]-thinking"},
        {"name": "nghi/claude-sonnet-5[1m]"},
        {"name": "nghi/claude-sonnet-4.6[1m]"},
        {"name": "nghi/claude-sonnet-4.6[1m]-thinking"},
        {"name": "nghi/claude-sonnet-4.5[1m]"},
        {"name": "nghi/claude-sonnet-4.5[1m]-thinking"},
        {"name": "nghi/claude-sonnet-4[1m]"},
        {"name": "nghi/claude-sonnet-4[1m]-thinking"},
        {"name": "nghi/claude-haiku-4.5[1m]"},
        {"name": "nghi/claude-haiku-4.5[1m]-thinking"},
        {"name": "nghi/auto[1m]"},
    ],
    "chatTabEnabled": True,
    "coworkTabEnabled": True,
    "isClaudeCodeForDesktopEnabled": True,
}
meta = {"appliedId": cid, "entries": [{"id": cid, "name": "Claude Key Tool"}]}
(lib / f"{cid}.json").write_text(json.dumps(cfg, indent=2) + "\n", encoding="utf-8")
(lib / "_meta.json").write_text(json.dumps(meta, indent=2) + "\n", encoding="utf-8")
Path(os.environ["MARKER"]).write_text(json.dumps({"version": ver, "key": key, "gateway": gw}, indent=2) + "\n", encoding="utf-8")
PY
  else
    local esc
    esc=$(printf '%s' "$key" | sed 's/\\/\\\\/g; s/"/\\"/g')
    cat >"$LIB/$CONFIG_ID.json" <<EOF
{
  "inferenceProvider": "gateway",
  "inferenceGatewayBaseUrl": "$GATEWAY",
  "inferenceGatewayApiKey": "$esc",
  "inferenceGatewayAuthScheme": "x-api-key",
  "inferenceCredentialKind": "static",
  "modelDiscoveryEnabled": true,
  "inferenceModels": [
    {"name": "nghi/claude-opus-5[1m]"},
    {"name": "nghi/claude-opus-5[1m]-thinking"},
    {"name": "nghi/claude-opus-4.8[1m]"},
    {"name": "nghi/claude-opus-4.8[1m]-thinking"},
    {"name": "nghi/claude-opus-4.7[1m]"},
    {"name": "nghi/claude-opus-4.7[1m]-thinking"},
    {"name": "nghi/claude-opus-4.6[1m]"},
    {"name": "nghi/claude-opus-4.6[1m]-thinking"},
    {"name": "nghi/claude-opus-4.5[1m]"},
    {"name": "nghi/claude-opus-4.5[1m]-thinking"},
    {"name": "nghi/claude-sonnet-5[1m]"},
    {"name": "nghi/claude-sonnet-4.6[1m]"},
    {"name": "nghi/claude-sonnet-4.6[1m]-thinking"},
    {"name": "nghi/claude-sonnet-4.5[1m]"},
    {"name": "nghi/claude-sonnet-4.5[1m]-thinking"},
    {"name": "nghi/claude-sonnet-4[1m]"},
    {"name": "nghi/claude-sonnet-4[1m]-thinking"},
    {"name": "nghi/claude-haiku-4.5[1m]"},
    {"name": "nghi/claude-haiku-4.5[1m]-thinking"},
    {"name": "nghi/auto[1m]"}
  ],
  "chatTabEnabled": true,
  "coworkTabEnabled": true,
  "isClaudeCodeForDesktopEnabled": true
}
EOF
    cat >"$LIB/_meta.json" <<EOF
{
  "appliedId": "$CONFIG_ID",
  "entries": [
    {
      "id": "$CONFIG_ID",
      "name": "Claude Key Tool"
    }
  ]
}
EOF
    printf '{\n  "version": "%s",\n  "key": "%s",\n  "gateway": "%s"\n}\n' "$VERSION" "$esc" "$GATEWAY" >"$MARKER"
  fi
}

write_claude_code() {
  local key="$1"
  local clear_cache="${2:-1}"
  mkdir -p "$(dirname "$SETTINGS")"
  if [ "$clear_cache" = "1" ]; then
    clear_extension_cache
  fi
  if command -v python3 >/dev/null 2>&1; then
    SETTINGS="$SETTINGS" CLAUDE_JSON="$CLAUDE_JSON" GATEWAY="$GATEWAY" KEY="$key" python3 <<'PY'
import json, os
from pathlib import Path
sf = Path(os.environ["SETTINGS"])
data = {}
if sf.exists():
    try:
        data = json.loads(sf.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        data = {}
if not isinstance(data, dict):
    data = {}
env = data.get("env")
if not isinstance(env, dict):
    env = {}
env["ANTHROPIC_BASE_URL"] = os.environ["GATEWAY"]
env["ANTHROPIC_AUTH_TOKEN"] = os.environ["KEY"]
env["ANTHROPIC_MODEL"] = "nghi/claude-sonnet-5[1m]"
env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "nghi/claude-opus-5[1m]"
env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = "nghi/claude-sonnet-5[1m]"
env["ANTHROPIC_SMALL_FAST_MODEL"] = "nghi/claude-haiku-4.5[1m]"
env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "nghi/claude-haiku-4.5[1m]"
env["CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"] = "1"
env["CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK"] = "1"
env["API_TIMEOUT_MS"] = "600000"
env.pop("ANTHROPIC_API_KEY", None)
data["env"] = env
sf.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
cj = Path(os.environ["CLAUDE_JSON"])
if cj.exists():
    try:
        cdata = json.loads(cj.read_text(encoding="utf-8"))
        if isinstance(cdata, dict):
            for k in ("primaryApiKey", "customApiKeyResponses"):
                cdata.pop(k, None)
            cj.write_text(json.dumps(cdata, indent=2) + "\n", encoding="utf-8")
    except json.JSONDecodeError:
        pass
PY
  else
    cat >"$SETTINGS" <<EOF
{
  "env": {
    "ANTHROPIC_BASE_URL": "$GATEWAY",
    "ANTHROPIC_AUTH_TOKEN": "$key",
    "ANTHROPIC_MODEL": "nghi/claude-sonnet-5[1m]",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "nghi/claude-opus-5[1m]",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "nghi/claude-sonnet-5[1m]",
    "ANTHROPIC_SMALL_FAST_MODEL": "nghi/claude-haiku-4.5[1m]",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "nghi/claude-haiku-4.5[1m]",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1",
    "CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK": "1",
    "API_TIMEOUT_MS": "600000"
  }
}
EOF
  fi
}

reset_all() {
  rm -f "$LIB/$CONFIG_ID.json" "$MARKER"
  if [ -f "$LIB/_meta.json" ]; then
    printf '{\n  "appliedId": "",\n  "entries": []\n}\n' >"$LIB/_meta.json"
  fi
  clear_extension_cache
  if command -v python3 >/dev/null 2>&1 && [ -f "$SETTINGS" ]; then
    SETTINGS="$SETTINGS" CLAUDE_JSON="$CLAUDE_JSON" python3 <<'PY'
import json, os
from pathlib import Path
for path_key in ("SETTINGS", "CLAUDE_JSON"):
    p = Path(os.environ[path_key])
    if not p.exists():
        continue
    try:
        data = json.loads(p.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        continue
    if path_key == "SETTINGS" and isinstance(data.get("env"), dict):
        for k in ("ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_SMALL_FAST_MODEL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY", "CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK", "API_TIMEOUT_MS"):
            data["env"].pop(k, None)
    if path_key == "CLAUDE_JSON" and isinstance(data, dict):
        for k in ("primaryApiKey", "customApiKeyResponses"):
            data.pop(k, None)
    p.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
PY
  fi
}

pick_action_terminal() {
  echo ""
  echo "  1) Áp dụng key"
  echo "  2) Reset cấu hình"
  echo "  3) Thoát"
  read -r -p "Chọn [1]: " choice </dev/tty 2>/dev/null || choice="1"
  case "${choice:-1}" in
    2) printf 'reset' ;;
    3) printf 'exit' ;;
    *) printf 'apply' ;;
  esac
}

pick_action() {
  gui_activate
  local out
  out="$(osascript <<'APPLE' 2>/dev/null || true
set choices to {"Ap dung key", "Reset cau hinh", "Thoat"}
set picked to choose from list choices with prompt "Claude Key Tool — chon thao tac:" default items {"Ap dung key"}
if picked is false then return ""
if item 1 of picked is "Reset cau hinh" then return "reset"
if item 1 of picked is "Thoat" then return "exit"
return "apply"
APPLE
)"
  out="$(printf '%s' "$out" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
  case "$out" in
    apply|reset|exit) printf '%s' "$out" ;;
    *)
      say "Không mở được hộp thoại — chọn trong Terminal:"
      pick_action_terminal
      ;;
  esac
}

ask_key() {
  local preset="${1:-}"
  if [ -n "$preset" ]; then
    printf '%s' "$preset"
    return
  fi
  gui_activate
  local out
  out="$(osascript <<'APPLE' 2>/dev/null || true
set dlg to display dialog "Dan API key Claude (sk-...):" default answer "" with hidden answer buttons {"Huy", "OK"} default button "OK" with title "Claude Key Tool"
return text returned of dlg
APPLE
)"
  out="$(printf '%s' "$out" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
  if [ -n "$out" ]; then
    printf '%s' "$out"
    return
  fi
  say "Nhập key trong Terminal:"
  read -r -s -p "API key (sk-...): " out </dev/tty 2>/dev/null || out=""
  echo >&2
  printf '%s' "$out"
}

claude_running() {
  pgrep -x Claude >/dev/null 2>&1
}

quit_claude() {
  local i
  if claude_running; then
    log "quit: Claude running before quit"
  fi
  osascript -e 'tell application "Claude" to quit' >/dev/null 2>&1 || true
  sleep 1
  killall Claude >/dev/null 2>&1 || true
  pkill -x Claude >/dev/null 2>&1 || true
  for i in $(seq 1 20); do
    claude_running || break
    sleep 0.5
  done
  if claude_running; then
    log "warn: Claude still running after quit attempts"
  else
    log "quit: Claude stopped"
  fi
  sleep 3
}

verify_desktop_config() {
  [ -f "$LIB/$CONFIG_ID.json" ] && [ -f "$LIB/_meta.json" ] && [ -f "$MARKER" ]
}

is_gateway_configured() {
  [ -f "$LIB/$CONFIG_ID.json" ] && grep -q '"inferenceProvider"[[:space:]]*:[[:space:]]*"gateway"' "$LIB/$CONFIG_ID.json" 2>/dev/null
}

check_managed_profile() {
  [ -f "/Library/Managed Preferences/com.anthropic.claudefordesktop.plist" ]
}

open_claude() {
  if [ -d "/Applications/Claude.app" ]; then
    open -n -a "Claude"
    return 0
  fi
  if [ -d "$HOME/Applications/Claude.app" ]; then
    open -n -a "$HOME/Applications/Claude.app"
    return 0
  fi
  return 1
}

check_claude_app() {
  [ -d "/Applications/Claude.app" ] || [ -d "$HOME/Applications/Claude.app" ]
}

usage() {
  cat <<EOF
Claude Key Tool v$VERSION

Cách dùng:
  Double-click file .command
  bash "$0" sk-...
  bash "$0" --reset

Log: $LOG
EOF
}

# --- CLI ---
KEY_ARG=""
ACTION_ARG=""
while [ $# -gt 0 ]; do
  case "$1" in
    --reset|-r) ACTION_ARG=reset; shift ;;
    --apply|-a)
      ACTION_ARG=apply
      KEY_ARG="${2:-}"
      shift 2
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    sk-*)
      ACTION_ARG=apply
      KEY_ARG="$1"
      shift
      ;;
    *)
      say "Bỏ qua tham số không rõ: $1"
      shift
      ;;
  esac
done

# --- main ---
say "=== Claude Key Tool v$VERSION (user=$USER) ==="
log "=== start pid $$ home=$HOME ==="

if [ -n "$ACTION_ARG" ]; then
  action="$ACTION_ARG"
else
  action="$(pick_action)"
fi

case "$action" in
  exit)
    say "Đã huỷ."
    pause_if_needed
    exit 0
    ;;
  reset)
    if confirm "Xóa cấu hình Claude Desktop + Claude Code trên máy này?"; then
      reset_all
      alert "Đã reset. Mở lại Claude Desktop / reload VS Code nếu đang mở." "informational"
    else
      say "Reset đã huỷ."
    fi
    pause_if_needed
    exit 0
    ;;
  apply|*) ;;
esac

key="$(ask_key "$KEY_ARG")"
key="$(printf '%s' "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"

if [ -z "$key" ]; then
  say "Chưa nhập key."
  alert "Chưa nhập key." "warning"
  pause_if_needed
  exit 1
fi
if [ "${#key}" -lt 8 ] || [[ "$key" == *YOUR_API_KEY* ]]; then
  say "Key không hợp lệ."
  alert "Key không hợp lệ. Kiểm tra lại." "warning"
  pause_if_needed
  exit 1
fi

if ! check_claude_app; then
  say "Chưa cài Claude Desktop (.dmg từ claude.com/download)."
  alert "Chưa cài Claude Desktop.\n\nTải từ claude.com/download (file .dmg, không dùng App Store) rồi chạy lại tool." "warning"
  pause_if_needed
  exit 1
fi

if check_managed_profile; then
  say "Máy có profile MDM quản lý Claude Desktop."
  alert "Máy có profile MDM quản lý Claude Desktop — configLibrary bị bỏ qua. Cần admin gỡ profile hoặc cấu hình gateway qua MDM." "warning"
  pause_if_needed
  exit 1
fi

say "Đang áp dụng key..."
quit_claude
already=0
if is_gateway_configured; then
  already=1
  say "Gateway đã cấu hình — giữ lịch sử chat."
else
  say "Lần đầu — xóa session claude.ai cũ."
  clear_claude_session
fi
enable_dev_mode
write_desktop_config "$key"
write_claude_code "$key" "$((1 - already))"

if ! verify_desktop_config; then
  say "Không ghi được config vào $LIB"
  alert "Không ghi được cấu hình Claude Desktop.\n\nChạy lại từ Terminal:\nbash \"$0\"\n\nXem log: $LOG\nClaude log: ~/Library/Logs/Claude-3p/main.log" "warning"
  pause_if_needed
  exit 1
fi

if [ -f "$LIB/_meta.json" ]; then
  log "meta: $(tr -d '\n' <"$LIB/_meta.json" | head -c 200)"
fi

say "Đã ghi config: $LIB/$CONFIG_ID.json"
if open_claude; then
  if [ "$already" -eq 1 ]; then
    msg="Xong! Đã đổi key — lịch sử chat được giữ nguyên.\n\nClaude đã mở lại. Nếu không thấy Gateway: Cmd+Q thoát hẳn → mở lại.\n\nVS Code/Cursor: Cmd+Shift+P → Reload Window."
  else
    msg="Xong! Claude đã được mở lại (cold launch).\n\nTrên màn hình đăng nhập chọn Continue with Gateway — KHÔNG đăng nhập claude.ai.\n\nNếu vẫn thấy màn claude.ai: Cmd+Q thoát hẳn → chạy lại tool từ Terminal:\nbash \"$0\"\n\nVS Code/Cursor: Cmd+Shift+P → Reload Window."
  fi
  alert "$msg" "informational"
else
  alert "Xong! Cài Claude Desktop từ claude.com/download (không dùng App Store) rồi mở app → Continue with Gateway." "informational"
fi
log "=== done ==="
pause_if_needed
