#!/bin/sh
set -eu

SERVER_NAME="barvinca"
SERVER_URL="https://mcp.barvinca.com/mcp"
READ_SCOPES="barvinca.read"
DOMAIN_SCOPES="barvinca.read,barvinca.issues.read,barvinca.rfis.read,barvinca.assets.read,barvinca.submittals.read,barvinca.forms.read"
ADMIN_SCOPES="barvinca.read,barvinca.issues.read,barvinca.rfis.read,barvinca.assets.read,barvinca.submittals.read,barvinca.forms.read,barvinca.write"
CLIENT=""
MODE="install"
MODE_SET=0
NON_INTERACTIVE=0
PROFILE="read"

say() { printf '%s\n' "$*"; }
die() { code="$1"; shift; say "$*" >&2; exit "$code"; }
usage() {
  say "Usage: install/mcp.sh --client codex|claude|cursor|all [--check|--update|--uninstall] [--non-interactive] [--profile read|domains|admin]"
}
set_mode() {
  [ "$MODE_SET" -eq 0 ] || die 2 "Choose only one of --check, --update, or --uninstall."
  MODE="$1"; MODE_SET=1
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --client) [ "$#" -ge 2 ] || die 2 "--client requires a value."; CLIENT="$2"; shift 2 ;;
    --profile) [ "$#" -ge 2 ] || die 2 "--profile requires a value."; PROFILE="$2"; shift 2 ;;
    --check) set_mode check; shift ;;
    --update) set_mode update; shift ;;
    --uninstall) set_mode uninstall; shift ;;
    --non-interactive) NON_INTERACTIVE=1; shift ;;
    -h|--help) usage; exit 0 ;;
    *) usage >&2; die 2 "Unknown option: $1" ;;
  esac
done

[ -n "$CLIENT" ] || { usage >&2; die 2 "Explicit --client selection is required."; }
case "$CLIENT" in codex|claude|cursor|all) ;; *) die 2 "Unsupported client: $CLIENT" ;; esac
case "$PROFILE" in read) SCOPES="$READ_SCOPES" ;; domains) SCOPES="$DOMAIN_SCOPES" ;; admin) SCOPES="$ADMIN_SCOPES" ;; *) die 2 "Unsupported profile: $PROFILE" ;; esac
[ "$PROFILE" = read ] || [ "$CLIENT" = codex ] || [ "$CLIENT" = all ] || die 2 "--profile domains|admin is supported only with --client codex or all."

find_cursor() {
  if command -v agent >/dev/null 2>&1; then command -v agent; return 0; fi
  if command -v cursor-agent >/dev/null 2>&1; then command -v cursor-agent; return 0; fi
  return 1
}

codex_existing() { "$1" mcp get "$SERVER_NAME" --json 2>/dev/null; }
run_codex() {
  command -v codex >/dev/null 2>&1 || die 4 "Codex CLI was not found."
  bin="$(command -v codex)"
  existing=""
  if existing="$(codex_existing "$bin")"; then
    printf '%s\n' "$existing" | grep -Fq "$SERVER_URL" || die 3 "A different Codex MCP server already uses the name $SERVER_NAME."
  fi
  if [ "$MODE" = uninstall ]; then
    [ -n "$existing" ] || { say "Codex: already absent."; return; }
    "$bin" mcp logout "$SERVER_NAME" >/dev/null 2>&1 || true
    "$bin" mcp remove "$SERVER_NAME" >/dev/null || die 6 "Codex could not remove $SERVER_NAME."
    say "Codex: removed."; return
  fi
  [ -n "$existing" ] || { [ "$MODE" != check ] || die 5 "Codex: $SERVER_NAME is not configured."; }
  if [ "$MODE" = update ] && [ -n "$existing" ]; then
    "$bin" mcp remove "$SERVER_NAME" >/dev/null || die 6 "Codex could not update $SERVER_NAME."
    existing=""
  fi
  if [ -z "$existing" ]; then
    "$bin" mcp add "$SERVER_NAME" --url "$SERVER_URL" --oauth-resource "$SERVER_URL" >/dev/null || die 6 "Codex could not configure $SERVER_NAME."
  fi
  if [ "$MODE" != check ] && [ "$NON_INTERACTIVE" -eq 0 ]; then
    "$bin" mcp login "$SERVER_NAME" --scopes "$SCOPES" || die 5 "Codex OAuth login did not complete."
  elif [ "$MODE" != check ]; then
    say "Codex: configured; run codex mcp login $SERVER_NAME --scopes $SCOPES to authorize."
    return
  fi
  codex_existing "$bin" | grep -Fq "$SERVER_URL" || die 5 "Codex configuration verification failed."
  "$bin" --ask-for-approval never -c 'mcp_servers.barvinca.url="https://mcp.barvinca.com/mcp"' -c 'mcp_servers.barvinca.oauth_resource="https://mcp.barvinca.com/mcp"' -c 'mcp_servers.barvinca.enabled_tools=["barvinca_connection_status"]' exec --ephemeral --sandbox read-only --skip-git-repo-check 'Call barvinca_connection_status exactly once. Do not call any other tool. Return only whether the Barvinca connection is healthy.' >/dev/null || die 5 "Codex could not execute barvinca_connection_status."
  say "Codex: configuration, transport, and connection-status tool verified."
}

