#!/usr/bin/env bash
# Seshat MCP installer  (Linux / macOS)
#   curl -fsSL http://install.seshat.click/install.sh | bash
#
# Installer chỉ làm: ensure jq -> cài CLI `seshat` -> chèn MCP vào provider (key rỗng).
# Gắn key sau bằng: seshat init "api-key"
#
set -u

MCP_NAME="omSeshat"
DEFAULT_API_BASE_URL="${SESHAT_API_BASE_URL:-http://portal.seshat.click/api}"
DEFAULT_API_BASE_URL="${DEFAULT_API_BASE_URL%/}"
DEFAULT_MCP_URL="${SESHAT_MCP_URL:-$DEFAULT_API_BASE_URL}"
DEFAULT_VALIDATE_URL="${SESHAT_VALIDATE_URL:-$DEFAULT_API_BASE_URL/validate}"
DEFAULT_MCP_URL="${DEFAULT_MCP_URL%/}"
DEFAULT_VALIDATE_URL="${DEFAULT_VALIDATE_URL%/}"
BIN_DIR="${SESHAT_BIN_DIR:-$HOME/.local/bin}"
SESHAT_BIN="$BIN_DIR/seshat"

c_ok="\033[32m"; c_warn="\033[33m"; c_err="\033[31m"; c_dim="\033[2m"; c_off="\033[0m"
say()  { printf "%b\n" "$*"; }
ok()   { printf "  ${c_ok}\xe2\x9c\x93${c_off} %s\n" "$*"; }
warn() { printf "  ${c_warn}!${c_off} %s\n" "$*"; }
die()  { printf "${c_err}x %s${c_off}\n" "$*" >&2; exit 1; }

ensure_jq() {
  command -v jq >/dev/null 2>&1 && return 0
  say "-> Chua co jq, dang thu cai..."
  if   command -v brew    >/dev/null 2>&1; then brew install jq
  elif command -v apt-get >/dev/null 2>&1; then sudo apt-get update -y && sudo apt-get install -y jq
  elif command -v dnf     >/dev/null 2>&1; then sudo dnf install -y jq
  elif command -v yum     >/dev/null 2>&1; then sudo yum install -y jq
  elif command -v pacman  >/dev/null 2>&1; then sudo pacman -Sy --noconfirm jq
  elif command -v apk     >/dev/null 2>&1; then sudo apk add jq
  fi
  command -v jq >/dev/null 2>&1 || die "Can jq nhung cai tu dong khong duoc. Cai tay: https://jqlang.github.io/jq/download/"
}

install_cli() {
  mkdir -p "$BIN_DIR"
  cat > "$SESHAT_BIN" <<'SESHAT_EOF'
#!/usr/bin/env bash
# Seshat MCP CLI - quan ly MCP `omSeshat` tren cac AI provider.
set -u

MCP_NAME="omSeshat"
API_BASE_URL="${SESHAT_API_BASE_URL:-__DEFAULT_API_BASE_URL__}"
API_BASE_URL="${API_BASE_URL%/}"
MCP_URL="${SESHAT_MCP_URL:-__DEFAULT_MCP_URL__}"
MCP_URL="${MCP_URL%/}"
VALIDATE_URL="${SESHAT_VALIDATE_URL:-__DEFAULT_VALIDATE_URL__}"
VALIDATE_URL="${VALIDATE_URL%/}"
SESHAT_HOME="${SESHAT_HOME:-$HOME/.config/seshat}"
CRED_FILE="$SESHAT_HOME/credentials"

c_ok="\033[32m"; c_warn="\033[33m"; c_err="\033[31m"; c_dim="\033[2m"; c_off="\033[0m"
ok()   { printf "  ${c_ok}\xe2\x9c\x93${c_off} %s\n" "$*"; }
warn() { printf "  ${c_warn}!${c_off} %s\n" "$*"; }
die()  { printf "${c_err}x %s${c_off}\n" "$*" >&2; exit 1; }

command -v jq >/dev/null 2>&1 || die "Thieu jq. Cai jq roi chay lai."

get_key() { [ -f "$CRED_FILE" ] && cat "$CRED_FILE" || printf ''; }
need_key() {
  local key; key="$(get_key)"
  [ -n "$key" ] || die "Chua co API key. Chay: seshat init \"<api-key>\""
  printf '%s' "$key"
}

api_json() {
  local method="$1" path="$2" body="${3:-}"
  local key; key="$(need_key)"
  if [ -n "$body" ]; then
    curl -fsS -X "$method" "$API_BASE_URL$path" \
      -H "Authorization: Bearer $key" \
      -H "Content-Type: application/json" \
      -d "$body"
  else
    curl -fsS -X "$method" "$API_BASE_URL$path" \
      -H "Authorization: Bearer $key"
  fi
}

prompt_value() {
  local label="$1" value="${2:-}"
  if [ -n "$value" ]; then printf '%s' "$value"; return 0; fi
  printf "%s: " "$label" >&2
  read -r value
  printf '%s' "$value"
}

prompt_mode() {
  local mode="${1:-}"
  if [ -n "$mode" ]; then printf '%s' "$mode"; return 0; fi
  printf "Mode [PUBLIC/private/custom]: " >&2
  read -r mode
  [ -n "$mode" ] || mode="PUBLIC"
  printf '%s' "$mode"
}

detect_paths() {
  case "$(uname -s)" in
    Darwin)
      CLAUDE_DESKTOP_DIR="$HOME/Library/Application Support/Claude"
      VSCODE_USER="$HOME/Library/Application Support/Code/User" ;;
    *)
      CLAUDE_DESKTOP_DIR="$HOME/.config/Claude"
      VSCODE_USER="$HOME/.config/Code/User" ;;
  esac
}

