#!/bin/sh
set -eu

# Port Zero installer bootstrap.
#
# Usage: curl -fsSL https://portzero.net/install.sh | sh
#
# This is a thin bootstrap. It detects your platform and hands off to the right
# full installer so you get the COMPLETE local experience — not just a bare CLI:
#   * Linux   -> downloads and runs the official linux-install.sh release asset
#               (installs portzero + tray + desktop app, grants CAP_NET_ADMIN,
#               installs the local CA into your trust store, sets up systemd +
#               polkit, and starts the daemon so *.portzero.local resolves in
#               the browser).
#   * macOS   -> Homebrew when available, otherwise a tarball install followed
#               by the required `sudo portzero setup` step.
#   * Windows -> points you at the signed .msi on GitHub Releases.
#
# Environment variables (kept stable for backward compatibility):
#   PORT_ZERO_INSTALL_DIR  - Override the install directory.
#   PORT_ZERO_VERSION      - Install a specific version (e.g. "v0.1.0").
#   PORT_ZERO_CHANNEL      - Install channel: latest (default) or staging.
#
# ---------------------------------------------------------------------------
# INVARIANT: this file is mirrored BYTE-IDENTICALLY at two paths in the repo:
#   * cloud/landing/install.sh              (source of truth; run by e2e tests)
#   * cloud/landing/blog/public/install.sh  (served at https://portzero.net/install.sh)
# Edit BOTH together and keep them identical (verify with `cmp` / `diff`).
# ---------------------------------------------------------------------------

REPO="PortZeroNetwork/portzero"
GITHUB_RELEASE_BASE_URL="https://github.com/${REPO}/releases"
RELEASES_CDN_BASE_URL="${PORT_ZERO_RELEASES_CDN_BASE_URL:-https://releases.portzero.cloud/releases}"
STAGING_DOWNLOAD_BASE_URL="${PORT_ZERO_STAGING_DOWNLOAD_BASE_URL:-}"
HOMEBREW_TAP="PortZeroNetwork/portzero"

# Globals populated by the detection helpers below.
OS=""
ARCH=""
DL_BASE=""
DL_FALLBACK=""

# --- Colors (only when connected to a terminal) ---

setup_colors() {
    if [ -t 1 ] && [ -t 2 ]; then
        RED='\033[0;31m'
        GREEN='\033[0;32m'
        YELLOW='\033[0;33m'
        BLUE='\033[0;34m'
        BOLD='\033[1m'
        RESET='\033[0m'
    else
        RED=''
        GREEN=''
        YELLOW=''
        BLUE=''
        BOLD=''
        RESET=''
    fi
}

info()    { printf "${BLUE}info:${RESET} %s\n" "$1"; }
warn()    { printf "${YELLOW}warn:${RESET} %s\n" "$1" >&2; }
error()   { printf "${RED}error:${RESET} %s\n" "$1" >&2; }
success() { printf "${GREEN}${BOLD}%s${RESET}\n" "$1"; }

# --- Small helpers ---

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

download() {
    _url="$1"
    _dest="$2"

    if has_cmd curl; then
        curl -fsSL --retry 3 --retry-delay 2 -o "$_dest" "$_url"
    elif has_cmd wget; then
        wget -q --tries=3 -O "$_dest" "$_url"
    else
        error "Neither curl nor wget was found."
        echo "Install one of them (e.g. 'apt-get install curl') and re-run this command." >&2
        exit 1
    fi
}

download_with_fallback() {
    _url="$1"
    _fallback_url="$2"
    _dest="$3"

    if download "$_url" "$_dest"; then
        return 0
    fi

    if [ -n "$_fallback_url" ]; then
        warn "Primary download failed; trying fallback source."
        info "Downloading ${_fallback_url}"
        download "$_fallback_url" "$_dest"
        return $?
    fi

    return 1
}

# --- Platform detection ---

refuse_windows() {
    error "Windows is not supported by this shell installer."
    echo "" >&2
    echo "Install Port Zero on Windows with the signed MSI instead:" >&2
    echo "  1. Open ${GITHUB_RELEASE_BASE_URL}/latest" >&2
    echo "  2. Download the 'portzero-<version>-x86_64.msi' asset" >&2
    echo "  3. Run it and follow the prompts" >&2
    echo "" >&2
    echo "The MSI sets up the daemon and local name resolution automatically." >&2
    exit 1
}

