#!/usr/bin/env bash
# ContextStream MCP installer.
#
# This is the source for:
#   curl -fsSL https://contextstream.io/scripts/mcp.sh | bash
#
# It downloads the latest published platform binary, installs it, and then
# runs the interactive setup wizard when a TTY is available.
set -euo pipefail

R2_BASE_URL="${CONTEXTSTREAM_RELEASE_BASE_URL:-https://pub-68429b9f7857416c9484b75bf1887b96.r2.dev/mcp}"
INSTALL_DIR="${CONTEXTSTREAM_INSTALL_DIR:-/usr/local/bin}"
BINARY_NAME="contextstream-mcp"
HOOK_BINARY_NAME="contextstream-hook"
PRESELECTED_TRANSPORT_MODE="remote"

if [ -t 1 ]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[1;33m'
    BLUE='\033[0;34m'
    CYAN='\033[0;36m'
    BOLD='\033[1m'
    NC='\033[0m'
else
    RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' NC=''
fi

print_banner() {
    echo ""
    echo -e "${BLUE}-------------------------------${NC}"
    echo -e "${BOLD}   ContextStream MCP Beta Setup${NC}"
    echo -e "${BLUE}-------------------------------${NC}"
    echo ""
}

print_success() { echo -e "${GREEN}OK${NC} $1"; }
print_warning() { echo -e "${YELLOW}!${NC} $1" >&2; }
print_error() { echo -e "${RED}ERROR${NC} $1" >&2; }
print_info() { echo -e "${BLUE}->${NC} $1"; }

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

should_skip_setup() {
    [ "${CONTEXTSTREAM_INSTALL_SKIP_SETUP:-}" = "1" ] \
        || [ "${CONTEXTSTREAM_INSTALL_SKIP_SETUP:-}" = "true" ]
}

has_tty() {
    (true < /dev/tty) 2>/dev/null
}

normalize_transport_mode() {
    case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" in
        remote|hosted|hosted-remote)
            echo "remote"
            ;;
        local|binary|local-binary)
            echo "local"
            ;;
        *)
            echo ""
            ;;
    esac
}

ensure_preselected_transport_mode() {
    local from_env
    from_env="$(normalize_transport_mode "${CONTEXTSTREAM_SETUP_TRANSPORT:-}")"
    if [ -n "$from_env" ]; then
        PRESELECTED_TRANSPORT_MODE="$from_env"
    fi
}

mcp_command_path() {
    local installed="$INSTALL_DIR/$BINARY_NAME"
    if [ -x "$installed" ]; then
        printf '%s\n' "$installed"
        return 0
    fi

    if command_exists "$BINARY_NAME"; then
        command -v "$BINARY_NAME"
        return 0
    fi

    printf '%s\n' "$BINARY_NAME"
}

run_mcp() {
    local bin
    bin="$(mcp_command_path)"
    "$bin" "$@"
}

run_setup() {
    if ! has_tty; then
        print_warning "No TTY available - run '$BINARY_NAME setup' manually"
        return 1
    fi

    local bin
    bin="$(mcp_command_path)"

    if [ ! -x "$bin" ] && ! command_exists "$bin"; then
        print_warning "$BINARY_NAME is not installed or not executable at $bin"
        return 1
    fi

    # When piped on macOS, use script to create a PTY with /dev/tty as input.
    # Use the absolute installed path so PATH/cache issues cannot hide install
    # failures as "script: contextstream-mcp: No such file or directory".
    if [ ! -t 0 ] && [ "$(uname)" = "Darwin" ]; then
        CONTEXTSTREAM_SETUP_TRANSPORT="${PRESELECTED_TRANSPORT_MODE:-}" \
            script -q /dev/null "$bin" setup < /dev/tty
        return $?
    fi

    CONTEXTSTREAM_SETUP_TRANSPORT="${PRESELECTED_TRANSPORT_MODE:-}" \
        "$bin" setup < /dev/tty
}