# Sinh object server theo schema cua tung client, dung KEY hien tai.
server_obj() {
  local key; key="$(get_key)"
  case "$1" in
    std)      jq -nc --arg u "$MCP_URL" --arg k "$key" '{url:$u, headers:{API_KEY:$k}}' ;;
    http)     jq -nc --arg u "$MCP_URL" --arg k "$key" '{type:"http", url:$u, headers:{API_KEY:$k}}' ;;
    windsurf) jq -nc --arg u "$MCP_URL" --arg k "$key" '{serverUrl:$u, headers:{API_KEY:$k}}' ;;
  esac
}

# Chen / ghi de CHI key omSeshat, giu nguyen moi mcp khac trong file.
merge_provider() {
  local label="$1" cfg="$2" parent="$3" schema="$4" detect="$5" force="$6"
  if [ "$force" != "1" ] && [ ! -e "$detect" ]; then return 0; fi
  mkdir -p "$(dirname "$cfg")"
  [ -s "$cfg" ] || echo '{}' > "$cfg"
  jq -e . "$cfg" >/dev/null 2>&1 || { warn "$label: config khong phai JSON hop le, bo qua ($cfg)"; return 0; }
  local obj tmp; obj="$(server_obj "$schema")"; tmp="$(mktemp)"
  if jq --arg p "$parent" --arg n "$MCP_NAME" --argjson o "$obj" \
        '.[$p] = ((.[$p] // {}) + {($n): $o})' "$cfg" > "$tmp" 2>/dev/null; then
    mv "$tmp" "$cfg"; ok "$label  ${c_dim}($cfg)${c_off}"
  else
    rm -f "$tmp"; warn "$label: ghi that bai"
  fi
}

remove_provider() {
  local label="$1" cfg="$2" parent="$3"
  [ -s "$cfg" ] || return 0
  jq -e . "$cfg" >/dev/null 2>&1 || return 0
  local tmp; tmp="$(mktemp)"
  if jq --arg p "$parent" --arg n "$MCP_NAME" 'if .[$p] then .[$p] |= del(.[$n]) else . end' "$cfg" > "$tmp" 2>/dev/null; then
    mv "$tmp" "$cfg"; ok "Go khoi $label"
  else rm -f "$tmp"; fi
}

# label | cfg file | parent | schema | thu muc phat hien
for_each_merge() {
  detect_paths; local force="${1:-0}"
  merge_provider "Claude Desktop" "$CLAUDE_DESKTOP_DIR/claude_desktop_config.json" "mcpServers" "std"      "$CLAUDE_DESKTOP_DIR"      "$force"
  merge_provider "Claude Code"    "$HOME/.claude.json"                            "mcpServers" "http"     "$HOME/.claude"            "$force"
  merge_provider "Cursor"         "$HOME/.cursor/mcp.json"                        "mcpServers" "std"      "$HOME/.cursor"            "$force"
  merge_provider "Windsurf"       "$HOME/.codeium/windsurf/mcp_config.json"       "mcpServers" "windsurf" "$HOME/.codeium/windsurf"  "$force"
  merge_provider "VS Code"        "$VSCODE_USER/mcp.json"                         "servers"    "http"     "$VSCODE_USER"             "$force"
}