claude_existing() { "$1" mcp get "$SERVER_NAME" 2>/dev/null; }
run_claude() {
  command -v claude >/dev/null 2>&1 || die 4 "Claude Code CLI was not found."
  bin="$(command -v claude)"
  existing=""
  if existing="$(claude_existing "$bin")"; then
    printf '%s\n' "$existing" | grep -Fq "$SERVER_URL" || die 3 "A different Claude MCP server already uses the name $SERVER_NAME."
  fi
  if [ "$MODE" = uninstall ]; then
    [ -n "$existing" ] || { say "Claude Code: already absent."; return; }
    "$bin" mcp remove --scope user "$SERVER_NAME" >/dev/null || die 6 "Claude Code could not remove $SERVER_NAME."
    say "Claude Code: removed."; return
  fi
  [ -n "$existing" ] || { [ "$MODE" != check ] || die 5 "Claude Code: $SERVER_NAME is not configured."; }
  if [ "$MODE" = update ] && [ -n "$existing" ]; then
    "$bin" mcp remove --scope user "$SERVER_NAME" >/dev/null || die 6 "Claude Code could not update $SERVER_NAME."
    existing=""
  fi
  if [ -z "$existing" ]; then
    config="{\"type\":\"http\",\"url\":\"$SERVER_URL\",\"oauth\":{\"scopes\":\"$READ_SCOPES\"}}"
    "$bin" mcp add-json --scope user "$SERVER_NAME" "$config" >/dev/null || die 6 "Claude Code could not configure $SERVER_NAME."
  fi
  claude_existing "$bin" | grep -Fq "$SERVER_URL" || die 5 "Claude Code configuration verification failed."
  "$bin" mcp list >/dev/null || die 5 "Claude Code could not connect to its MCP catalog."
  if [ "$NON_INTERACTIVE" -eq 1 ] && [ "$MODE" != check ]; then
    say "Claude Code: configured; open /mcp in Claude Code to authorize."; return
  fi
  "$bin" -p --max-turns 1 --tools "" --allowedTools "mcp__barvinca__barvinca_connection_status" 'Call mcp__barvinca__barvinca_connection_status exactly once and return only whether it is healthy.' >/dev/null || die 5 "Claude Code could not execute barvinca_connection_status; open /mcp to authorize, then retry --check."
  say "Claude Code: configuration, transport, and connection-status tool verified."
}

