#!/usr/bin/env bash
# Token Optimizer MCP - Automated Hooks Installer (macOS/Linux)
# Installs global Claude Code hooks for automatic token optimization

set -euo pipefail

# ============================================================
# Configuration
# ============================================================

FORCE=false
SKIP_MCP_CHECK=false
DRY_RUN=false

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --force)
            FORCE=true
            shift
            ;;
        --skip-mcp-check)
            SKIP_MCP_CHECK=true
            shift
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        -h|--help)
            echo "Usage: $0 [OPTIONS]"
            echo ""
            echo "Options:"
            echo "  --force           Force reinstall even if already installed"
            echo "  --skip-mcp-check  Skip MCP server installation check"
            echo "  --dry-run         Preview changes without applying"
            echo "  -h, --help        Show this help message"
            exit 0
            ;;
        *)
            echo "Unknown option: $1"
            echo "Run '$0 --help' for usage information"
            exit 1
            ;;
    esac
done

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HOOKS_DIR="$HOME/.claude-global/hooks"
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
CLAUDE_STATE="$HOME/.claude.json"

# Determine Claude Desktop config path based on OS
if [[ "$OSTYPE" == "darwin"* ]]; then
    MCP_CONFIG="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
    NPM_PREFIX=$(npm config get prefix 2>/dev/null || echo "/usr/local")
    MCP_GLOBAL_PATH="$NPM_PREFIX/lib/node_modules/@ooples/token-optimizer-mcp"
else
    MCP_CONFIG="$HOME/.config/Claude/claude_desktop_config.json"
    NPM_PREFIX=$(npm config get prefix 2>/dev/null || echo "/usr/local")
    MCP_GLOBAL_PATH="$NPM_PREFIX/lib/node_modules/@ooples/token-optimizer-mcp"
fi

REPO_URL="https://raw.githubusercontent.com/ooples/token-optimizer-mcp/main/hooks"

# ============================================================
# Helper Functions
# ============================================================

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

write_status() {
    local message="$1"
    local type="${2:-INFO}"

    case "$type" in
        SUCCESS)
            echo -e "${GREEN}[SUCCESS]${NC} $message"
            ;;
        ERROR)
            echo -e "${RED}[ERROR]${NC} $message"
            ;;
        WARN)
            echo -e "${YELLOW}[WARN]${NC} $message"
            ;;
        *)
            echo -e "${CYAN}[INFO]${NC} $message"
            ;;
    esac
}

test_prerequisites() {
    write_status "Checking prerequisites..." "INFO"

    # Check Bash version
    if [[ ${BASH_VERSION%%.*} -lt 4 ]]; then
        echo "Bash 4.0 or later is required. Current version: $BASH_VERSION"
        exit 1
    fi
    write_status "✓ Bash version: $BASH_VERSION" "SUCCESS"

    # Check Claude Code is installed
    if ! command -v claude &> /dev/null; then
        echo "Claude Code CLI not found. Install from: https://docs.claude.com/en/docs/claude-code"
        exit 1
    fi
    local claude_version=$(claude --version 2>&1 || echo "unknown")
    write_status "✓ Claude Code installed: $claude_version" "SUCCESS"

    # Check npm is installed
    if ! command -v npm &> /dev/null; then
        echo "npm not found. Install Node.js from: https://nodejs.org/"
        exit 1
    fi
    local npm_version=$(npm --version)
    write_status "✓ npm version: $npm_version" "SUCCESS"

    # Check token-optimizer-mcp is installed (optional)
    if [[ "$SKIP_MCP_CHECK" == "false" ]]; then
        if [[ ! -d "$MCP_GLOBAL_PATH" ]]; then
            write_status "token-optimizer-mcp not found" "WARN"
            write_status "Install with: npm install -g @ooples/token-optimizer-mcp" "INFO"

            read -p "Install token-optimizer-mcp now? (y/n) " -n 1 -r
            echo
            if [[ $REPLY =~ ^[Yy]$ ]]; then
                npm install -g @ooples/token-optimizer-mcp
                write_status "✓ token-optimizer-mcp installed" "SUCCESS"
            else
                echo "token-optimizer-mcp is required for hooks to work"
                exit 1
            fi
        else
            write_status "✓ token-optimizer-mcp found" "SUCCESS"
        fi
    fi
}