configure_hooks() {
    local key_info
    key_info="$(run_mcp verify-key --json 2>/dev/null || echo '{"valid":false}')"
    local key_valid
    key_valid="$(echo "$key_info" | grep -o '"valid":true' || true)"

    if [ -n "$key_valid" ]; then
        print_info "Refreshing hooks..."
        if run_mcp update-hooks --scope=global 2>/dev/null; then
            print_success "Editor hooks updated"
        fi
        return 0
    fi

    return 1
}

schedule_post_update_finalize() {
    local mode="${1:-local}"
    local installed="$INSTALL_DIR/$BINARY_NAME"
    [ -x "$installed" ] || return 0

    nohup sh -c '
        mode="$1"
        project_dir="$2"
        binary="$3"

        finalize_once() {
            cd "$project_dir" 2>/dev/null || true
            if [ "$mode" = "remote" ]; then
                "$binary" migrate-remote --scope=all >/dev/null 2>&1 || true
            else
                "$binary" update-hooks --scope=global >/dev/null 2>&1 || true
            fi
        }

        sleep 8
        finalize_once
        sleep 8
        finalize_once
    ' sh "$mode" "$PWD" "$installed" >/dev/null 2>&1 &
}

config_uses_remote_transport() {
    local path="$1"
    [ -f "$path" ] || return 1

    grep -q '"type"[[:space:]]*:[[:space:]]*"http"' "$path" 2>/dev/null \
        || grep -q 'url = "https://mcp\.contextstream\.io/mcp' "$path" 2>/dev/null
}

detect_selected_transport_mode() {
    local marker="$HOME/.contextstream/setup-transport-mode"
    if [ -f "$marker" ]; then
        local mode
        mode="$(tr -d '[:space:]' < "$marker" 2>/dev/null || true)"
        if [ "$mode" = "remote" ] || [ "$mode" = "local" ]; then
            echo "$mode"
            return 0
        fi
    fi

    if config_uses_remote_transport "$PWD/.mcp.json" \
        || config_uses_remote_transport "$HOME/.claude/mcp.json" \
        || config_uses_remote_transport "$HOME/.codex/config.toml"; then
        echo "remote"
    else
        echo "local"
    fi
}

is_project_configured() {
    [ -f ".contextstream/config.json" ]
}

resolve_existing_path() {
    local path="$1"
    if command_exists realpath; then
        realpath "$path" 2>/dev/null && return 0
    fi

    local dir
    dir="$(cd -P "$(dirname "$path")" >/dev/null 2>&1 && pwd)" || {
        echo "$path"
        return 0
    }
    echo "$dir/$(basename "$path")"
}

remove_shadowing_binaries() {
    local expected="$INSTALL_DIR/$BINARY_NAME"
    local resolved
    resolved="$(command -v "$BINARY_NAME" 2>/dev/null || true)"

    if [ -z "$resolved" ]; then
        return 0
    fi

    local resolved_real expected_real
    resolved_real="$(resolve_existing_path "$resolved")"
    expected_real="$(resolve_existing_path "$expected")"

    if [ "$resolved_real" = "$expected_real" ]; then
        return 0
    fi

    local all_paths
    all_paths="$(type -a "$BINARY_NAME" 2>/dev/null | awk '{print $NF}' | grep '^/' || true)"

    local home_dir="${HOME:-}"
    [ -z "$home_dir" ] && home_dir="$(eval echo "~" 2>/dev/null || true)"

    local removed=false
    while IFS= read -r bin_path; do
        [ -z "$bin_path" ] && continue

        local bin_real
        bin_real="$(resolve_existing_path "$bin_path")"
        [ "$bin_real" = "$expected_real" ] && continue

        if [ -n "$home_dir" ] && echo "$bin_path" | grep -q "^$home_dir/"; then
            print_info "Removing stale binary at $bin_path (shadows $expected)..."
            rm -f "$bin_path" 2>/dev/null && removed=true \
                || print_warning "Could not remove $bin_path"
        fi
    done <<< "$all_paths"

    if [ "$removed" = true ]; then
        hash -r 2>/dev/null || true
        print_success "Stale binaries removed"
    fi
}

