#!/bin/bash
# XPyCode macOS .pkg Post-Install Script
# Runs as root after the payload has been laid down.
# Mirrors the logic of installer/windows/innosetup/xpycode_postinstall.bat
# and the Pascal CurStepChanged(ssPostInstall) block in xpycode.iss.

set -uo pipefail

INSTALL_DIR="/Applications/XPyCode"
PLIST_LABEL="com.xpycode.master"
LAUNCH_ARGS="--log-level INFO"
export BROWSER=false

echo "============================================================"
echo "  XPyCode Post-Install"
echo "============================================================"
echo "  Install dir : ${INSTALL_DIR}"
echo "============================================================"
echo ""

# ── Resolve the real user (not root) ─────────────────────────────────────────
if [ -n "${USER:-}" ] && [ "${USER}" != "root" ]; then
    REAL_USER="${USER}"
elif [ -n "${SUDO_USER:-}" ]; then
    REAL_USER="${SUDO_USER}"
else
    REAL_USER=$(stat -f "%Su" /dev/console 2>/dev/null || echo "")
fi

run_as_user() {
    if [ -n "${REAL_USER}" ] && [ "${REAL_USER}" != "root" ]; then
        sudo -H -u "${REAL_USER}" "$@"
    else
        "$@"
    fi
}

user_home() {
    if [ -n "${REAL_USER}" ] && [ "${REAL_USER}" != "root" ]; then
        eval echo "~${REAL_USER}"
    else
        echo "${HOME:-/var/root}"
    fi
}

USER_HOME=$(user_home)
echo "Real user : ${REAL_USER:-<unknown>}"
echo "User home : ${USER_HOME}"
echo ""

# ── Notify the logged-in user via macOS notification banners ─────────────────
# Uses display notification (no Automation permission required).
notify_user() {
    local msg="${1:-Installation in progress...}"
    if [ -n "${REAL_USER}" ] && [ "${REAL_USER}" != "root" ]; then
        sudo -H -u "${REAL_USER}" osascript -e "display notification \"${msg}\" with title \"XPyCode Installer\"" 2>/dev/null || true
    fi
}

# ── Step 0: Configuration ─────────────────────────────────────────────────────
notify_user "Step 0/9: Reading configuration..."
echo "[0/9] Reading configuration from installer choices..."
LOG_LEVEL="INFO"

# Each marker file is installed by its optional component package only when the
# user keeps the corresponding option selected in the Customize panel.
CREATE_LOG_FILE="Yes"
if [ ! -f "${INSTALL_DIR}/.opt_log_file" ]; then
    CREATE_LOG_FILE="No"
fi
rm -f "${INSTALL_DIR}/.opt_log_file"

CREATE_DESKTOP_SHORTCUT="Yes"
if [ ! -f "${INSTALL_DIR}/.opt_desktop_shortcut" ]; then
    CREATE_DESKTOP_SHORTCUT="No"
fi
rm -f "${INSTALL_DIR}/.opt_desktop_shortcut"

AUTOSTART="Yes"
if [ ! -f "${INSTALL_DIR}/.opt_autostart" ]; then
    AUTOSTART="No"
fi
rm -f "${INSTALL_DIR}/.opt_autostart"

LAUNCH_ARGS="--log-level ${LOG_LEVEL}"
if [ "${CREATE_LOG_FILE}" = "No" ]; then
    LAUNCH_ARGS="${LAUNCH_ARGS} --no-log-file"
fi
DESKTOP_SHORTCUT_ENABLED=true
if [ "${CREATE_DESKTOP_SHORTCUT}" = "No" ]; then
    DESKTOP_SHORTCUT_ENABLED=false
fi
AUTOSTART_ENABLED=true
if [ "${AUTOSTART}" = "No" ]; then
    AUTOSTART_ENABLED=false
fi

echo "  Log level        : ${LOG_LEVEL}"
echo "  Create log file  : ${CREATE_LOG_FILE}"
echo "  Desktop shortcut : ${CREATE_DESKTOP_SHORTCUT}"
echo "  Autostart        : ${AUTOSTART}"
echo ""

# ── Step 1: Detect architecture ───────────────────────────────────────────────
notify_user "Step 1/9: Detecting architecture..."
echo "[1/9] Detecting architecture..."
ARCH=$(uname -m)
if [ "${ARCH}" = "arm64" ]; then
    echo "  Architecture: Apple Silicon (arm64)"
else
    echo "  Architecture: Intel (x86_64)"
fi
echo ""