install_hooks_files() {
    write_status "Installing hooks files..." "INFO"

    # Create hooks directory
    if [[ ! -d "$HOOKS_DIR" ]]; then
        mkdir -p "$HOOKS_DIR"
        write_status "✓ Created hooks directory: $HOOKS_DIR" "SUCCESS"
    fi

    if [[ "$DRY_RUN" == "true" ]]; then
        write_status "[DRY RUN] Would install the plugin hooks into $HOOKS_DIR/token-optimizer" "INFO"
        return
    fi

    # Install the CURRENT hook files from the package.
    #
    # This used to download a `dispatcher.sh` from the default branch and wire
    # four events at it -- none of them SessionStart, which is where the policy
    # and the project briefing are delivered. The plugin's real hooks are three
    # ES modules under plugin/hooks, and they are what the doctor probes, so
    # they are what gets installed.
    local source_hooks="$SCRIPT_DIR/plugin/hooks"
    if [[ ! -d "$source_hooks" ]]; then
        source_hooks="$MCP_GLOBAL_PATH/plugin/hooks"
    fi

    if [[ ! -d "$source_hooks" ]]; then
        write_status "Could not find the plugin hooks to install (looked in $source_hooks)" "ERROR"
        write_status "Reinstall the package: npm install -g @ooples/token-optimizer-mcp" "INFO"
        exit 1
    fi

    # Under a token-optimizer/ subdirectory on purpose: it puts our marker into
    # every command string we write, which is what makes the entries findable
    # for verification and removable on uninstall.
    local dest="$HOOKS_DIR/token-optimizer"
    mkdir -p "$dest"
    cp -r "$source_hooks"/. "$dest"/
    write_status "Installed hooks to $dest" "SUCCESS"
}

configure_claude_settings() {
    write_status "Configuring Claude Code settings..." "INFO"

    # Create .claude directory if it doesn't exist
    mkdir -p "$(dirname "$CLAUDE_SETTINGS")"

    # Backup existing settings
    if [[ -f "$CLAUDE_SETTINGS" ]]; then
        local backup="$CLAUDE_SETTINGS.backup.$(date +%Y%m%d-%H%M%S)"
        cp "$CLAUDE_SETTINGS" "$backup"
        write_status "✓ Backed up existing settings to: $backup" "SUCCESS"
    fi

    # Wire the hooks by MERGING, never by overwriting.
    #
    # The previous version wrote the whole settings file when `jq` was absent
    # and shallow-merged the `hooks` object when it was present -- both of which
    # destroyed every hook the user had configured, silently. The merge now
    # lives in scripts/wire-hooks.mjs, which appends our entries, replaces only
    # our own from a previous run, and leaves everything else untouched.
    local wire_script="$SCRIPT_DIR/scripts/wire-hooks.mjs"
    if [[ ! -f "$wire_script" ]]; then
        wire_script="$MCP_GLOBAL_PATH/scripts/wire-hooks.mjs"
    fi

    if [[ "$DRY_RUN" == "true" ]]; then
        node "$wire_script" "$CLAUDE_SETTINGS" "$HOOKS_DIR/token-optimizer" --dry-run
        return
    fi

    if ! node "$wire_script" "$CLAUDE_SETTINGS" "$HOOKS_DIR/token-optimizer"; then
        write_status "Could not wire hooks into $CLAUDE_SETTINGS" "ERROR"
        return 1
    fi

    write_status "✓ Updated Claude Code settings" "SUCCESS"
}

configure_workspace_trust() {
    write_status "Configuring workspace trust..." "INFO"

    local current_dir=$(pwd)

    if [[ ! -f "$CLAUDE_STATE" ]]; then
        write_status "No .claude.json found - trust will be prompted on first run" "WARN"
        return
    fi

    # Backup existing state
    local backup="$CLAUDE_STATE.backup.$(date +%Y%m%d-%H%M%S)"
    cp "$CLAUDE_STATE" "$backup"

    # Update state using jq if available, otherwise manual JSON manipulation
    if command -v jq &> /dev/null; then
        local updated=$(jq ".projects[\"$current_dir\"].hasTrustDialogAccepted = true" "$CLAUDE_STATE")
        echo "$updated" > "$CLAUDE_STATE"
    else
        write_status "jq not found - workspace trust must be accepted manually on first run" "WARN"
        return
    fi

    if [[ "$DRY_RUN" == "true" ]]; then
        write_status "[DRY RUN] Would accept workspace trust for: $current_dir" "INFO"
        return
    fi

    write_status "✓ Accepted workspace trust for: $current_dir" "SUCCESS"
}