cursor_config() {
  command -v python3 >/dev/null 2>&1 || die 4 "Python 3 is required to update Cursor JSON safely."
  config_path="$HOME/.cursor/mcp.json"
  owner_path="$HOME/.cursor/.barvinca-mcp-installer-owned"
  [ ! -L "$config_path" ] || die 6 "Refusing to modify symlinked Cursor configuration: $config_path"
  python3 - "$config_path" "$owner_path" "$1" "$SERVER_NAME" "$SERVER_URL" "$READ_SCOPES" <<'PY'
import json, os, stat, sys, tempfile
path, owner_path, mode, name, url, scopes = sys.argv[1:]
created_by_installer = not os.path.exists(path)
if os.path.islink(path):
    print(f"Refusing to modify symlinked Cursor configuration: {path}", file=sys.stderr)
    raise SystemExit(6)
try:
    with open(path, encoding="utf-8") as source:
        data = json.load(source)
except FileNotFoundError:
    data = {}
except (OSError, json.JSONDecodeError) as error:
    print(f"Cannot read Cursor configuration: {error}", file=sys.stderr)
    raise SystemExit(6)
if not isinstance(data, dict):
    print("Cursor configuration root must be a JSON object.", file=sys.stderr)
    raise SystemExit(6)
servers = data.setdefault("mcpServers", {})
if not isinstance(servers, dict):
    print("Cursor mcpServers must be a JSON object.", file=sys.stderr)
    raise SystemExit(6)
entry = servers.get(name)
entry_url = entry.get("url") if isinstance(entry, dict) else None
if entry is not None and entry_url != url:
    print(f"A different Cursor MCP server already uses the name {name}.", file=sys.stderr)
    raise SystemExit(3)
if mode == "check":
    raise SystemExit(0 if entry_url == url else 5)
if mode == "uninstall":
    if entry is None:
        raise SystemExit(0)
    del servers[name]
    if not servers:
        del data["mcpServers"]
    if not data and os.path.isfile(owner_path):
        os.unlink(path)
        os.unlink(owner_path)
        raise SystemExit(0)
else:
    servers[name] = {"type": "http", "url": url, "auth": {"scopes": scopes}}
directory = os.path.dirname(path)
os.makedirs(directory, mode=0o700, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix=".mcp.json.", dir=directory)
try:
    os.fchmod(fd, stat.S_IRUSR | stat.S_IWUSR)
    with os.fdopen(fd, "w", encoding="utf-8") as target:
        json.dump(data, target, indent=2, sort_keys=True)
        target.write("\n")
        target.flush()
        os.fsync(target.fileno())
    os.replace(temporary, path)
    if created_by_installer:
        owner_fd = os.open(owner_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
        os.close(owner_fd)
finally:
    if os.path.exists(temporary):
        os.unlink(temporary)
PY
}

run_cursor() {
  bin="$(find_cursor)" || die 4 "Cursor Agent CLI was not found (tried agent and cursor-agent)."
  cursor_config "$MODE" || { status="$?"; case "$status" in 3|5|6) exit "$status" ;; *) die 6 "Cursor configuration update failed." ;; esac; }
  if [ "$MODE" = uninstall ]; then say "Cursor Agent: removed; revoke its OAuth connection in Barvinca Settings."; return; fi
  if [ "$MODE" != check ] && [ "$NON_INTERACTIVE" -eq 0 ]; then
    "$bin" mcp login "$SERVER_NAME" || die 5 "Cursor OAuth login did not complete."
  elif [ "$MODE" != check ]; then
    say "Cursor Agent: configured; run $bin mcp login $SERVER_NAME to authorize."; return
  fi
  "$bin" mcp list | grep -E "$SERVER_NAME.*(ready|connected)|((ready|connected).*$SERVER_NAME)" >/dev/null || die 5 "Cursor did not report a ready MCP server."
  tools="$("$bin" mcp list-tools "$SERVER_NAME")" || die 5 "Cursor could not discover Barvinca tools."
  for tool in barvinca_connection_status barvinca_account_list barvinca_project_list barvinca_project_user_list barvinca_account_user_list barvinca_industry_role_list barvinca_operation_list barvinca_operation_get barvinca_operation_evidence barvinca_operation_recovery_status barvinca_operation_export; do printf '%s\n' "$tools" | grep -Fq "$tool" || die 5 "Cursor read-only catalog is incomplete."; done
  printf '%s\n' "$tools" | grep -Eq "barvinca_(issue|rfi|asset|submittal|form|user_.*_preflight|operation_execute|operation_cancel)" && die 5 "Cursor catalog contains tools outside the read-only profile."
  say "Cursor Agent: configuration, transport, and tool discovery verified."
}

say "Barvinca MCP: client=$CLIENT mode=$MODE profile=$PROFILE"
case "$CLIENT" in
  codex) run_codex ;;
  claude) run_claude ;;
  cursor) run_cursor ;;
  all) run_codex; run_claude; run_cursor ;;
esac