refuse_unsupported_os() {
    error "Unsupported operating system: $(uname -s)"
    echo "" >&2
    echo "Port Zero ships installers for Linux, macOS, and Windows." >&2
    echo "Browse the available downloads at:" >&2
    echo "  ${GITHUB_RELEASE_BASE_URL}" >&2
    exit 1
}

detect_os() {
    case "$(uname -s)" in
        Linux*)               OS="linux" ;;
        Darwin*)              OS="macos" ;;
        MINGW*|MSYS*|CYGWIN*) refuse_windows ;;
        *)                    refuse_unsupported_os ;;
    esac
}

detect_arch() {
    case "$(uname -m)" in
        x86_64|amd64)  ARCH="x86_64" ;;
        aarch64|arm64) ARCH="aarch64" ;;
        *)
            error "Unsupported architecture: $(uname -m)"
            echo "" >&2
            echo "Port Zero supports x86_64 and aarch64/arm64." >&2
            echo "Check ${GITHUB_RELEASE_BASE_URL} for available binaries." >&2
            exit 1
            ;;
    esac
}

# Map OS + arch to the release artifact stem used by portzero.
release_asset_stem() {
    case "${OS}/${ARCH}" in
        macos/x86_64)  echo "portzero-darwin-amd64" ;;
        macos/aarch64) echo "portzero-darwin-arm64" ;;
        linux/x86_64)  echo "portzero-linux-amd64" ;;
        linux/aarch64) echo "portzero-linux-arm64" ;;
    esac
}

# --- Channel / version resolution ---

# Validate the requested channel early so bad input fails loud and clear.
validate_channel() {
    case "${PORT_ZERO_CHANNEL:-latest}" in
        latest|staging) ;;
        *)
            error "Unsupported install channel: '${PORT_ZERO_CHANNEL:-}'"
            echo "Supported channels: latest, staging" >&2
            echo "Example: PORT_ZERO_CHANNEL=staging curl -fsSL https://portzero.net/install.sh | sh" >&2
            exit 1
            ;;
    esac
}

# A human-friendly label for the version/channel being installed.
channel_label() {
    if [ -n "${PORT_ZERO_VERSION:-}" ]; then
        echo "${PORT_ZERO_VERSION}"
    elif [ "${PORT_ZERO_CHANNEL:-latest}" = "staging" ]; then
        echo "latest unstable build"
    else
        echo "latest stable"
    fi
}

# Set DL_BASE and DL_FALLBACK to the release download bases for the macOS
# tarball path (the full Linux installer resolves its own download base).
resolve_download_base() {
    if [ -n "${PORT_ZERO_VERSION:-}" ]; then
        DL_BASE="${GITHUB_RELEASE_BASE_URL}/download/${PORT_ZERO_VERSION}"
        DL_FALLBACK="${RELEASES_CDN_BASE_URL}/${PORT_ZERO_VERSION}"
        return
    fi
    case "${PORT_ZERO_CHANNEL:-latest}" in
        latest)
            DL_BASE="${GITHUB_RELEASE_BASE_URL}/latest/download"
            DL_FALLBACK="${RELEASES_CDN_BASE_URL}/latest"
            ;;
        staging)
            if [ -n "$STAGING_DOWNLOAD_BASE_URL" ]; then
                DL_BASE="$STAGING_DOWNLOAD_BASE_URL"
                DL_FALLBACK="${GITHUB_RELEASE_BASE_URL}/latest/download"
            else
                warn "No staging download source configured; using the latest GitHub release."
                DL_BASE="${GITHUB_RELEASE_BASE_URL}/latest/download"
                DL_FALLBACK="${RELEASES_CDN_BASE_URL}/staging/latest"
            fi
            ;;
    esac
}

