#!/bin/bash
# DeepSeek Key Tool (Mac) — key DeepSeek → Claude Desktop + Claude Code CLI / extension.
# Double-click hoặc: bash DeepSeekKeyTool.command sk-...   |   bash DeepSeekKeyTool.command --reset
set -euo pipefail
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

VERSION="1.0.0"
CONFIG_ID="d5c6668f-60a6-4c38-a4b2-5185021d13ea"
CONFIG_NAME="Nghimmo DeepSeek"
GATEWAY="https://api.nghimmo.com"
MODEL="nghi/deepseek-v4.1-flash"
MODEL2="nghi/glm-5.3-flash"
LIB="$HOME/Library/Application Support/Claude-3p/configLibrary"
MARKER="$HOME/Library/Application Support/Claude-3p/.deepseek-key-tool.json"
SETTINGS="$HOME/.claude/settings.json"
CLAUDE_JSON="$HOME/.claude.json"
LOG="$HOME/Library/Logs/deepseek-key-tool.log"
ENV_KEYS="ANTHROPIC_AUTH_TOKEN ANTHROPIC_API_KEY ANTHROPIC_BASE_URL ANTHROPIC_MODEL ANTHROPIC_DEFAULT_OPUS_MODEL ANTHROPIC_DEFAULT_SONNET_MODEL ANTHROPIC_DEFAULT_HAIKU_MODEL ANTHROPIC_SMALL_FAST_MODEL CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY CLAUDE_CODE_USE_GATEWAY CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK API_TIMEOUT_MS"

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

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

alert() {
  say "$1"
  osascript -e "display alert \"DeepSeek 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
}

# Hoi /v1/models bang key. 0 = key DeepSeek (hoac khong kiem tra duoc), 3 = khong phai DeepSeek, 4 = key sai/het han.
check_key() {
  command -v python3 >/dev/null 2>&1 || return 0
  local rc
  set +e
  GATEWAY="$GATEWAY" KEY="$1" python3 - <<'PY'
import json, os, sys, urllib.request, urllib.error
req = urllib.request.Request(
    os.environ["GATEWAY"].rstrip("/") + "/v1/models",
    headers={"x-api-key": os.environ["KEY"], "user-agent": "DeepSeekKeyTool/1.0"},
)
try:
    with urllib.request.urlopen(req, timeout=15) as r:
        data = json.loads(r.read().decode())
except urllib.error.HTTPError as e:
    try:
        body = json.loads(e.read().decode())
    except Exception:
        body = None
    sys.exit(4 if e.code in (401, 403) and isinstance(body, dict) and "error" in body else 0)
except Exception:
    sys.exit(0)
ids = [str(m.get("id", "")) for m in (data.get("data") or []) if isinstance(m, dict)]
sys.exit(0 if any("deepseek" in i for i in ids) else 3)
PY
  rc=$?
  set -e
  return $rc
}

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" \
              "$HOME/Library/Application Support/Antigravity" \
              "$HOME/Library/Application Support/VSCodium" \
              "$HOME/Library/Application Support/Windsurf" \
              "$HOME/Library/Application Support/Trae"
  do
    if [ -d "$base/$rel" ]; then
      rm -rf "$base/$rel"
      log "cleared extension cache: $base/$rel"
    fi
  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" CONFIG_NAME="$CONFIG_NAME" GATEWAY="$GATEWAY" KEY="$key" MODEL="$MODEL" MODEL2="$MODEL2" 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"]
cfg = {
    "inferenceProvider": "gateway",
    "inferenceGatewayBaseUrl": gw,
    "inferenceGatewayApiKey": key,
    "inferenceGatewayAuthScheme": "x-api-key",
    "inferenceCredentialKind": "static",
    "modelDiscoveryEnabled": True,
    "inferenceModels": [{"name": os.environ["MODEL"]}, {"name": os.environ["MODEL2"]}],
    "chatTabEnabled": True,
    "coworkTabEnabled": True,
    "isClaudeCodeForDesktopEnabled": True,
}
meta = {"appliedId": cid, "entries": [{"id": cid, "name": os.environ["CONFIG_NAME"]}]}
(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": os.environ["VERSION"], "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": "$MODEL"},
    {"name": "$MODEL2"}
  ],
  "chatTabEnabled": true,
  "coworkTabEnabled": true,
  "isClaudeCodeForDesktopEnabled": true
}
EOF
    cat >"$LIB/_meta.json" <<EOF
{
  "appliedId": "$CONFIG_ID",
  "entries": [
    {
      "id": "$CONFIG_ID",
      "name": "$CONFIG_NAME"
    }
  ]
}
EOF
    printf '{\n  "version": "%s",\n  "key": "%s",\n  "gateway": "%s"\n}\n' "$VERSION" "$esc" "$GATEWAY" >"$MARKER"
  fi
}