for_each_remove() {
  detect_paths
  remove_provider "Claude Desktop" "$CLAUDE_DESKTOP_DIR/claude_desktop_config.json" "mcpServers"
  remove_provider "Claude Code"    "$HOME/.claude.json"                            "mcpServers"
  remove_provider "Cursor"         "$HOME/.cursor/mcp.json"                        "mcpServers"
  remove_provider "Windsurf"       "$HOME/.codeium/windsurf/mcp_config.json"       "mcpServers"
  remove_provider "VS Code"        "$VSCODE_USER/mcp.json"                         "servers"
}

cmd_sync() {
  local force=0; [ "${1:-}" = "--all" ] && force=1
  echo "-> Dong bo MCP '$MCP_NAME' vao cac provider:"
  for_each_merge "$force"
  [ -n "$(get_key)" ] || warn "Chua co API key. Chay: seshat init \"<api-key>\""
}

cmd_init() {
  local key="${1:-}"
  [ -n "$key" ] || die "Cu phap: seshat init \"<api-key>\""
  echo "-> Dang validate API key..."
  local resp code; resp="$(mktemp)"
  code="$(curl -fsS -o "$resp" -w '%{http_code}' \
          -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
          -X POST "$VALIDATE_URL" -d '{}' 2>/dev/null || true)"
  if [ "$code" != "200" ]; then rm -f "$resp"; die "Key khong hop le (HTTP ${code:-no-response})"; fi
  if [ "$(jq -r '.data.valid // .valid // "true"' "$resp" 2>/dev/null)" = "false" ]; then rm -f "$resp"; die "Server tu choi key nay"; fi
  rm -f "$resp"
  mkdir -p "$SESHAT_HOME"; printf '%s' "$key" > "$CRED_FILE"; chmod 600 "$CRED_FILE"
  ok "Key hop le, da luu."
  cmd_sync
  echo "Xong. Khoi dong lai app AI (Claude Desktop / Cursor...) de nap MCP."
}

cmd_status() {
  detect_paths
  echo "Key da luu: $([ -n "$(get_key)" ] && echo 'co' || echo 'CHUA')"
  echo "Trang thai '$MCP_NAME':"
  _st() { local l="$1" c="$2" p="$3"
    if [ -s "$c" ] && jq -e --arg p "$p" --arg n "$MCP_NAME" '.[$p][$n]' "$c" >/dev/null 2>&1
    then ok "$l"; else printf "  ${c_dim}-${c_off} %s\n" "$l"; fi; }
  _st "Claude Desktop" "$CLAUDE_DESKTOP_DIR/claude_desktop_config.json" "mcpServers"
  _st "Claude Code"    "$HOME/.claude.json"                            "mcpServers"
  _st "Cursor"         "$HOME/.cursor/mcp.json"                        "mcpServers"
  _st "Windsurf"       "$HOME/.codeium/windsurf/mcp_config.json"       "mcpServers"
  _st "VS Code"        "$VSCODE_USER/mcp.json"                         "servers"
}

cmd_remove() { echo "-> Go MCP '$MCP_NAME' khoi moi provider (giu mcp khac):"; for_each_remove; }

cmd_uninstall() {
  cmd_remove
  rm -f "$CRED_FILE"
  rmdir "$SESHAT_HOME" 2>/dev/null || true
  rm -f "$0"
  ok "Da go credentials va CLI seshat."
  warn "Neu shell van cache command, chay: hash -r"
}