# Translate the public PORT_ZERO_* variables into the PORTZERO_* names the full
# Linux installer expects, then export them for the handoff.
translate_linux_env() {
    if [ -n "${PORT_ZERO_INSTALL_DIR:-}" ]; then
        PORTZERO_INSTALL_DIR="$PORT_ZERO_INSTALL_DIR"
        export PORTZERO_INSTALL_DIR
    fi

    if [ -n "${PORT_ZERO_VERSION:-}" ]; then
        # The full installer only honors an explicit version pin on its
        # prerelease channel, where it downloads that exact tag — which works
        # for stable and unstable tags alike.
        PORTZERO_VERSION="$PORT_ZERO_VERSION"
        PORTZERO_CHANNEL="prerelease"
        export PORTZERO_VERSION PORTZERO_CHANNEL
    else
        case "${PORT_ZERO_CHANNEL:-latest}" in
            latest)  PORTZERO_CHANNEL="stable" ;;
            staging) PORTZERO_CHANNEL="prerelease" ;;
        esac
        export PORTZERO_CHANNEL
    fi
}

# --- Linux: fetch and run the full installer ---

linux_installer_url() {
    # The linux-install.sh asset is identical across releases, so for the
    # latest/staging channels we always fetch the latest published copy and let
    # PORTZERO_CHANNEL (set by translate_linux_env) decide which binary it
    # installs. A pinned version fetches that release's copy.
    if [ -n "${PORT_ZERO_VERSION:-}" ]; then
        echo "${GITHUB_RELEASE_BASE_URL}/download/${PORT_ZERO_VERSION}/linux-install.sh"
    else
        echo "${GITHUB_RELEASE_BASE_URL}/latest/download/linux-install.sh"
    fi
}

install_linux() {
    installer_url=$(linux_installer_url)

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT
    installer="${tmpdir}/linux-install.sh"

    info "Fetching the full Port Zero installer ($(channel_label))..."
    info "  ${installer_url}"
    if ! download "$installer_url" "$installer"; then
        error "Could not download the full Linux installer."
        echo "" >&2
        echo "Possible causes:" >&2
        echo "  - No internet connection, or a firewall blocking github.com" >&2
        echo "  - The requested version does not exist" >&2
        echo "" >&2
        echo "Check available releases at:" >&2
        echo "  ${GITHUB_RELEASE_BASE_URL}" >&2
        exit 1
    fi
    if [ ! -s "$installer" ]; then
        error "The downloaded installer was empty — refusing to run it."
        echo "Try again in a moment, or download it manually from:" >&2
        echo "  ${GITHUB_RELEASE_BASE_URL}" >&2
        exit 1
    fi

    translate_linux_env
    info "Handing off to the full installer (setcap, local CA trust, systemd, tray, daemon start)..."
    echo ""
    sh "$installer"
}

# --- macOS: Homebrew or tarball ---

print_macos_setup_block() {
    echo ""
    echo "  ${BOLD}sudo portzero setup${RESET}"
    echo ""
    echo "Why: this installs the local CA into your login keychain and configures"
    echo "a DNS resolver so https://*.portzero.local works in your browser."
    echo "Without it, local tunnel hostnames will NOT resolve."
    echo ""
    echo "Run it now, then try the commands below."
}

install_macos_brew() {
    info "Homebrew detected — installing the Port Zero tap and formula."
    echo ""
    echo "  brew tap ${HOMEBREW_TAP} && brew install portzero"
    echo ""
    # 'brew tap' is idempotent (a no-op if already tapped).
    brew tap "${HOMEBREW_TAP}"
    brew install portzero
    echo ""
    success "portzero installed via Homebrew!"
    echo ""
    warn "One required step remains for *.portzero.local to resolve:"
    print_macos_setup_block
}

determine_install_dir() {
    if [ -n "${PORT_ZERO_INSTALL_DIR:-}" ]; then
        echo "$PORT_ZERO_INSTALL_DIR"
    elif [ -d /usr/local/bin ] && [ -w /usr/local/bin ]; then
        echo "/usr/local/bin"
    else
        echo "${HOME}/.local/bin"
    fi
}

install_binary() {
    _src="$1"
    _name="$2"
    _install_dir="$3"

    if [ ! -f "$_src" ]; then
        error "Expected binary not found in archive: ${_name}"
        echo "" >&2
        echo "The archive layout may have changed. Please report this at:" >&2
        echo "  https://github.com/${REPO}/issues" >&2
        exit 1
    fi

    rm -f "${_install_dir}/${_name}" 2>/dev/null || true
    if ! cp "$_src" "${_install_dir}/${_name}" 2>/dev/null; then
        error "Permission denied: cannot write to ${_install_dir}"
        echo "" >&2
        echo "Options:" >&2
        echo "  1. Set a writable directory:  PORT_ZERO_INSTALL_DIR=\$HOME/.local/bin curl -fsSL https://portzero.net/install.sh | sh" >&2
        echo "  2. Re-run with sudo:          curl -fsSL https://portzero.net/install.sh | sudo sh" >&2
        exit 1
    fi

    chmod +x "${_install_dir}/${_name}"
    info "Installed ${_name} to ${_install_dir}/${_name}"
}

