#!/usr/bin/env bash
set -euo pipefail

# Public install endpoint (cli.dodil.io serves the binaries baked into the image).
# Override with DODIL_CLI_BASE_URL for testing against a PR image / local proxy.
DOWNLOAD_REPO_URL="${DODIL_CLI_BASE_URL:-https://cli.dodil.io}"

# Release signing public key. The matching private key lives in the
# DODIL_SIGNING_KEY GitHub Actions secret and never leaves CI. To rotate:
# generate a new ECDSA P-256 keypair, update the secret, and replace this
# block. Old installs continue to verify against whatever key they shipped
# with.
DODIL_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdut3HXOt7WOQTY1H+EWdw8I1xCBj
bFSVZEXutrPtJVjJIKFEUp0ylB5lO3Q6RAkPXvXbBxu/70Ly08TKFVFu6Q==
-----END PUBLIC KEY-----"

info() {
    echo -e "\033[34m==>\033[0m $1"
}

error() {
    echo -e "\033[31m==> ERROR:\033[0m $1" >&2
    exit 1
}

download() {
    local url="$1"
    local dest="$2"
    if command -v curl >/dev/null 2>&1; then
        curl -fSL -o "${dest}" "${url}" || error "Download failed: ${url}"
    elif command -v wget >/dev/null 2>&1; then
        wget -qO "${dest}" "${url}" || error "Download failed: ${url}"
    else
        error "Neither curl nor wget was found. Please install one of them to proceed."
    fi
}

command -v openssl >/dev/null 2>&1 || error "openssl is required to verify the release signature."

# Pick a sha256 tool (sha256sum on Linux, shasum on macOS)
if command -v sha256sum >/dev/null 2>&1; then
    sha256_of() { sha256sum "$1" | awk '{print $1}'; }
elif command -v shasum >/dev/null 2>&1; then
    sha256_of() { shasum -a 256 "$1" | awk '{print $1}'; }
else
    error "Neither sha256sum nor shasum was found. Cannot verify checksum."
fi

# Determine OS
OS="$(uname -s)"
case "${OS}" in
    Linux*)     OS_NAME="linux";;
    Darwin*)    OS_NAME="darwin";;
    *)          error "Unsupported OS: ${OS}";;
esac

# Determine Architecture
ARCH="$(uname -m)"
case "${ARCH}" in
    x86_64|amd64) ARCH_NAME="amd64";;
    aarch64|arm64) ARCH_NAME="arm64";;
    i386|i686)    ARCH_NAME="386";;
    *)            error "Unsupported architecture: ${ARCH}";;
esac

info "Detected OS: ${OS_NAME}"
info "Detected Architecture: ${ARCH_NAME}"

# Construct the binary name
BIN_NAME="dodil-${OS_NAME}-${ARCH_NAME}"
if [ "${OS_NAME}" = "windows" ]; then
    BIN_NAME="${BIN_NAME}.exe"
fi

BIN_URL="${DOWNLOAD_REPO_URL}/${BIN_NAME}"

INSTALL_DIR="${HOME}/.local/bin"
EXE_PATH="${INSTALL_DIR}/dodil"
mkdir -p "${INSTALL_DIR}"

TMP="$(mktemp -d)"
trap 'rm -rf "${TMP}"' EXIT

info "Downloading ${BIN_NAME}..."
download "${BIN_URL}" "${TMP}/${BIN_NAME}"

info "Downloading SHA256SUMS + signature..."
download "${DOWNLOAD_REPO_URL}/SHA256SUMS"     "${TMP}/SHA256SUMS"
download "${DOWNLOAD_REPO_URL}/SHA256SUMS.sig" "${TMP}/SHA256SUMS.sig"

# Verify the signature on SHA256SUMS using the embedded public key.
# This is the trust anchor — every other check derives from it.
echo "${DODIL_PUBLIC_KEY}" > "${TMP}/dodil.pub"
if ! openssl dgst -sha256 -verify "${TMP}/dodil.pub" \
        -signature "${TMP}/SHA256SUMS.sig" \
        "${TMP}/SHA256SUMS" >/dev/null 2>&1; then
    error "Signature verification FAILED for SHA256SUMS. Refusing to install — the release artifacts may be tampered or corrupted."