# ── Step 2: Create installation directory ─────────────────────────────────────
notify_user "Step 2/9: Creating installation directory..."
echo "[2/9] Creating installation directory..."
mkdir -p "${INSTALL_DIR}"
echo "  Created: ${INSTALL_DIR}"
echo ""

# ── Step 3: Ensure Python 3.13 is available ───────────────────────────────────
echo "[3/9] Ensuring Python 3.13..."
notify_user "Step 3/9: Checking for Python 3.13..."

PYTHON_VERSION="3.13.2"
PYTHON_MINOR_VERSION="3.13"
PYTHON_FRAMEWORK_EXE="/Library/Frameworks/Python.framework/Versions/${PYTHON_MINOR_VERSION}/bin/python3"

# Helper: return 0 if the given executable is Python 3.13.x
_is_python313() {
    local exe="$1"
    [ -x "${exe}" ] || return 1
    "${exe}" -c "import sys; v=sys.version_info; exit(0 if (v.major==3 and v.minor==13) else 1)" 2>/dev/null
}

PYTHON_EXE=""

# 1. Check standard python.org framework location
if _is_python313 "${PYTHON_FRAMEWORK_EXE}"; then
    PYTHON_EXE="${PYTHON_FRAMEWORK_EXE}"
    echo "  Found existing Python 3.13 at ${PYTHON_EXE} (skipping install)"
fi

# 2. Check python3.13 on PATH
if [ -z "${PYTHON_EXE}" ]; then
    _candidate=$(command -v python3.13 2>/dev/null || true)
    if _is_python313 "${_candidate}"; then
        PYTHON_EXE="${_candidate}"
        echo "  Found existing Python 3.13 on PATH at ${PYTHON_EXE} (skipping install)"
    fi
fi

# 3. Check python3 on PATH (verify version is 3.13.x)
if [ -z "${PYTHON_EXE}" ]; then
    _candidate=$(command -v python3 2>/dev/null || true)
    if _is_python313 "${_candidate}"; then
        PYTHON_EXE="${_candidate}"
        echo "  Found existing Python 3.13 via python3 on PATH at ${PYTHON_EXE} (skipping install)"
    fi
fi

# 4. If still not found, download and install via the standard macOS installer
if [ -z "${PYTHON_EXE}" ]; then
    PYTHON_PKG_URL="https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg"
    PYTHON_PKG_DOWNLOAD="/tmp/python-${PYTHON_VERSION}-macos11.pkg"
    echo "  No suitable Python 3.13 found. Downloading Python ${PYTHON_VERSION}..."
    if curl -fsSL --retry 3 --retry-delay 5 -o "${PYTHON_PKG_DOWNLOAD}" "${PYTHON_PKG_URL}" 2>/dev/null; then
        echo "  Download complete"
    else
        echo "  ERROR: Failed to download Python from ${PYTHON_PKG_URL}"
        exit 1
    fi

    # Create a choices XML to suppress unwanted installer components:
    #   - PythonApplications: don't install IDLE/Python Launcher into /Applications
    #   - PythonProfileChanges: don't modify the user's shell profile (.zprofile etc.)
    #   - PythonDocumentation: keep install minimal
    #   - PythonInstallPip: we handle pip ourselves via ensurepip below
    CHOICES_XML=$(mktemp /tmp/xpycode_python_choices_XXXXXX.xml)
    cat > "${CHOICES_XML}" << 'CHOICESEOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>choiceIdentifier</key>
        <string>org.python.Python.PythonApplications</string>
        <key>choiceAttribute</key>
        <string>selected</string>
        <key>attributeSetting</key>
        <integer>0</integer>
    </dict>
    <dict>
        <key>choiceIdentifier</key>
        <string>org.python.Python.PythonProfileChanges</string>
        <key>choiceAttribute</key>
        <string>selected</string>
        <key>attributeSetting</key>
        <integer>0</integer>
    </dict>
    <dict>
        <key>choiceIdentifier</key>
        <string>org.python.Python.PythonDocumentation</string>
        <key>choiceAttribute</key>
        <string>selected</string>
        <key>attributeSetting</key>
        <integer>0</integer>
    </dict>
    <dict>
        <key>choiceIdentifier</key>
        <string>org.python.Python.PythonInstallPip</string>
        <key>choiceAttribute</key>
        <string>selected</string>
        <key>attributeSetting</key>
        <integer>0</integer>
    </dict>
