#!/bin/sh
# ha-mcp installer for macOS and Linux
#
# Default (Claude Desktop):
#   curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh
#
# Claude Code (official CLI — requires a paid Claude plan):
#   curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh -s -- --claude-code
set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Which client to configure: "desktop" (default) or "claude-code"
CLIENT="desktop"
for arg in "$@"; do
    case "$arg" in
        --claude-code) CLIENT="claude-code" ;;
        --desktop)     CLIENT="desktop" ;;
        -h|--help)
            printf "Usage: install.sh [--claude-code | --desktop]\n"
            printf "  --desktop      Configure Claude Desktop (default)\n"
            printf "  --claude-code  Configure Claude Code (CLI — requires a paid Claude plan)\n"
            exit 0
            ;;
        *) printf "${YELLOW}Ignoring unknown option: %s${NC}\n" "$arg" >&2 ;;
    esac
done

# Detect OS and set platform-specific values
OS=$(uname -s)
case "$OS" in
    Darwin)
        PLATFORM="macOS"
        CONFIG_DIR="$HOME/Library/Application Support/Claude"
        CLAUDE_DESKTOP_STEP="  1. Download Claude Desktop: ${BLUE}https://claude.ai/download${NC}\n"
        RESTART_STEP="  1. Quit Claude Desktop: Claude menu > Quit Claude"
        ;;
    Linux)
        PLATFORM="Linux"
        CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/Claude"
        CLAUDE_DESKTOP_STEP="  1. Install Claude Desktop for Linux (community build): ${BLUE}https://github.com/aaddrick/claude-desktop-debian${NC}\n     (Unofficial — Anthropic doesn't ship Claude Desktop for Linux.)\n"
        RESTART_STEP="  1. Quit Claude Desktop and reopen it"
        ;;
    *)
        printf "${RED}Unsupported OS: %s${NC}\n" "$OS"
        printf "See https://github.com/homeassistant-ai/ha-mcp for manual setup.\n"
        exit 1
        ;;
esac

CONFIG_FILE="$CONFIG_DIR/claude_desktop_config.json"
DEMO_URL="https://ha-mcp-demo-server.qc-h.net"
DEMO_TOKEN="demo"

# Detect WSL — Claude Desktop runs on the Windows host and reads %APPDATA%\Claude,
# so writing the Linux config does nothing. Claude Code, by contrast, runs fine in WSL.
IS_WSL=false
if [ "$OS" = "Linux" ]; then
    if [ -n "${WSL_DISTRO_NAME:-}" ] || grep -qiE 'microsoft|wsl' /proc/sys/kernel/osrelease 2>/dev/null; then
        IS_WSL=true
    fi
fi
if [ "$IS_WSL" = true ] && [ "$CLIENT" = "desktop" ]; then
    printf "${RED}Detected WSL (Windows Subsystem for Linux).${NC}\n" >&2
    printf "Claude Desktop runs on Windows and reads its config from %%APPDATA%%\\\\Claude\\\\,\n" >&2
    printf "not the WSL filesystem — configuring it here would do nothing.\n\n" >&2
    printf "Run the Windows installer from PowerShell instead:\n" >&2
    printf "  ${BLUE}irm https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-windows.ps1 | iex${NC}\n\n" >&2
    printf "Or use Claude Code inside WSL (supported) by re-running with --claude-code:\n" >&2
    printf "  ${BLUE}curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh -s -- --claude-code${NC}\n" >&2
    exit 1
fi

printf "\n"
printf "${BLUE}============================================${NC}\n"
if [ "$CLIENT" = "claude-code" ]; then
    printf "${BLUE}   ha-mcp Installer for %s (Claude Code)${NC}\n" "$PLATFORM"
else
    printf "${BLUE}   ha-mcp Installer for %s${NC}\n" "$PLATFORM"
fi
printf "${BLUE}============================================${NC}\n"
printf "\n"