remove_npm_version() {
    local bin_path
    bin_path="$(command -v "$BINARY_NAME" 2>/dev/null || true)"

    if [ -z "$bin_path" ]; then
        return 0
    fi

    if echo "$bin_path" | grep -qE "(node_modules|\.nvm|\.npm|/npm/|/node/)"; then
        print_info "Removing npm-installed version..."

        if command_exists npm; then
            npm uninstall -g @contextstream/mcp-server 2>/dev/null || true
            npm uninstall -g contextstream-mcp 2>/dev/null || true
        fi

        local new_path
        new_path="$(command -v "$BINARY_NAME" 2>/dev/null || true)"
        if [ -n "$new_path" ] && echo "$new_path" | grep -qE "(node_modules|\.nvm|\.npm|/npm/|/node/)"; then
            rm -f "$new_path" 2>/dev/null || true
        fi

        print_success "npm version removed"
    fi
}

get_os() {
    case "$(uname -s)" in
        Darwin*)  echo "darwin" ;;
        Linux*)   echo "linux" ;;
        CYGWIN*|MINGW*|MSYS*) echo "windows" ;;
        *)        echo "unknown" ;;
    esac
}

get_arch() {
    case "$(uname -m)" in
        x86_64|amd64) echo "x64" ;;
        arm64|aarch64) echo "arm64" ;;
        *)        echo "unknown" ;;
    esac
}

get_latest_version() {
    local version_json
    version_json="$(curl -fsSL -H "Cache-Control: no-cache" "$R2_BASE_URL/latest/version.json" 2>/dev/null || true)"
    echo "$version_json" | grep '"version"' | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/' || true
}

get_current_version() {
    local bin=""
    if command_exists "$BINARY_NAME"; then
        bin="$(command -v "$BINARY_NAME")"
    elif [ -x "$INSTALL_DIR/$BINARY_NAME" ]; then
        bin="$INSTALL_DIR/$BINARY_NAME"
    fi

    if [ -n "$bin" ]; then
        "$bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true
    fi
}

ensure_install_dir() {
    if [ -d "$INSTALL_DIR" ]; then
        return 0
    fi

    if mkdir -p "$INSTALL_DIR" 2>/dev/null; then
        return 0
    fi

    print_info "Requesting sudo to create $INSTALL_DIR..."
    sudo mkdir -p "$INSTALL_DIR"
}

install_file() {
    local src="$1"
    local dest="$2"

    if [ -w "$INSTALL_DIR" ]; then
        install -m 755 "$src" "$dest"
    else
        sudo install -m 755 "$src" "$dest"
    fi
}

clear_macos_security_attrs() {
    local path="$1"
    [ "$(uname)" = "Darwin" ] || return 0

    if command_exists xattr; then
        if [ -w "$path" ]; then
            xattr -cr "$path" 2>/dev/null || true
            xattr -d com.apple.provenance "$path" 2>/dev/null || true
        else
            sudo xattr -cr "$path" 2>/dev/null || true
            sudo xattr -d com.apple.provenance "$path" 2>/dev/null || true
        fi
    fi

    if command_exists codesign; then
        if [ -w "$path" ]; then
            codesign --sign - --force "$path" 2>/dev/null || true
        else
            sudo codesign --sign - --force "$path" 2>/dev/null || true
        fi
    fi
}