</array>
</plist>
CHOICESEOF

    echo "  Installing Python ${PYTHON_VERSION} system-wide..."
    if ! installer -applyChoiceChangesXML "${CHOICES_XML}" -pkg "${PYTHON_PKG_DOWNLOAD}" -target /; then
        echo "  ERROR: Python installer failed"
        rm -f "${CHOICES_XML}" "${PYTHON_PKG_DOWNLOAD}"
        exit 1
    fi
    rm -f "${CHOICES_XML}" "${PYTHON_PKG_DOWNLOAD}"

    PYTHON_EXE="${PYTHON_FRAMEWORK_EXE}"
    echo "  Python ${PYTHON_VERSION} installed at ${PYTHON_EXE}"
fi

# Ensure pip is available
if ! "${PYTHON_EXE}" -m pip --version >/dev/null 2>&1; then
    "${PYTHON_EXE}" -m ensurepip --upgrade 2>/dev/null || true
fi

# Verify Python executable
if [ ! -x "${PYTHON_EXE}" ]; then
    echo "ERROR: Python executable not found at ${PYTHON_EXE}"
    exit 1
fi
echo "  Python ready: ${PYTHON_EXE}"
echo ""

# ── Step 4: Upgrade pip + setuptools ─────────────────────────────────────────
notify_user "Step 4/9: Upgrading pip and setuptools..."
echo "[4/9] Upgrading pip and setuptools..."
"${PYTHON_EXE}" -m pip install --upgrade --no-warn-script-location pip setuptools >/dev/null 2>&1
echo "  Done"
echo ""

# ── Step 5: Install xpycode_master ────────────────────────────────────────────
notify_user "Step 5/9: Installing XPyCode packages..."
echo "[5/9] Installing xpycode_master..."
PIP_LOG=$(mktemp /tmp/xpycode_pip_XXXXXX.log)
if "${PYTHON_EXE}" -m pip install --upgrade --no-warn-script-location xpycode_master >"${PIP_LOG}" 2>&1; then
    echo "  Done"
else
    echo "  ERROR: xpycode_master installation failed. Details:"
    cat "${PIP_LOG}" >&2
    rm -f "${PIP_LOG}"
    exit 1
fi
rm -f "${PIP_LOG}"
echo ""

# ── Step 6: Save Python executable path to service config ────────────────────
notify_user "Step 6/9: Saving service configuration..."
echo "[6/9] Saving service configuration..."
run_as_user "${PYTHON_EXE}" -c "from xpycode_master.utils.start_config import set_python_exe; set_python_exe('${PYTHON_EXE}')" \
    && echo "  Python exe path saved" \
    || echo "  WARNING: Could not save service config"
echo ""

# ── Step 7: Generate SSL certificate files as the real user ───────────────────
# Cert files must be owned by the real user (~/.xpycode/certs/ is user-owned).
# We only generate the files here; trust registration is done as root in Step 8.
notify_user "Step 7/9: Generating SSL certificates..."
echo "[7/9] Generating SSL certificates (as ${REAL_USER:-user})..."
run_as_user "${PYTHON_EXE}" -c "
from xpycode_master.addin_launcher.certificate_manager import CertificateManager
cm = CertificateManager()
paths = cm.ensure_certificates()
print(f'  CA cert   : {paths.ca_cert}')
print(f'  Server cert: {paths.server_cert}')
" && echo "  SSL certificate files generated" \
  || echo "  WARNING: Could not generate SSL certificate files"
echo ""

# ── Step 8: Register CA certificate in System keychain (as root) ──────────────
# postinstall runs as root so we can call `security add-trusted-cert` directly —
# no nested sudo and no password prompt required.
notify_user "Step 8/9: Registering CA certificate in System keychain..."
echo "[8/9] Registering CA certificate in System keychain (as root)..."
CA_CERT_PATH="${USER_HOME}/.xpycode/certs/xpycode-ca.crt"
if [ -f "${CA_CERT_PATH}" ]; then
    security add-trusted-cert -d -r trustRoot \
        -k /Library/Keychains/System.keychain \
        "${CA_CERT_PATH}" \
        && echo "  CA certificate trusted in System keychain" \
        || echo "  WARNING: Could not trust CA certificate (can be fixed later with xpycode --setup)"
else
    echo "  WARNING: CA certificate not found at ${CA_CERT_PATH} — skipping trust registration"
fi
echo ""