# Step 1: Check/install uv
printf "${YELLOW}Step 1: Checking for uv...${NC}\n"
# Gate on uvx, not uv. uvx (the `uv tool run` shortcut) ships only with newer
# uv releases, so an old uv can be present without it. Since the rest of this
# script invokes uvx, a uvx-less uv must fall through to the install/upgrade
# path rather than being treated as already installed (issue #1593).
if command -v uvx > /dev/null 2>&1; then
    printf "${GREEN}  uv is already installed${NC}\n"
else
    printf "  Installing uv...\n"
    curl -LsSf https://astral.sh/uv/install.sh | sh
    # Source the new path
    export PATH="$HOME/.local/bin:$PATH"
    if command -v uvx > /dev/null 2>&1; then
        printf "${GREEN}  uv installed successfully${NC}\n"
    else
        printf "${RED}  Failed to install uv. Please install manually:${NC}\n"
        case "$OS" in
            Darwin)
                printf "  brew install uv\n"
                printf "  OR\n"
                ;;
        esac
        printf "  curl -LsSf https://astral.sh/uv/install.sh | sh\n"
        exit 1
    fi
fi

# Get full path to uvx (Claude Desktop and Claude Code don't inherit shell PATH)
UVX_PATH=""
if command -v uvx > /dev/null 2>&1; then
    UVX_PATH=$(command -v uvx)
elif [ -x "$HOME/.local/bin/uvx" ]; then
    UVX_PATH="$HOME/.local/bin/uvx"
elif [ -x "/usr/local/bin/uvx" ]; then
    UVX_PATH="/usr/local/bin/uvx"
elif [ "$OS" = "Darwin" ] && [ -x "/opt/homebrew/bin/uvx" ]; then
    UVX_PATH="/opt/homebrew/bin/uvx"
else
    printf "${RED}  Could not find uvx. Please check your installation.${NC}\n"
    exit 1
fi
printf "  Using uvx at: ${BLUE}%s${NC}\n" "$UVX_PATH"
printf "\n"