cmd_projects() {
  api_json GET "/projects" | jq -r '
    (.data // []) as $items
    | if ($items | length) == 0 then "Chua co project nao."
      else
        $items[]
        | "\(.id)\t\(.slug)\t\(.mode)\t\(.url // "-")"
      end'
}

cmd_project_add() {
  local slug="" mode="" arg
  while [ "$#" -gt 0 ]; do
    arg="$1"; shift
    case "$arg" in
      --mode|-m) mode="${1:-}"; shift || true ;;
      --mode=*) mode="${arg#*=}" ;;
      *) [ -z "$slug" ] && slug="$arg" || true ;;
    esac
  done
  slug="$(prompt_value "Project slug" "$slug")"
  mode="$(prompt_mode "$mode")"
  [ -n "$slug" ] || die "Project slug la bat buoc"
  api_json POST "/projects" "$(jq -nc --arg slug "$slug" --arg mode "$mode" '{slug:$slug, mode:$mode}')" \
    | jq -r '.data | "Created project #\(.id): \(.slug) (\(.mode))"'
}

project_id_for_slug() {
  local slug="$1"
  api_json GET "/projects" | jq -r --arg slug "$slug" '
    (.data // [])
    | map(select(.slug == $slug or (.id | tostring) == $slug))
    | first
    | if . then .id else empty end'
}

project_json_for_slug() {
  local slug="$1"
  api_json GET "/projects" | jq -c --arg slug "$slug" '
    (.data // [])
    | map(select(.slug == $slug or (.id | tostring) == $slug))
    | first
    | if . then . else empty end'
}

append_upload_file() {
  local file="$1" rel="$2"
  UPLOAD_FORM_ARGS+=("-F" "files=@${file}" "-F" "paths=${rel}")
}

collect_upload_path() {
  local input="$1"
  if [ -d "$input" ]; then
    local root rel file
    root="$(cd "$input" && pwd)"
    while IFS= read -r -d '' file; do
      rel="${file#$root/}"
      append_upload_file "$file" "$rel"
    done < <(find "$root" -type f -print0)
  elif [ -f "$input" ]; then
    append_upload_file "$input" "$(basename "$input")"
  else
    die "Khong tim thay file/thu muc: $input"
  fi
}

cmd_upload() {
  [ "$#" -ge 2 ] || die "Cu phap: seshat upload <project-slug-or-id> <path...>"
  local project="$1"; shift
  local project_id key
  project_id="$(project_id_for_slug "$project")"
  [ -n "$project_id" ] || die "Khong tim thay project: $project"
  key="$(need_key)"
  UPLOAD_FORM_ARGS=()
  local item
  for item in "$@"; do collect_upload_path "$item"; done
  [ "${#UPLOAD_FORM_ARGS[@]}" -gt 0 ] || die "Khong co file de upload"
  curl -fsS -X POST "$API_BASE_URL/projects/$project_id/files" \
    -H "Authorization: Bearer $key" \
    "${UPLOAD_FORM_ARGS[@]}" \
    | jq -r '.data | "Uploaded files: \(.fileCount)\nProject: \(.projectId)"'
}

cmd_pull() {
  local project="" target_dir="" force=0 clean=0 arg
  while [ "$#" -gt 0 ]; do
    arg="$1"; shift
    case "$arg" in
      --force) force=1 ;;
      --clean) clean=1 ;;
      *) if [ -z "$project" ]; then project="$arg"; elif [ -z "$target_dir" ]; then target_dir="$arg"; fi ;;
    esac
  done

  [ -n "$project" ] || die "Cu phap: seshat pull <project-slug-or-id> [target-dir] [--force] [--clean]"
  command -v unzip >/dev/null 2>&1 || die "Thieu unzip. Cai unzip roi chay lai."

  local project_json project_id project_slug key tmp_zip unzip_args
  project_json="$(project_json_for_slug "$project")"
  [ -n "$project_json" ] || die "Khong tim thay project: $project"

  project_id="$(printf '%s' "$project_json" | jq -r '.id')"
  project_slug="$(printf '%s' "$project_json" | jq -r '.slug')"
  [ -n "$target_dir" ] || target_dir="./$project_slug"

  if [ "$clean" = "1" ] && { [ "$target_dir" = "." ] || [ "$target_dir" = "/" ]; }; then
    die "Khong cho phep --clean voi target nguy hiem: $target_dir"
  fi

  if [ -d "$target_dir" ] && [ "$clean" != "1" ] && [ "$force" != "1" ] && [ -n "$(find "$target_dir" -mindepth 1 -print -quit 2>/dev/null)" ]; then
    die "Thu muc dich khong rong. Dung --force de ghi de file trung, hoac --clean de tai lai tu dau."
  fi

  if [ "$clean" = "1" ] && [ -e "$target_dir" ]; then
    rm -rf -- "$target_dir"
  fi

  mkdir -p "$target_dir"
  tmp_zip="$(mktemp "${TMPDIR:-/tmp}/seshat-pull.XXXXXX.zip")"
  key="$(need_key)"

  echo "-> Dang tai project '$project_slug'..."
  if ! curl -fsS -L \
      -H "Authorization: Bearer $key" \
      -o "$tmp_zip" \
      "$API_BASE_URL/projects/$project_id/archive"; then
    rm -f "$tmp_zip"
    die "Tai project that bai"
  fi

  unzip_args="-q"
  if [ "$force" = "1" ] || [ "$clean" = "1" ]; then unzip_args="-oq"; fi
  unzip $unzip_args "$tmp_zip" -d "$target_dir"
  rm -f "$tmp_zip"

  ok "Da pull project '$project_slug' ve $target_dir"
}