install_binary() {
    local os="$1"
    local arch="$2"
    local version="$3"
    local artifact_name="contextstream-mcp-${os}-${arch}"
    local hook_name="contextstream-hook-${os}-${arch}"
    local download_url="$R2_BASE_URL/v${version}/${artifact_name}"
    local hook_url="$R2_BASE_URL/v${version}/${hook_name}"
    local tmp_file tmp_hook

    tmp_file="$(mktemp "${TMPDIR:-/tmp}/contextstream-mcp.XXXXXX")" || return 1
    tmp_hook="$(mktemp "${TMPDIR:-/tmp}/contextstream-hook.XXXXXX")" || {
        rm -f "$tmp_file"
        return 1
    }

    print_info "Downloading $artifact_name..."

    if ! curl -fsSL -H "Cache-Control: no-cache, no-store" -H "Pragma: no-cache" \
        "$download_url" -o "$tmp_file" 2>/dev/null; then
        rm -f "$tmp_file" "$tmp_hook"
        return 1
    fi

    curl -fsSL -H "Cache-Control: no-cache, no-store" -H "Pragma: no-cache" \
        "$hook_url" -o "$tmp_hook" 2>/dev/null || true

    local install_path="$INSTALL_DIR/$BINARY_NAME"
    local hook_path="$INSTALL_DIR/$HOOK_BINARY_NAME"

    if ! ensure_install_dir; then
        rm -f "$tmp_file" "$tmp_hook"
        return 1
    fi

    if [ ! -w "$INSTALL_DIR" ]; then
        print_info "Requesting sudo to install to $INSTALL_DIR..."
    fi

    if ! install_file "$tmp_file" "$install_path"; then
        print_error "Could not install $install_path"
        rm -f "$tmp_file" "$tmp_hook"
        return 1
    fi

    clear_macos_security_attrs "$install_path"

    if [ -s "$tmp_hook" ]; then
        if ! install_file "$tmp_hook" "$hook_path"; then
            print_warning "Could not install optional hook helper at $hook_path"
        else
            clear_macos_security_attrs "$hook_path"
        fi
    fi

    rm -f "$tmp_file" "$tmp_hook"
    hash -r 2>/dev/null || true

    if [ ! -x "$install_path" ]; then
        print_error "Installed binary is not executable: $install_path"
        return 1
    fi

    if ! "$install_path" --version >/dev/null 2>&1; then
        print_error "Installed binary failed to run: $install_path"
        return 1
    fi

    return 0
}

ensure_deleted_path_compat_link() {
    local install_path="$INSTALL_DIR/$BINARY_NAME"
    local compat_path="$INSTALL_DIR/$BINARY_NAME (deleted)"

    [ -e "$install_path" ] || return 0

    if [ -w "$INSTALL_DIR" ]; then
        rm -f "$compat_path" 2>/dev/null || true
        ln -s "$install_path" "$compat_path" 2>/dev/null || true
    else
        sudo rm -f "$compat_path" 2>/dev/null || true
        sudo ln -s "$install_path" "$compat_path" 2>/dev/null || true
    fi
}

install_via_npm() {
    if [ "${CONTEXTSTREAM_BETA_ALLOW_LEGACY_NPM:-}" = "1" ] \
        || [ "${CONTEXTSTREAM_BETA_ALLOW_LEGACY_NPM:-}" = "true" ]; then
        print_warning "Using legacy npm fallback (@contextstream/mcp-server) because CONTEXTSTREAM_BETA_ALLOW_LEGACY_NPM is enabled."
        npm install -g @contextstream/mcp-server@latest
        return $?
    fi

    print_error "Rust beta binary unavailable and legacy npm fallback is disabled."
    print_info "Enable legacy fallback temporarily with CONTEXTSTREAM_BETA_ALLOW_LEGACY_NPM=true"
    return 1
}

maybe_run_setup_for_current_install() {
    local current="$1"

    if should_skip_setup; then
        print_success "Already up to date, skipping setup"
        echo ""
        return 0
    fi

    local key_info
    key_info="$(run_mcp verify-key --json 2>/dev/null || echo '{"valid":false}')"
    local key_valid
    key_valid="$(echo "$key_info" | grep -o '"valid":true' || true)"

    if [ -n "$key_valid" ] && is_project_configured; then
        if has_tty; then
            echo -n -e "${BLUE}->${NC} Update configurations? [y/N] "
            local response
            read -r response < /dev/tty
            if [[ "$response" =~ ^[Yy]$ ]]; then
                echo ""
                ensure_preselected_transport_mode
                run_setup
            else
                echo ""
                print_success "No changes made"
            fi
        else
            print_success "No changes made (non-interactive mode)"
        fi
    else
        if has_tty; then
            if [ -n "$current" ] && [ -n "$key_valid" ]; then
                print_info "New project directory detected, running setup wizard..."
            else
                print_info "Running setup wizard..."
            fi
            ensure_preselected_transport_mode
            run_setup
        else
            print_warning "No TTY available - run '$BINARY_NAME setup' manually to complete setup"
        fi
    fi
}