# ----------------------------------------------------------------------------
# Claude Code path (opt-in via --claude-code)
# ----------------------------------------------------------------------------
if [ "$CLIENT" = "claude-code" ]; then
    printf "${YELLOW}Step 2: Configuring Claude Code...${NC}\n"

    if ! command -v claude > /dev/null 2>&1 && [ ! -x "$HOME/.local/bin/claude" ]; then
        printf "  Claude Code not found - installing...\n"
        CC_INSTALLER=$(curl -fsSL https://claude.ai/install.sh) || {
            printf "${RED}  Failed to download the Claude Code installer.${NC}\n" >&2
            printf "  Install it manually, then re-run this script:\n" >&2
            printf "    curl -fsSL https://claude.ai/install.sh | bash\n" >&2
            exit 1
        }
        [ -n "$CC_INSTALLER" ] || {
            printf "${RED}  Claude Code installer download was empty.${NC}\n" >&2
            exit 1
        }
        # The official installer is a bash script; fall back to sh if bash is absent.
        INSTALL_SHELL=$(command -v bash || command -v sh)
        if [ -z "$INSTALL_SHELL" ]; then
            printf "${RED}  No bash or sh found to run the Claude Code installer.${NC}\n" >&2
            exit 1
        fi
        printf '%s\n' "$CC_INSTALLER" | "$INSTALL_SHELL" || {
            printf "${RED}  Claude Code installation failed.${NC}\n" >&2
            printf "  See https://code.claude.com/docs/en/setup for manual steps.\n" >&2
            exit 1
        }
        export PATH="$HOME/.local/bin:$PATH"
    else
        printf "${GREEN}  Claude Code is already installed${NC}\n"
    fi

    # Resolve the claude binary
    CLAUDE_BIN=""
    if command -v claude > /dev/null 2>&1; then
        CLAUDE_BIN=$(command -v claude)
    elif [ -x "$HOME/.local/bin/claude" ]; then
        CLAUDE_BIN="$HOME/.local/bin/claude"
    fi
    if [ -z "$CLAUDE_BIN" ]; then
        printf "${RED}  Could not find the 'claude' command after install.${NC}\n" >&2
        printf "  Open a new terminal (so PATH picks up ~/.local/bin) and re-run with --claude-code.\n" >&2
        exit 1
    fi

    # Configure ha-mcp at user scope so it's available in every project.
    # Remove any prior entry first so re-running this script is idempotent.
    "$CLAUDE_BIN" mcp remove home-assistant --scope user > /dev/null 2>&1 || true
    if "$CLAUDE_BIN" mcp add home-assistant --scope user \
        --env HOMEASSISTANT_URL="$DEMO_URL" \
        --env HOMEASSISTANT_TOKEN="$DEMO_TOKEN" \
        -- "$UVX_PATH" --python 3.13 --refresh ha-mcp@latest; then
        printf "${GREEN}  Claude Code configured (server: home-assistant)${NC}\n"
    else
        printf "${RED}  'claude mcp add' failed.${NC}\n" >&2
        printf "  Configure it manually:\n" >&2
        printf "    claude mcp add home-assistant --scope user \\\\\n" >&2
        printf "      --env HOMEASSISTANT_URL=%s \\\\\n" "$DEMO_URL" >&2
        printf "      --env HOMEASSISTANT_TOKEN=%s \\\\\n" "$DEMO_TOKEN" >&2
        printf "      -- %s --python 3.13 --refresh ha-mcp@latest\n" "$UVX_PATH" >&2
        exit 1
    fi
    printf "\n"

    # Step 3: Pre-download dependencies
    printf "${YELLOW}Step 3: Pre-downloading ha-mcp...${NC}\n"
    printf "  This speeds up the first Claude Code session...\n"
    if "$UVX_PATH" --python 3.13 --refresh ha-mcp@latest --version > /dev/null 2>&1; then
        printf "${GREEN}  Dependencies cached${NC}\n"
    else
        printf "${YELLOW}  Could not pre-download ha-mcp (network or PyPI issue).${NC}\n"
        printf "${YELLOW}  Not fatal — it will download on first use. To retry now:${NC}\n"
        printf "    %s --python 3.13 --refresh ha-mcp@latest --version\n" "$UVX_PATH"
    fi
    printf "\n"

    printf "${GREEN}============================================${NC}\n"
    printf "${GREEN}   Installation Complete!${NC}\n"
    printf "${GREEN}============================================${NC}\n"
    printf "\n"
    printf "${YELLOW}Next steps:${NC}\n"
    printf "\n"
    printf "  1. Start Claude Code:  ${BLUE}claude${NC}\n"
    printf "     (Claude Code requires a paid plan — Pro, Max, Team, or Enterprise.)\n"
    printf "  2. Run ${BLUE}/mcp${NC} inside Claude Code to confirm \"home-assistant\" is connected\n"
    printf "  3. Ask: \"Can you see my Home Assistant?\"\n"
    printf "\n"
    printf "${BLUE}Demo environment:${NC}\n"
    printf "  Web UI: %s\n" "$DEMO_URL"
    printf "  Login:  mcp / mcp\n"
    printf "  (Resets weekly - changes won't persist)\n"
    printf "\n"
    printf "${YELLOW}To use YOUR Home Assistant:${NC}\n"
    printf "  claude mcp remove home-assistant --scope user\n"
    printf "  then re-add with your own HOMEASSISTANT_URL / HOMEASSISTANT_TOKEN\n"
    printf "  (Generate a token in HA: Profile > Security > Long-lived tokens)\n"
    printf "\n"
    exit 0
fi

# ----------------------------------------------------------------------------
# Claude Desktop path (default)
# ----------------------------------------------------------------------------
# Step 2: Configure Claude Desktop
printf "${YELLOW}Step 2: Configuring Claude Desktop...${NC}\n"
CLAUDE_NOT_INSTALLED=false
if [ ! -d "$CONFIG_DIR" ]; then
    CLAUDE_NOT_INSTALLED=true
    printf "  Claude Desktop not yet installed - creating config for later\n"