cmd_deploy() {
  [ "$#" -ge 1 ] || die "Cu phap: seshat deploy <project-slug-or-id>"
  local project="$1" project_id no_wait=0 arg
  shift
  for arg in "$@"; do
    case "$arg" in
      --no-wait) no_wait=1 ;;
    esac
  done
  project_id="$(project_id_for_slug "$project")"
  [ -n "$project_id" ] || die "Khong tim thay project: $project"

  local response job_id
  response="$(api_json POST "/projects/$project_id/deployments" "{}")"
  printf '%s\n' "$response" | jq -r '.data | "Queued deploy job: \(.jobId)"'
  job_id="$(printf '%s\n' "$response" | jq -r '.data.jobId')"

  if [ "$no_wait" = "1" ]; then
    return 0
  fi

  wait_for_deploy "$project_id" "$job_id"
}

wait_for_deploy() {
  local project_id="$1" job_id="$2"
  local interval="${SESHAT_DEPLOY_POLL_INTERVAL_SECONDS:-2}"
  local timeout="${SESHAT_DEPLOY_WAIT_TIMEOUT_SECONDS:-600}"
  local started now status response url error_message

  started="$(date +%s)"
  echo "Waiting for deploy to finish..."

  while true; do
    response="$(api_json GET "/projects/$project_id/deployments/$job_id")"
    status="$(printf '%s\n' "$response" | jq -r '.data.status')"
    printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$status"

    case "$status" in
      DONE)
        url="$(printf '%s\n' "$response" | jq -r '.data.resultUrl // "-"')"
        ok "Deploy completed."
        echo "URL: $url"
        return 0
        ;;
      FAILED)
        error_message="$(printf '%s\n' "$response" | jq -r '.data.error // "Deploy failed"')"
        echo "Deploy failed." >&2
        echo "Error: $error_message" >&2
        return 1
        ;;
    esac

    now="$(date +%s)"
    if [ $((now - started)) -ge "$timeout" ]; then
      echo "Deploy wait timed out after ${timeout}s. Check status with: seshat deploy status <project> $job_id" >&2
      return 1
    fi

    sleep "$interval"
  done
}