main() {
    print_banner

    local os arch
    os="$(get_os)"
    arch="$(get_arch)"

    print_info "Detected: $os ($arch)"
    echo ""

    remove_npm_version
    remove_shadowing_binaries

    local current latest
    current="$(get_current_version)"
    latest="$(get_latest_version)"

    if [ -n "$current" ]; then
        print_info "Current version: $current"
    fi

    if [ -n "$latest" ]; then
        print_info "Latest version:  $latest"
    else
        print_warning "Could not fetch latest version"
        latest="latest"
    fi

    echo ""

    if [ -n "$current" ] && [ "$current" = "$latest" ]; then
        print_success "Already up to date!"
        echo ""
        maybe_run_setup_for_current_install "$current"
        echo ""
        return 0
    fi

    if [ -n "$current" ]; then
        print_info "Updating ContextStream MCP Server..."
    else
        print_info "Installing ContextStream MCP Server..."
    fi

    ensure_preselected_transport_mode

    local binary_installed=false
    if [ "$os" != "unknown" ] && [ "$arch" != "unknown" ] && [ "$latest" != "latest" ]; then
        if install_binary "$os" "$arch" "$latest"; then
            binary_installed=true
            print_success "Binary installed to $INSTALL_DIR/$BINARY_NAME"
            ensure_deleted_path_compat_link
            remove_shadowing_binaries
            configure_hooks || true
        else
            print_warning "Binary not available for $os-$arch."
        fi
    fi

    if [ "$binary_installed" = false ]; then
        if install_via_npm; then
            print_success "Installed via legacy npm fallback"
        else
            print_error "Installation failed for Rust MCP beta."
            print_info "Expected binary: contextstream-mcp-$os-$arch"
            exit 1
        fi
    fi

    echo ""

    if should_skip_setup; then
        if [ -n "$current" ]; then
            print_success "Binary updated, skipping setup"
        else
            print_success "Binary installed, skipping setup"
        fi
        echo ""
        print_success "Done!"
        echo ""
        return 0
    fi

    local key_info key_valid
    key_info="$(run_mcp verify-key --json 2>/dev/null || echo '{"valid":false}')"
    key_valid="$(echo "$key_info" | grep -o '"valid":true' || true)"

    if [ -n "$current" ] && [ -n "$key_valid" ] && is_project_configured; then
        if has_tty; then
            echo -n -e "${BLUE}->${NC} Update configurations? [y/N] "
            local response
            read -r response < /dev/tty
            if [[ "$response" =~ ^[Yy]$ ]]; then
                echo ""
                ensure_preselected_transport_mode
                run_setup
            else
                echo ""
                print_success "Binary updated, no editor changes"
            fi
        else
            print_success "Binary updated, no editor changes (non-interactive mode)"
        fi
    else
        if has_tty; then
            if [ -n "$current" ] && [ -n "$key_valid" ]; then
                print_info "New project directory detected, running setup wizard..."
            else
                print_info "Running setup wizard..."
            fi
            ensure_preselected_transport_mode
            run_setup
        else
            print_warning "No TTY available - run '$BINARY_NAME setup' manually to complete setup"
        fi
    fi

    if [ -n "$current" ]; then
        schedule_post_update_finalize "$(detect_selected_transport_mode)"
    fi

    echo ""
    print_success "Done!"
    echo ""
    print_info "Next steps:"
    echo "  - Restart your AI editor (Claude Code, Cursor, etc.)"
    echo "  - Try: \"session summary\" in your AI tool"
    echo "  - Docs: https://contextstream.io/docs/mcp"
    echo ""
}

main "$@"