fi

# Create config directory if needed
mkdir -p "$CONFIG_DIR"

# Check if config file exists and handle accordingly
if [ -f "$CONFIG_FILE" ]; then
    # Backup existing config
    BACKUP_FILE="${CONFIG_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
    cp "$CONFIG_FILE" "$BACKUP_FILE"
    printf "  Backed up existing config to:\n"
    printf "  ${BLUE}%s${NC}\n" "$BACKUP_FILE"

    # Check if ha-mcp is already configured
    if grep -q '"Home Assistant"' "$CONFIG_FILE" 2>/dev/null; then
        printf "${YELLOW}  Home Assistant MCP already configured.${NC}\n"
        printf "  Updating configuration...\n"
    fi

    # Merging an existing config needs python3
    if ! command -v python3 > /dev/null 2>&1; then
        printf "${RED}  python3 is required to merge your existing Claude config, but it isn't installed.${NC}\n" >&2
        printf "  Your existing config is untouched (backup above). Install python3 and re-run:\n" >&2
        printf "    Debian/Ubuntu:  sudo apt install python3\n" >&2
        printf "    Fedora/RHEL:    sudo dnf install python3\n" >&2
        printf "    Alpine:         sudo apk add python3\n" >&2
        printf "    macOS:          brew install python3\n" >&2
        exit 1
    fi

    # Use Python to merge the config. Values are passed via the environment and the
    # heredoc delimiter is quoted ('EOF'), so paths with quotes/backslashes/newlines
    # can't break the Python source or inject code.
    HA_CONFIG_FILE="$CONFIG_FILE" HA_BACKUP_FILE="$BACKUP_FILE" \
    HA_DEMO_URL="$DEMO_URL" HA_DEMO_TOKEN="$DEMO_TOKEN" HA_UVX_PATH="$UVX_PATH" \
    python3 << 'EOF'
import json
import os
import sys
import tempfile

config_file = os.environ["HA_CONFIG_FILE"]
backup_file = os.environ["HA_BACKUP_FILE"]
demo_url = os.environ["HA_DEMO_URL"]
demo_token = os.environ["HA_DEMO_TOKEN"]
uvx_path = os.environ["HA_UVX_PATH"]

try:
    with open(config_file, 'r') as f:
        content = f.read().strip()
        config = json.loads(content) if content else {}
except FileNotFoundError:
    config = {}
except json.JSONDecodeError as e:
    sys.stderr.write(
        "  WARNING: existing config is not valid JSON (%s).\n"
        "  Starting from a fresh config. Your original file — including any other\n"
        "  MCP servers it defined — is preserved in the backup:\n"
        "    %s\n"
        "  Merge those servers back manually if you need them.\n" % (e, backup_file)
    )
    config = {}

if 'mcpServers' not in config:
    config['mcpServers'] = {}

# Add/update Home Assistant config (using full path for Claude Desktop compatibility)
config['mcpServers']['Home Assistant'] = {
    "command": uvx_path,
    "args": ["--python", "3.13", "--refresh", "ha-mcp@latest"],
    "env": {
        "HOMEASSISTANT_URL": demo_url,
        "HOMEASSISTANT_TOKEN": demo_token
    }
}

# Write atomically: a crash mid-write must not truncate the user's config.
dir_name = os.path.dirname(config_file) or "."
fd, tmp_path = tempfile.mkstemp(dir=dir_name, suffix=".tmp")
try:
    with os.fdopen(fd, 'w') as f:
        json.dump(config, f, indent=2)
    os.replace(tmp_path, config_file)
except Exception:
    try:
        os.unlink(tmp_path)
    except OSError:
        pass
    raise