configure_mcp_server() {
    write_status "Detecting installed AI tools and configuring MCP server..." "INFO"

    local mcp_path="$MCP_GLOBAL_PATH/dist/index.js"
    local tools_configured=0

    # Helper function to configure a tool
    configure_tool() {
        local config_file="$1"
        local tool_name="$2"

        if [[ -f "$config_file" ]] || [[ "$tool_name" == "Claude Desktop" ]]; then
            # Create directory if needed
            mkdir -p "$(dirname "$config_file")"

            local mcp_entry=$(cat <<EOF
{
  "mcpServers": {
    "token-optimizer": {
      "type": "stdio",
      "command": "node",
      "args": ["$mcp_path"],
      "env": {}
    }
  }
}
EOF
)

            if [[ "$DRY_RUN" == "true" ]]; then
                write_status "[DRY RUN] Would configure $tool_name" "INFO"
                return 0
            fi

            # Merge with existing config if jq is available
            if [[ -f "$config_file" ]] && command -v jq &> /dev/null; then
                local merged=$(jq -s '.[0] * .[1]' "$config_file" <(echo "$mcp_entry"))
                echo "$merged" > "$config_file"
            else
                echo "$mcp_entry" > "$config_file"
            fi

            write_status "✓ Configured token-optimizer for $tool_name" "SUCCESS"
            return 1
        fi
        return 0
    }

    # 1. Claude Desktop
    if [[ "$OSTYPE" == "darwin"* ]]; then
        claude_config="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
    else
        claude_config="$HOME/.config/Claude/claude_desktop_config.json"
    fi
    configure_tool "$claude_config" "Claude Desktop" && ((tools_configured++)) || true

    # 2. Cursor IDE
    cursor_config="$HOME/.cursor/mcp.json"
    configure_tool "$cursor_config" "Cursor IDE" && ((tools_configured++)) || true

    # 3. Cline (VS Code)
    if [[ "$OSTYPE" == "darwin"* ]]; then
        cline_config="$HOME/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/cline_mcp_settings.json"
    else
        cline_config="$HOME/.config/Code/User/globalStorage/saoudrizwan.claude-dev/cline_mcp_settings.json"
    fi
    configure_tool "$cline_config" "Cline (VS Code)" && ((tools_configured++)) || true

    # 4. VS Code Copilot (workspace)
    vscode_config=".vscode/mcp.json"
    configure_tool "$vscode_config" "VS Code Copilot (workspace)" && ((tools_configured++)) || true

    # 5. Windsurf IDE
    if [[ "$OSTYPE" == "darwin"* ]]; then
        windsurf_config="$HOME/Library/Application Support/Windsurf/mcp.json"
    else
        windsurf_config="$HOME/.config/Windsurf/mcp.json"
    fi
    configure_tool "$windsurf_config" "Windsurf IDE" && ((tools_configured++)) || true

    if [[ $tools_configured -eq 0 ]]; then
        write_status "No AI tools detected. MCP server not configured." "WARN"
        write_status "Supported tools: Claude Desktop, Cursor IDE, Cline (VS Code), GitHub Copilot (VS Code), Windsurf" "INFO"
    else
        write_status "✓ Configured token-optimizer MCP server for $tools_configured AI tool(s)" "SUCCESS"
    fi
}