fi
info "Signature verified."

# Extract the expected hash for our binary from the signed SHA256SUMS file.
EXPECTED="$(awk -v f="${BIN_NAME}" '$2==f {print $1}' "${TMP}/SHA256SUMS")"
if [ -z "${EXPECTED}" ]; then
    error "No checksum entry for ${BIN_NAME} in SHA256SUMS."
fi

ACTUAL="$(sha256_of "${TMP}/${BIN_NAME}")"
if [ "${EXPECTED}" != "${ACTUAL}" ]; then
    error "Checksum mismatch for ${BIN_NAME} (expected ${EXPECTED}, got ${ACTUAL})."
fi
info "Checksum verified (sha256: ${ACTUAL})."

mv "${TMP}/${BIN_NAME}" "${EXE_PATH}"
chmod +x "${EXE_PATH}"

info "Successfully installed dodil to ${EXE_PATH}"

# ----------------------------------------------------------------------------
# Best-effort post-install: shell completions + MCP config for local AI agents.
# Neither must ever fail a successful binary install, so errexit is relaxed for
# the remainder of the script. Opt out of MCP wiring with DODIL_SKIP_MCP_SETUP=1
# and completions with DODIL_SKIP_COMPLETIONS=1.
# ----------------------------------------------------------------------------
set +e

install_completions() {
    [ -n "${DODIL_SKIP_COMPLETIONS:-}" ] && return 0
    info "Installing shell completions..."

    # bash: the XDG user dir is sourced automatically by bash-completion.
    if command -v bash >/dev/null 2>&1; then
        local bdir="${XDG_DATA_HOME:-${HOME}/.local/share}/bash-completion/completions"
        if mkdir -p "${bdir}" 2>/dev/null && "${EXE_PATH}" completion bash >"${bdir}/dodil" 2>/dev/null; then
            info "  bash -> ${bdir}/dodil"
        fi
    fi

    # zsh: install to a user fpath dir and make sure .zshrc references it.
    if command -v zsh >/dev/null 2>&1; then
        local zdir="${HOME}/.zsh/completions"
        if mkdir -p "${zdir}" 2>/dev/null && "${EXE_PATH}" completion zsh >"${zdir}/_dodil" 2>/dev/null; then
            info "  zsh  -> ${zdir}/_dodil"
            local zrc="${HOME}/.zshrc"
            if [ -f "${zrc}" ] && ! grep -q '# dodil completions' "${zrc}" 2>/dev/null; then
                {
                    echo ""
                    echo "# dodil completions"
                    echo "fpath=(${zdir} \$fpath)"
                    echo "autoload -Uz compinit && compinit"
                } >>"${zrc}" && info "  zsh  -> registered fpath in ${zrc}"
            fi
        fi
    fi

    # fish: dropped into the auto-loaded completions dir.
    if command -v fish >/dev/null 2>&1; then
        local fdir="${HOME}/.config/fish/completions"
        if mkdir -p "${fdir}" 2>/dev/null && "${EXE_PATH}" completion fish >"${fdir}/dodil.fish" 2>/dev/null; then
            info "  fish -> ${fdir}/dodil.fish"
        fi
    fi
}