print("  Configuration updated successfully")
EOF
else
    # Create new config file (using full path for Claude Desktop compatibility).
    # Write to a private temp file then move into place so a crash can't leave a
    # partial config, and so the token is never briefly world-readable.
    TMP_CONFIG="${CONFIG_FILE}.tmp"
    trap 'rm -f "$TMP_CONFIG"' EXIT INT TERM
    if ! cat > "$TMP_CONFIG" << EOF
{
  "mcpServers": {
    "Home Assistant": {
      "command": "$UVX_PATH",
      "args": ["--python", "3.13", "--refresh", "ha-mcp@latest"],
      "env": {
        "HOMEASSISTANT_URL": "$DEMO_URL",
        "HOMEASSISTANT_TOKEN": "$DEMO_TOKEN"
      }
    }
  }
}
EOF
    then
        printf "${RED}  Failed to write config (disk full or permission denied?).${NC}\n" >&2
        exit 1
    fi
    chmod 600 "$TMP_CONFIG" 2>/dev/null || true
    if ! mv "$TMP_CONFIG" "$CONFIG_FILE"; then
        printf "${RED}  Failed to move config into place: %s${NC}\n" "$CONFIG_FILE" >&2
        exit 1
    fi
    trap - EXIT INT TERM
    printf "  Created new configuration file\n"
fi
printf "${GREEN}  Claude Desktop configured${NC}\n"
printf "\n"

# Step 3: Pre-download dependencies
printf "${YELLOW}Step 3: Pre-downloading ha-mcp...${NC}\n"
printf "  This speeds up Claude Desktop startup...\n"
if "$UVX_PATH" --python 3.13 --refresh ha-mcp@latest --version > /dev/null 2>&1; then
    printf "${GREEN}  Dependencies cached${NC}\n"
else
    printf "${YELLOW}  Could not pre-download ha-mcp (network or PyPI issue).${NC}\n"
    printf "${YELLOW}  Not fatal — Claude Desktop will download it on first launch,${NC}\n"
    printf "${YELLOW}  which only makes the first start slower. To retry now:${NC}\n"
    printf "    %s --python 3.13 --refresh ha-mcp@latest --version\n" "$UVX_PATH"
fi
printf "\n"

# Success message
printf "${GREEN}============================================${NC}\n"
printf "${GREEN}   Installation Complete!${NC}\n"
printf "${GREEN}============================================${NC}\n"
printf "\n"
printf "${YELLOW}Next steps:${NC}\n"
printf "\n"
if [ "$CLAUDE_NOT_INSTALLED" = true ]; then
    printf "%b" "$CLAUDE_DESKTOP_STEP"
    printf "  2. Create a free account at claude.ai (if you haven't)\n"
    printf "  3. Open Claude Desktop and ask: \"Can you see my Home Assistant?\"\n"
else
    printf "%s\n" "$RESTART_STEP"
    printf "  2. Ask: \"Can you see my Home Assistant?\"\n"
fi
printf "\n"
printf "${YELLOW}Note:${NC} If Claude Desktop was already running, you must restart it\n"
printf "      to load the new configuration.\n"
printf "\n"
printf "${BLUE}Demo environment:${NC}\n"
printf "  Web UI: %s\n" "$DEMO_URL"
printf "  Login:  mcp / mcp\n"
printf "  (Resets weekly - changes won't persist)\n"
printf "\n"
printf "${YELLOW}To use YOUR Home Assistant:${NC}\n"
printf "  Edit: %s\n" "$CONFIG_FILE"
printf "  Replace HOMEASSISTANT_URL with your HA URL\n"
printf "  Replace HOMEASSISTANT_TOKEN with your token\n"
printf "  (Generate token in HA: Profile > Security > Long-lived tokens)\n"
printf "\n"
if [ "$OS" = "Linux" ]; then
    printf "${BLUE}Prefer the official CLI?${NC} Claude Code also works on Linux (paid plan):\n"
    printf "  curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh -s -- --claude-code\n"
    printf "\n"
fi