write_claude_code() {
  local key="$1"
  mkdir -p "$(dirname "$SETTINGS")"
  clear_extension_cache
  if command -v python3 >/dev/null 2>&1; then
    SETTINGS="$SETTINGS" CLAUDE_JSON="$CLAUDE_JSON" GATEWAY="$GATEWAY" KEY="$key" MODEL="$MODEL" 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 = {}
model = os.environ["MODEL"]
env["ANTHROPIC_BASE_URL"] = os.environ["GATEWAY"]
env["ANTHROPIC_AUTH_TOKEN"] = os.environ["KEY"]
for k in ("ANTHROPIC_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL",
          "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_SMALL_FAST_MODEL"):
    env[k] = model
env["CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"] = "1"
env["CLAUDE_CODE_USE_GATEWAY"] = "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": "$MODEL",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "$MODEL",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "$MODEL",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "$MODEL",
    "ANTHROPIC_SMALL_FAST_MODEL": "$MODEL",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1",
    "CLAUDE_CODE_USE_GATEWAY": "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" ] && grep -q "$CONFIG_ID" "$LIB/_meta.json" 2>/dev/null; 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" ENV_KEYS="$ENV_KEYS" 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 os.environ["ENV_KEYS"].split():
            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 DeepSeek"
  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 DeepSeek", "Reset cau hinh", "Thoat"}
set picked to choose from list choices with prompt "DeepSeek Key Tool — chon thao tac:" default items {"Ap dung key DeepSeek"}
if picked is false then return "exit"
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 DeepSeek (sk-...):" default answer "" with hidden answer buttons {"Huy", "OK"} default button "OK" with title "DeepSeek 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 DeepSeek (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
  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
  sleep 2
}

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" ]
}

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

usage() {
  cat <<EOF
DeepSeek Key Tool v$VERSION — chỉ dùng key DeepSeek.

Cách dùng:
  Double-click file .command
  bash "$0" sk-...        # áp dụng cho Claude Desktop + Claude Code
  bash "$0" --reset       # gỡ cấu hình

Model: $MODEL (mặc định), $MODEL2
Log: $LOG
EOF
}

# --- tham so ---
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 || shift ;;
    --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

say "=== DeepSeek Key Tool v$VERSION (user=$USER) ==="

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

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

key="$(ask_key "$KEY_ARG")"
key="$(printf '%s' "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$key" ] || [ "${#key}" -lt 8 ] || [[ "$key" == *YOUR_API_KEY* ]]; then
  alert "Key không hợp lệ. Kiểm tra lại." "warning"
  pause_if_needed
  exit 1
fi

say "Đang kiểm tra key..."
rc=0
check_key "$key" || rc=$?
if [ "$rc" = "3" ]; then
  alert "Key này không phải key DeepSeek.\n\nKey Claude dùng ClaudeKeyTool; key DeepSeek mới dùng tool này." "warning"
  pause_if_needed
  exit 1
fi
if [ "$rc" = "4" ]; then
  alert "Key sai, bị khoá hoặc đã hết hạn.\n\nKiểm tra tại https://api.nghimmo.com/check" "warning"
  pause_if_needed
  exit 1
fi

# 1) Claude Code CLI / extension
write_claude_code "$key"
say "Đã ghi ~/.claude/settings.json (model $MODEL)"

# 2) Claude Desktop (neu co cai)
desktop_msg=""
if ! check_claude_app; then
  desktop_msg="Chưa cài Claude Desktop — mới cấu hình Claude Code."
elif check_managed_profile; then
  desktop_msg="Máy có profile MDM quản lý Claude Desktop — bỏ qua Desktop."
else
  quit_claude
  enable_dev_mode
  write_desktop_config "$key"
  if [ -f "$LIB/$CONFIG_ID.json" ] && [ -f "$LIB/_meta.json" ]; then
    open_claude || true
    desktop_msg="Claude Desktop: chọn Continue with Gateway (không đăng nhập claude.ai)."
  else
    desktop_msg="Không ghi được cấu hình Claude Desktop."
  fi
fi

alert "Xong! Model: $MODEL\n\n$desktop_msg\nClaude Code: chạy lại claude. VS Code/Cursor/Antigravity: Reload Window." "informational"
log "=== done ==="
pause_if_needed