setup_mcp_agents() {
    [ -n "${DODIL_SKIP_MCP_SETUP:-}" ] && { info "Skipping MCP agent setup (DODIL_SKIP_MCP_SETUP set)."; return 0; }
    info "Registering dodil as an MCP server for detected AI agents..."

    # JSON-config agents share the de-facto {"mcpServers": {...}} schema. We use
    # python3 to merge our entry in place, preserving existing content and
    # backing the file up first. Without python3 we still handle the CLI/TOML
    # agents below.
    local have_py=0
    if command -v python3 >/dev/null 2>&1; then
        have_py=1
        cat >"${TMP}/dodil_mcp_merge.py" <<'PY'
import json, os, shutil, sys
path, name, cmd = sys.argv[1], sys.argv[2], sys.argv[3]
data = {}
if os.path.exists(path) and os.path.getsize(path) > 0:
    try:
        with open(path) as f:
            data = json.load(f)
    except Exception:
        shutil.copy2(path, path + ".dodil.bak")
        data = {}
if not isinstance(data, dict):
    data = {}
servers = data.get("mcpServers")
if not isinstance(servers, dict):
    servers = {}
    data["mcpServers"] = servers
mcp_args = sys.argv[4:] if len(sys.argv) > 4 else ["mcp"]
servers[name] = {"command": cmd, "args": mcp_args}
d = os.path.dirname(path)
if d:
    os.makedirs(d, exist_ok=True)
if os.path.exists(path) and not os.path.exists(path + ".dodil.bak"):
    shutil.copy2(path, path + ".dodil.bak")
tmp = path + ".dodil.tmp"
with open(tmp, "w") as f:
    json.dump(data, f, indent=2)
    f.write("\n")
os.replace(tmp, path)
PY
    else
        info "  python3 not found; skipping JSON-config agents."
    fi

    # Args the registered MCP server is launched with. Code mode (the dodil_exec
    # tool) is enabled by default so agents can compose CLI calls and run mounts;
    # set DODIL_MCP_NO_EXEC=1 to register a plain (exec-less) server instead.
    local mcp_args=(mcp)
    if [ -z "${DODIL_MCP_NO_EXEC:-}" ]; then
        mcp_args+=(--enable-exec)
    fi

    # merge_json <display-name> <marker-dir> <config-file>
    # Only touches an agent whose directory already exists (i.e. it is installed).
    merge_json() {
        local name="$1" dir="$2" file="$3"
        [ "${have_py}" = "1" ] || return 0
        [ -d "${dir}" ] || return 0
        if python3 "${TMP}/dodil_mcp_merge.py" "${file}" dodil "${EXE_PATH}" "${mcp_args[@]}" 2>/dev/null; then
            info "  ${name}: ${file}"
        fi
    }

    # --- Claude Code: prefer the official CLI, fall back to ~/.claude.json ---
    if command -v claude >/dev/null 2>&1; then
        if claude mcp get dodil >/dev/null 2>&1; then
            info "  Claude Code: already configured"
        elif claude mcp add --scope user dodil -- "${EXE_PATH}" "${mcp_args[@]}" >/dev/null 2>&1; then
            info "  Claude Code: added via 'claude mcp add'"
        fi
    elif [ "${have_py}" = "1" ] && [ -f "${HOME}/.claude.json" ]; then
        python3 "${TMP}/dodil_mcp_merge.py" "${HOME}/.claude.json" dodil "${EXE_PATH}" "${mcp_args[@]}" 2>/dev/null \
            && info "  Claude Code: ~/.claude.json"
    fi

    # --- Agents using the {"mcpServers": {...}} schema ---
    merge_json "Cursor"          "${HOME}/.cursor"                  "${HOME}/.cursor/mcp.json"
    merge_json "Windsurf"        "${HOME}/.codeium/windsurf"        "${HOME}/.codeium/windsurf/mcp_config.json"
    merge_json "Gemini CLI"      "${HOME}/.gemini"                  "${HOME}/.gemini/settings.json"
    # Google Antigravity (IDE + CLI) shares the ~/.gemini dir with the Gemini CLI
    # but reads MCP servers from a dedicated ~/.gemini/config/mcp_config.json.
    merge_json "Antigravity"     "${HOME}/.gemini"                  "${HOME}/.gemini/config/mcp_config.json"
    merge_json "Claude Desktop"  "${HOME}/Library/Application Support/Claude" "${HOME}/Library/Application Support/Claude/claude_desktop_config.json"
    merge_json "Claude Desktop"  "${HOME}/.config/Claude"          "${HOME}/.config/Claude/claude_desktop_config.json"
    # MiniMax Mini-Agent (official MiniMax-AI): MCP servers live in a dedicated
    # mcp.json beside its config.yaml. Note the agent also needs `enable_mcp: true`
    # set in ~/.mini-agent/config/config.yaml for these servers to be loaded.
    merge_json "MiniMax Mini-Agent" "${HOME}/.mini-agent/config" "${HOME}/.mini-agent/config/mcp.json"
    # Kimi Code (Moonshot): user-level MCP config in the {"mcpServers": ...} schema.
    merge_json "Kimi Code"       "${HOME}/.kimi-code"               "${HOME}/.kimi-code/mcp.json"

    # --- Codex CLI: TOML config, append a guarded block ---
    if [ -d "${HOME}/.codex" ]; then
        local cfg="${HOME}/.codex/config.toml"
        # Render mcp_args as a TOML array: ["mcp", "--enable-exec"]
        local toml_args="" a
        for a in "${mcp_args[@]}"; do toml_args="${toml_args}\"${a}\", "; done
        toml_args="[${toml_args%, }]"
        if [ -f "${cfg}" ] && grep -q '^\[mcp_servers\.dodil\]' "${cfg}" 2>/dev/null; then
            info "  Codex: already configured"
        elif {
                printf '\n[mcp_servers.dodil]\n'
                printf 'command = "%s"\n' "${EXE_PATH}"
                printf 'args = %s\n' "${toml_args}"
            } >>"${cfg}" 2>/dev/null; then
            info "  Codex: ${cfg}"
        fi
    fi

    info "  Code mode (dodil_exec) is enabled in the registered server; set DODIL_MCP_NO_EXEC=1 to disable it."
    info "  (set DODIL_SKIP_MCP_SETUP=1 to skip this next time; backups saved as *.dodil.bak)"
}