# ── Step 9: Apply configuration ───────────────────────────────────────────────
# Delegates to xpycode_master --apply-config which handles: SSL certificates
# (idempotent — already trusted above), protocol handler registration, manifest
# registration, desktop shortcut, autostart (launchd plist) and saving start_config.json.
notify_user "Step 9/9: Applying configuration..."
echo "[9/9] Applying configuration..."
NO_LOG_FILE_VAL=$([ "${CREATE_LOG_FILE}" = "No" ] && echo true || echo false)
CONFIG_JSON="{\"log_level\":\"${LOG_LEVEL}\",\"no_log_file\":${NO_LOG_FILE_VAL},\"skip_manifest\":false,\"desktop_shortcut\":${DESKTOP_SHORTCUT_ENABLED},\"autostart\":${AUTOSTART_ENABLED}}"

APPLY_LOG=$(mktemp /tmp/xpycode_apply_XXXXXX.log)
if run_as_user "${PYTHON_EXE}" -m xpycode_master --apply-config "${CONFIG_JSON}" >"${APPLY_LOG}" 2>&1; then
    echo "  Configuration applied successfully"
else
    echo "  WARNING: Configuration apply failed (some settings may need manual setup)"
    cat "${APPLY_LOG}" >&2
fi
rm -f "${APPLY_LOG}"
echo ""

# ── Create XPyCode.app launcher bundle ───────────────────────────────────────
# This is a simple launcher .app (CFBundleIdentifier=com.xpycode.launcher) that
# opens the xpycode:// protocol URL. It is separate from XPyCodeHandler.app
# (com.xpycode.handler) which is the protocol handler registered by watchdog_xpc.
LAUNCHER_APP="${INSTALL_DIR}/XPyCode.app"
mkdir -p "${LAUNCHER_APP}/Contents/MacOS"
mkdir -p "${LAUNCHER_APP}/Contents/Resources"

cat > "${LAUNCHER_APP}/Contents/Info.plist" << 'LAUNCHERPLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>com.xpycode.launcher</string>
    <key>CFBundleName</key>
    <string>XPyCode</string>
    <key>CFBundleExecutable</key>
    <string>XPyCode</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleIconFile</key>
    <string>XPyCode</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.15</string>
    <key>LSUIElement</key>
    <true/>
</dict>
</plist>
LAUNCHERPLIST

cat > "${LAUNCHER_APP}/Contents/MacOS/XPyCode" << 'LAUNCHERBUNDLE'
#!/bin/sh
open xpycode://
LAUNCHERBUNDLE

chmod 0755 "${LAUNCHER_APP}/Contents/MacOS/XPyCode"

# Copy icon into launcher bundle
if [ -f "${INSTALL_DIR}/XPyCode.icns" ]; then
    cp "${INSTALL_DIR}/XPyCode.icns" "${LAUNCHER_APP}/Contents/Resources/XPyCode.icns"
fi

echo "  Created launcher bundle: ${LAUNCHER_APP}"
echo ""

# ── Create Uninstall XPyCode.app bundle ──────────────────────────────────────
UNINSTALLER_APP="${INSTALL_DIR}/Uninstall XPyCode.app"
mkdir -p "${UNINSTALLER_APP}/Contents/MacOS"
mkdir -p "${UNINSTALLER_APP}/Contents/Resources"

cat > "${UNINSTALLER_APP}/Contents/Info.plist" << 'UNINSTALLERPLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>com.xpycode.uninstaller</string>
    <key>CFBundleName</key>
    <string>Uninstall XPyCode</string>
    <key>CFBundleDisplayName</key>
    <string>Uninstall XPyCode</string>
    <key>CFBundleExecutable</key>
    <string>uninstall</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleIconFile</key>
    <string>XPyCode</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.15</string>
</dict>
</plist>
UNINSTALLERPLIST

# Copy icon into uninstaller bundle
if [ -f "${INSTALL_DIR}/XPyCode.icns" ]; then
    cp "${INSTALL_DIR}/XPyCode.icns" "${UNINSTALLER_APP}/Contents/Resources/XPyCode.icns"
fi

cat > "${UNINSTALLER_APP}/Contents/MacOS/uninstall" << 'UNINSTALLSCRIPT'
#!/bin/bash
# XPyCode Uninstaller — runs in a new Terminal window (opened by macOS when the .app is launched)
set -euo pipefail

PLIST_LABEL="com.xpycode.master"
INSTALL_DIR="/Applications/XPyCode"
PLIST_PATH="${HOME}/Library/LaunchAgents/${PLIST_LABEL}.plist"
HANDLER_APP="${INSTALL_DIR}/XPyCodeHandler.app"
HANDLER_APP_USER="${HOME}/Applications/XPyCodeHandler.app"
DESKTOP_SHORTCUT="${HOME}/Desktop/XPyCode.app"