test_installation() {
    write_status "Verifying installation..." "INFO"

    local issues=()

    # Verify the files this installer ACTUALLY SHIPS.
    #
    # This required a dispatcher.sh plus handlers/ and helpers/ shell libraries
    # -- the design install_hooks_files replaced with three ES modules. None of
    # them is produced by an install, so verification could never pass, and a
    # failed verification skipped the install manifest. Same bug, same cause, as
    # install-hooks.ps1.
    #
    # Note the subdirectory: install_hooks_files copies into
    # $HOOKS_DIR/token-optimizer, which the old list also omitted.
    local required_files=(
        "$HOOKS_DIR/token-optimizer/session-start.mjs"
        "$HOOKS_DIR/token-optimizer/pretooluse-router.mjs"
        "$HOOKS_DIR/token-optimizer/precompact-optimize.mjs"
        "$HOOKS_DIR/token-optimizer/hooks.json"
        "$HOOKS_DIR/token-optimizer/lib/policy.mjs"
    )

    for file in "${required_files[@]}"; do
        if [[ ! -f "$file" ]]; then
            issues+=("Missing file: $file")
        fi
    done

    # Check settings.json has hooks
    if [[ -f "$CLAUDE_SETTINGS" ]]; then
        if ! grep -q '"hooks"' "$CLAUDE_SETTINGS"; then
            issues+=("Hooks not configured in settings.json")
        fi
    else
        issues+=("Settings.json not found")
    fi

    # Check MCP server configured in at least one AI tool
    local mcp_configured=false
    local checked_configs=()

    # Claude Desktop
    if [[ "$OSTYPE" == "darwin"* ]]; then
        claude_config="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
    else
        claude_config="$HOME/.config/Claude/claude_desktop_config.json"
    fi
    if [[ -f "$claude_config" ]] && grep -q '"token-optimizer"' "$claude_config"; then
        mcp_configured=true
        checked_configs+=("Claude Desktop")
    fi

    # Cursor IDE
    cursor_config="$HOME/.cursor/mcp.json"
    if [[ -f "$cursor_config" ]] && grep -q '"token-optimizer"' "$cursor_config"; then
        mcp_configured=true
        checked_configs+=("Cursor IDE")
    fi

    # Cline (VS Code)
    if [[ "$OSTYPE" == "darwin"* ]]; then
        cline_config="$HOME/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/cline_mcp_settings.json"
    else
        cline_config="$HOME/.config/Code/User/globalStorage/saoudrizwan.claude-dev/cline_mcp_settings.json"
    fi
    if [[ -f "$cline_config" ]] && grep -q '"token-optimizer"' "$cline_config"; then
        mcp_configured=true
        checked_configs+=("Cline (VS Code)")
    fi

    # VS Code Copilot
    vscode_config=".vscode/mcp.json"
    if [[ -f "$vscode_config" ]] && grep -q '"token-optimizer"' "$vscode_config"; then
        mcp_configured=true
        checked_configs+=("VS Code Copilot")
    fi

    # Windsurf IDE
    if [[ "$OSTYPE" == "darwin"* ]]; then
        windsurf_config="$HOME/Library/Application Support/Windsurf/mcp.json"
    else
        windsurf_config="$HOME/.config/Windsurf/mcp.json"
    fi
    if [[ -f "$windsurf_config" ]] && grep -q '"token-optimizer"' "$windsurf_config"; then
        mcp_configured=true
        checked_configs+=("Windsurf IDE")
    fi

    # ADVISORY, NOT FATAL. This looks for the server in Claude Desktop, Cursor,
    # Cline, Copilot and Windsurf. A Claude Code user has it in none of them --
    # that client gets the MCP server from the plugin's own .mcp.json -- so
    # counting absence as an installation failure fails the install for the
    # primary supported client.
    if [[ "$mcp_configured" == "false" ]]; then
        write_status "MCP server not found in Claude Desktop / Cursor / Cline / Copilot / Windsurf" "WARN"
        write_status "That is expected on Claude Code, which gets the server from the plugin instead" "INFO"
    else
        write_status "✓ MCP server configured in: ${checked_configs[*]}" "SUCCESS"
    fi

    if [[ ${#issues[@]} -gt 0 ]]; then
        write_status "Installation issues found:" "ERROR"
        for issue in "${issues[@]}"; do
            write_status "  - $issue" "ERROR"
        done
        return 1
    fi

    write_status "✓ All verification checks passed!" "SUCCESS"
    return 0
}

# ============================================================
# Main Installation Flow
# ============================================================

echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║   Token Optimizer MCP - Hooks Installer                  ║"
echo "║   Automated installation of global Claude Code hooks     ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""

if [[ "$DRY_RUN" == "true" ]]; then
    write_status "DRY RUN MODE - No changes will be made" "WARN"
    echo ""
fi

# Step 1: Prerequisites
test_prerequisites
echo ""

# Step 2: Install hooks files
install_hooks_files
echo ""

# Step 3: Configure Claude Code settings
configure_claude_settings
echo ""

# Step 4: Configure workspace trust
configure_workspace_trust
echo ""

# Step 5: Configure MCP server
configure_mcp_server
echo ""

# Step 6: Verify installation
if [[ "$DRY_RUN" == "true" ]]; then
    write_status "DRY RUN COMPLETE - No changes were made" "SUCCESS"
else
    # RECORD THE MANIFEST REGARDLESS OF THE VERDICT.
    #
    # Files were written to this machine before verification ran, so the record
    # of what was written must not be conditional on verification passing --
    # that is precisely the case where an exact uninstall matters most, and it
    # is why the doctor reported a missing manifest on a working install.
    if command -v node >/dev/null 2>&1; then
        node "$(dirname "${BASH_SOURCE[0]}")/scripts/record-install.mjs" \
            "$HOOKS_DIR" "$CLAUDE_SETTINGS" 2>/dev/null \
            || write_status "Could not record the install manifest (uninstall will be manual)" "WARN"
    fi

    if test_installation; then
        echo ""
        echo "╔═══════════════════════════════════════════════════════════╗"
        echo "║   Installation Complete!                                  ║"
        echo "╚═══════════════════════════════════════════════════════════╝"
        echo ""
        write_status "Next steps:" "INFO"
        write_status "1. Restart Claude Code CLI" "INFO"
        write_status "2. Run any command (e.g., claude 'help')" "INFO"
        write_status "3. Check logs: tail -f '$HOOKS_DIR/logs/dispatcher.log'" "INFO"
        echo ""
        write_status "Documentation: $HOME/source/repos/token-optimizer-mcp/HOOKS-INSTALLATION.md" "INFO"
    else
        echo "Installation verification failed"
        exit 1
    fi
fi