cmd_deployments() {
  [ "$#" -ge 1 ] || die "Cu phap: seshat deployments <project-slug-or-id>"
  local project="$1" project_id
  project_id="$(project_id_for_slug "$project")"
  [ -n "$project_id" ] || die "Khong tim thay project: $project"

  api_json GET "/projects/$project_id/deployments" | jq -r '
    (.data // []) as $items
    | if ($items | length) == 0 then "Chua co deploy nao."
      else
        $items[]
        | "\(.id[0:8])\t\(.status)\t\(.createdAt)\t\(.resultUrl // "-")\t\(.error // "-")"
      end'
}

cmd_deploy_status() {
  [ "$#" -ge 2 ] || die "Cu phap: seshat deploy status <project-slug-or-id> <job-id>"
  local project="$1" job_id="$2" project_id
  project_id="$(project_id_for_slug "$project")"
  [ -n "$project_id" ] || die "Khong tim thay project: $project"

  api_json GET "/projects/$project_id/deployments/$job_id" | jq -r '
    .data
    | "Job: \(.id)\nProject: \(.projectId)\nStatus: \(.status)\nCreated: \(.createdAt)\nStarted: \(.startedAt // "-")\nFinished: \(.finishedAt // "-")\nURL: \(.resultUrl // "-")\nError: \(.error // "-")"'
}

cmd_deploy_requeue() {
  [ "$#" -ge 2 ] || die "Cu phap: seshat deploy requeue <project-slug-or-id> <job-id>"
  local project="$1" job_id="$2" project_id
  project_id="$(project_id_for_slug "$project")"
  [ -n "$project_id" ] || die "Khong tim thay project: $project"

  api_json POST "/projects/$project_id/deployments/$job_id/requeue" "{}" \
    | jq -r '.data | "Requeued deploy job: \(.jobId)"'
}

case "${1:-help}" in
  init)   shift; cmd_init "${1:-}";;
  sync)   shift; cmd_sync "${1:-}";;
  status) cmd_status;;
  remove) cmd_remove;;
  uninstall) cmd_uninstall;;
  projects) cmd_projects;;
  project)
    shift
    case "${1:-help}" in
      add) shift; cmd_project_add "$@";;
      *) die "Cu phap: seshat project add [slug] --mode public|private|custom";;
    esac
    ;;
  add)
    shift
    case "${1:-help}" in
      project) shift; cmd_project_add "$@";;
      *) die "Cu phap: seshat add project [slug] --mode public|private|custom";;
    esac
    ;;
  upload) shift; cmd_upload "$@";;
  pull) shift; cmd_pull "$@";;
  deployments) shift; cmd_deployments "$@";;
  deploy)
    shift
    case "${1:-}" in
      status) shift; cmd_deploy_status "$@";;
      requeue) shift; cmd_deploy_requeue "$@";;
      *) cmd_deploy "$@";;
    esac
    ;;
  *) cat <<USAGE
seshat - quan ly MCP '$MCP_NAME'
  seshat init "<api-key>"   Validate key qua server roi ghi vao tat ca provider
  seshat sync [--all]       Chen lai MCP (--all: ke ca app chua phat hien)
  seshat status             Xem provider nao da co MCP
  seshat remove             Go MCP khoi moi provider
  seshat uninstall          Go MCP, xoa key va xoa command seshat
  seshat projects           Liet ke project cua ban
  seshat project add [slug] --mode public|private|custom
  seshat add project [slug] --mode public|private|custom
  seshat upload <project> <path...>
  seshat pull <project> [target-dir] [--force] [--clean]
  seshat deploy <project> [--no-wait]
  seshat deploy requeue <project> <job-id>
  seshat deployments <project>
  seshat deploy status <project> <job-id>
USAGE
  ;;
esac
SESHAT_EOF
  sed -i.bak \
    -e "s|__DEFAULT_API_BASE_URL__|$DEFAULT_API_BASE_URL|g" \
    -e "s|__DEFAULT_MCP_URL__|$DEFAULT_MCP_URL|g" \
    -e "s|__DEFAULT_VALIDATE_URL__|$DEFAULT_VALIDATE_URL|g" \
    "$SESHAT_BIN"
  rm -f "$SESHAT_BIN.bak"
  chmod +x "$SESHAT_BIN"
  ok "Da cai CLI: $SESHAT_BIN"
}

ensure_path() {
  case ":$PATH:" in *":$BIN_DIR:"*) return 0;; esac
  local rc=""
  case "${SHELL:-}" in */zsh) rc="$HOME/.zshrc";; *) rc="$HOME/.bashrc";; esac
  if [ -n "$rc" ] && { [ -f "$rc" ] || touch "$rc"; } && ! grep -q "$BIN_DIR" "$rc" 2>/dev/null; then
    printf '\nexport PATH="%s:$PATH"\n' "$BIN_DIR" >> "$rc"
    warn "Da them $BIN_DIR vao PATH ($rc) - mo terminal moi hoac: source $rc"
  fi
}

say "${c_dim}Seshat MCP installer${c_off}"
ensure_jq
install_cli
ensure_path
"$SESHAT_BIN" sync
echo
say "Buoc cuoi - gan API key:"
say "  ${c_ok}seshat init \"your-api-key\"${c_off}"
say "${c_dim}(Neu 'command not found': mo terminal moi, hoac $SESHAT_BIN init \"key\")${c_off}"