# Optional login. Controlled by DODIL_LOGIN (web | service-account | skip); when
# unset we only prompt on an interactive terminal (so piped `curl | bash` stays
# non-interactive unless DODIL_LOGIN is exported). Service-account credentials
# come from DODIL_SERVICE_ACCOUNT_ID/SECRET or an interactive prompt.
maybe_login() {
    local choice="${DODIL_LOGIN:-}"
    if [ -z "${choice}" ]; then
        if ! [ -t 0 ]; then
            choice="web"  # non-interactive (e.g. curl ... | bash) -> default to web login
        fi
        echo ""
        printf '%b' "\033[34m==>\033[0m Log in to dodil now? [w]eb / [s]ervice-account / [N]o: "
        local ans=""
        read -r ans || return 0
        case "${ans}" in
            w|W|web)              choice="web" ;;
            s|S|service-account)  choice="service-account" ;;
            *)                    return 0 ;;
        esac
    fi

    case "${choice}" in
        skip|none|no|"")
            return 0 ;;
        web)
            info "Starting browser/device login..."
            "${EXE_PATH}" auth login --web ;;
        service-account|sa)
            local said="${DODIL_SERVICE_ACCOUNT_ID:-}"
            local sasecret="${DODIL_SERVICE_ACCOUNT_SECRET:-}"
            if [ -z "${said}" ] && [ -t 0 ]; then
                printf '%b' "\033[34m==>\033[0m Service account ID: "
                read -r said
            fi
            if [ -z "${sasecret}" ] && [ -t 0 ]; then
                printf '%b' "\033[34m==>\033[0m Service account secret: "
                read -rs sasecret; echo ""
            fi
            if [ -n "${said}" ] && [ -n "${sasecret}" ]; then
                info "Logging in with service account..."
                "${EXE_PATH}" auth login --service-account-id "${said}" --service-account-secret "${sasecret}"
            else
                info "Skipping service-account login (missing id/secret; set DODIL_SERVICE_ACCOUNT_ID/SECRET)."
            fi ;;
        *)
            info "Unknown DODIL_LOGIN value '${choice}' (use web | service-account | skip); skipping login." ;;
    esac
}