# Locate Python 3.13 — check framework path first, then PATH
PYTHON_EXE=""
_fw_exe="/Library/Frameworks/Python.framework/Versions/3.13/bin/python3"
if [ -x "${_fw_exe}" ]; then
    PYTHON_EXE="${_fw_exe}"
else
    _candidate=$(command -v python3.13 2>/dev/null || true)
    if [ -x "${_candidate}" ]; then
        PYTHON_EXE="${_candidate}"
    else
        _candidate=$(command -v python3 2>/dev/null || true)
        if [ -x "${_candidate}" ]; then
            PYTHON_EXE="${_candidate}"
        fi
    fi
fi

echo "============================================================"
echo "  XPyCode Uninstaller"
echo "============================================================"
echo ""

read -r -p "Are you sure you want to uninstall XPyCode? [y/N]: " confirm
if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then
    echo "Uninstall cancelled."
    exit 0
fi

echo ""
echo "Uninstalling XPyCode..."
echo ""

echo "[1/3] Stopping XPyCode processes..."
pkill -f "xpycode_master" 2>/dev/null || true
sleep 1
echo "  Done"

echo "[2/3] Running XPyCode teardown..."
if [ -x "${PYTHON_EXE}" ]; then
    "${PYTHON_EXE}" -m xpycode_master --teardown \
        && echo "  Teardown complete" \
        || echo "  WARNING: Teardown had errors (continuing with manual cleanup)"
else
    echo "  Python not found; performing manual cleanup..."
    # Manual fallback: remove launchd plist
    if [ -f "${PLIST_PATH}" ]; then
        CURRENT_UID=$(id -u)
        launchctl bootout "gui/${CURRENT_UID}/${PLIST_LABEL}" 2>/dev/null \
            || launchctl unload "${PLIST_PATH}" 2>/dev/null \
            || true
        rm -f "${PLIST_PATH}" && echo "  Removed: ${PLIST_PATH}" || true
    fi
    # Remove protocol handler
    [ -d "${HANDLER_APP}" ] && rm -rf "${HANDLER_APP}" && echo "  Removed: ${HANDLER_APP}" || true
    [ -d "${HANDLER_APP_USER}" ] && rm -rf "${HANDLER_APP_USER}" && echo "  Removed: ${HANDLER_APP_USER}" || true
    # Remove desktop shortcut
    [ -d "${DESKTOP_SHORTCUT}" ] && rm -rf "${DESKTOP_SHORTCUT}" && echo "  Removed: ${DESKTOP_SHORTCUT}" || true
fi

echo "[3/3] Removing installation directory..."
if [ -d "${INSTALL_DIR}" ]; then
    sudo rm -rf "${INSTALL_DIR}" && echo "  Removed: ${INSTALL_DIR}" || echo "  WARNING: Could not remove ${INSTALL_DIR} (may need manual removal)"
fi

echo ""
echo "============================================================"
echo "  XPyCode has been uninstalled."
echo ""
echo "  User data directory (~/.xpycode) was NOT removed."
echo "  To remove all user data, run:  rm -rf ~/.xpycode"
echo "============================================================"
UNINSTALLSCRIPT

chmod 0755 "${UNINSTALLER_APP}/Contents/MacOS/uninstall"
echo "  Created uninstaller bundle: ${UNINSTALLER_APP}"
echo ""

# ── Set custom folder icon on /Applications/XPyCode/ ────────────────────────
if [ -f "${INSTALL_DIR}/XPyCode.icns" ]; then
    "${PYTHON_EXE}" - "${INSTALL_DIR}" << 'SETICON'
import sys
icns_path = sys.argv[1] + '/XPyCode.icns'
install_dir = sys.argv[1]
try:
    import Cocoa
    icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icns_path)
    if icon:
        Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(icon, install_dir, 0)
except Exception:
    pass
SETICON
fi

# ── Fix ownership of everything under INSTALL_DIR ────────────────────────────
echo "Setting ownership..."
if [ -n "${REAL_USER}" ] && [ "${REAL_USER}" != "root" ]; then
    chown -R "${REAL_USER}" "${INSTALL_DIR}" 2>/dev/null \
        && echo "  Owner: ${REAL_USER}" \
        || echo "  WARNING: chown failed (non-critical)"
fi
echo ""

echo "============================================================"
echo "  XPyCode Post-Install complete!"
echo "  XPyCode will start automatically at login."
echo "  Uninstaller: ${INSTALL_DIR}/Uninstall XPyCode.app"
echo "============================================================"
notify_user "XPyCode installation complete!"
exit 0