install_macos_tarball() {
    warn "Homebrew was not found; falling back to a direct binary install."
    detect_arch
    asset_stem=$(release_asset_stem)
    info "Platform: ${OS}/${ARCH} (${asset_stem})"

    resolve_download_base
    archive="${asset_stem}.tar.gz"
    url="${DL_BASE}/${archive}"
    fallback_url=""
    if [ -n "$DL_FALLBACK" ]; then
        fallback_url="${DL_FALLBACK}/${archive}"
    fi
    info "Downloading ${url}"

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    if ! download_with_fallback "$url" "$fallback_url" "${tmpdir}/${archive}"; then
        error "Download failed."
        echo "" >&2
        echo "Possible causes:" >&2
        echo "  - No internet connection or a firewall blocking the download" >&2
        echo "  - The requested version does not exist" >&2
        echo "" >&2
        echo "Check available releases at:" >&2
        echo "  ${GITHUB_RELEASE_BASE_URL}" >&2
        exit 1
    fi

    info "Extracting archive"
    tar xzf "${tmpdir}/${archive}" -C "$tmpdir"

    install_dir=$(determine_install_dir)
    if [ ! -d "$install_dir" ]; then
        info "Creating ${install_dir}"
        mkdir -p "$install_dir"
    fi
    install_binary "${tmpdir}/${asset_stem}/portzero" "portzero" "$install_dir"

    case ":${PATH}:" in
        *":${install_dir}:"*) ;;
        *)
            warn "${install_dir} is not in your PATH."
            echo "" >&2
            echo "Add it by appending this line to your shell profile (~/.zshrc, ~/.bashrc):" >&2
            echo "" >&2
            echo "  export PATH=\"${install_dir}:\$PATH\"" >&2
            echo "" >&2
            ;;
    esac

    echo ""
    success "portzero installed to ${install_dir}!"
    echo ""
    error "REQUIRED next step — the CLI alone cannot resolve *.portzero.local yet:"
    print_macos_setup_block
}

install_macos() {
    if has_cmd brew; then
        install_macos_brew
    else
        install_macos_tarball
    fi
}

# --- Shared epilogue (all platforms) ---

find_installed_portzero() {
    if command -v portzero >/dev/null 2>&1; then
        command -v portzero
        return 0
    fi
    for candidate in "${PORT_ZERO_INSTALL_DIR:-}/portzero" /usr/local/bin/portzero "${HOME}/.local/bin/portzero"; do
        if [ -x "$candidate" ]; then
            echo "$candidate"
            return 0
        fi
    done
    return 1
}

print_epilogue() {
    echo ""
    info "Local tunnels (*.portzero.local) are free, open-source software (GNU GPL v3.0):"
    info "  https://github.com/${REPO}/blob/staging/LICENSE"
    info "Cloud features governed by https://portzero.net/terms"
    echo ""
    echo "Next steps:"
    # Older releases predate `portzero demo`; only advertise it when the
    # installed binary actually supports it.
    if pz_bin="$(find_installed_portzero)" && "$pz_bin" demo --help >/dev/null 2>&1; then
        echo "  ${BOLD}portzero demo${RESET}      Spin up a sample local tunnel and open it in your browser"
    else
        echo "  ${BOLD}PZ_TUNNEL=web.myapp.portzero.local:80 <your dev command>${RESET}"
        echo "                     Give any process a stable local URL"
    fi
    echo "  ${BOLD}portzero status${RESET}    Show the daemon and active tunnels"
    echo "  ${BOLD}portzero login${RESET}     Log in for cloud tunnels (*.tunnel.portzero.cloud)"
    echo ""
}

# --- Main ---

main() {
    setup_colors
    validate_channel

    printf '%s\n\n' "${BOLD}Port Zero installer${RESET}"

    detect_os
    case "$OS" in
        linux) install_linux ;;
        macos) install_macos ;;
    esac

    print_epilogue
}

main "$@"