# print_path_copypaste renders a loud, copy-paste-able set of steps that gets
# `dodil` onto PATH in the CURRENT shell with a single pasted line (append +
# source) — no editor, no hunting for the right dotfile. Used when we can't
# auto-edit the rc, or the user opted out. $1 = display path of the shell rc.
print_path_copypaste() {
    local rc_disp="$1"
    echo ""
    echo -e "\033[1;33m╔══════════════════════════════════════════════════════════════╗\033[0m"
    echo -e "\033[1;33m║  ⚠  ONE MORE STEP — do this so 'dodil' works                 ║\033[0m"
    echo -e "\033[1;33m╚══════════════════════════════════════════════════════════════╝\033[0m"
    echo ""
    echo -e "  \033[1mCopy–paste this line into your terminal and press Enter:\033[0m"
    echo ""
    echo -e "\033[1;36m    echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ${rc_disp} && source ${rc_disp}\033[0m"
    echo ""
    echo "  Then confirm it worked:"
    echo -e "\033[1;36m    dodil --help\033[0m"
    echo ""
    echo -e "\033[1;33m──────────────────────────────────────────────────────────────\033[0m"
    echo ""
}

# Put INSTALL_DIR on PATH so a non-technical user never hits `dodil: command not
# found`. We try to append a guarded export to the login shell's rc; whether that
# works or not, we finish with a loud, copy-paste-able instruction (one line that
# both adds it and loads it into the current shell) — never "go edit a file".
# Idempotent; opt out of the auto-edit with DODIL_SKIP_PATH_SETUP=1.
ensure_on_path() {
    # Already reachable? Nothing to do.
    case ":$PATH:" in
        *":${INSTALL_DIR}:"*) return 0 ;;
    esac

    local line='export PATH="$HOME/.local/bin:$PATH"'

    # Pick the rc for the user's login shell; build a ~-relative display form.
    local rc
    case "$(basename "${SHELL:-}")" in
        zsh)  rc="${ZDOTDIR:-$HOME}/.zshrc" ;;
        bash) rc="${HOME}/.bashrc" ;;
        *)    rc="${HOME}/.profile" ;;
    esac

    # Opted out of auto-edit → just show the copy-paste steps.
    if [ -n "${DODIL_SKIP_PATH_SETUP:-}" ]; then
        print_path_copypaste "${rc}"
        return 0
    fi

    local wrote=""
    if [ -n "${rc}" ]; then
        [ -f "${rc}" ] || touch "${rc}" 2>/dev/null || true
        if [ -f "${rc}" ]; then
            if grep -q '# dodil PATH' "${rc}" 2>/dev/null; then
                wrote="${rc}"   # block already present from a prior install
            elif { echo ""; echo "# dodil PATH"; echo "${line}"; } >>"${rc}" 2>/dev/null; then
                wrote="${rc}"
            fi
        fi
    fi

    if [ -n "${wrote}" ]; then
        # Auto-edit succeeded — new terminals are set. Still give the ONE line to
        # activate it in THIS terminal, loud and copy-paste-able.
        echo ""
        echo -e "\033[1;32m╔══════════════════════════════════════════════════════════════╗\033[0m"
        echo -e "\033[1;32m║  ✓  Added 'dodil' to your PATH\033[0m"
        echo -e "\033[1;32m╚══════════════════════════════════════════════════════════════╝\033[0m"
        echo ""
        echo -e "  \033[1mTo use it in THIS terminal right now, copy–paste:\033[0m"
        echo ""
        echo -e "\033[1;36m    source ${rc}\033[0m"
        echo ""
        echo "  (A new terminal already has it.)  Skip next time: DODIL_SKIP_PATH_SETUP=1"
        echo ""
    else
        # Couldn't edit the rc — hand over the copy-paste steps.
        print_path_copypaste "${rc}"
    fi
}

install_completions
setup_mcp_agents
maybe_login
ensure_on_path

info "Run 'dodil --help' to get started."
