#!/bin/bash

# This file is part of Gluesync.
#
# Gluesync is dual-licensed under the following licenses:
#
# 1. GNU General Public License (GPL) Version 3
#    You may use, modify, and distribute this software under the terms of the GPL v3.
#    See the LICENSE-GPL file or <http://www.gnu.org/licenses/gpl-3.0.html> for details.
#    This option is available at no cost, but any derivative works must also be licensed under GPL v3.
#
# 2. MOLO17 Commercial License
#    Alternatively, you may use this software under the MOLO17 Commercial License,
#    which includes a warranty and permits proprietary use. Contact MOLO17 at info@molo17.com
#    for licensing terms and conditions.
#
# You must choose one of these licenses to use this software. Using this software implies
# acceptance of one of these licenses. See the accompanying LICENSE files or contact
# MOLO17 for more information.
#
# Copyright (C) 2026 MOLO17. All rights reserved.

clear

echo "╔══════════════════════════════════════════════════════════════╗"
echo "║                                                              ║"
echo "║  Welcome to the Gluesync by MOLO17 Offline Linux Installer   ║"
echo "║                         v1.0                                 ║"
echo "║                                                              ║"
echo "║  This installer performs an OFFLINE installation of          ║"
echo "║  Gluesync using pre-downloaded Docker images.                ║"
echo "║                                                              ║"
echo "║  Requirements:                                               ║"
echo "║    • Docker and Docker Compose must be installed             ║"
echo "║    • gluesync-platform-linux.zip/.tar.gz in the same folder  ║"
echo "║    • Valid kit ID                                            ║"
echo "║                                                              ║"
echo "║  The script will:                                            ║"
echo "║    • Verify Docker installation                              ║"
echo "║    • Check system requirements (local checks only)           ║"
echo "║    • Validate kit ID against local kit files                 ║"
echo "║    • Extract and load Docker images from the zip file        ║"
echo "║    • Start Gluesync platform                                 ║"
echo "║                                                              ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""

read -p "Press Enter to continue with the installation..."

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

# Logging functions
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

# Configure Docker logging (daemon.json)
configure_docker_logging() {
    log_info "Configuring Docker logging driver..."

    if [[ ! -d /etc/docker ]]; then
        log_info "Creating /etc/docker directory..."
        $SUDO mkdir -p /etc/docker
    fi

    if [[ -f /etc/docker/daemon.json ]]; then
        log_warning "Existing daemon.json found, backing up to daemon.json.backup"
        $SUDO cp /etc/docker/daemon.json /etc/docker/daemon.json.backup
    fi

    cat << EOF | $SUDO tee /etc/docker/daemon.json > /dev/null
{
    "log-driver": "json-file",
    "log-opts": {
        "mode": "non-blocking",
        "max-buffer-size": "4m",
        "max-size": "100m",
        "max-file": "10"
    }
}
EOF

    if command -v systemctl >/dev/null 2>&1; then
        log_info "Restarting Docker to apply logging configuration..."
        if $SUDO systemctl restart docker >/dev/null 2>&1; then
            log_success "Docker logging configured successfully"
        else
            log_warning "Docker restart failed. Please restart the Docker service manually to apply logging changes."
        fi
    else
        log_warning "systemctl not available; please restart Docker manually to apply logging changes."
    fi
}

ensure_docker_service_enabled() {
    if ! command -v systemctl >/dev/null 2>&1; then
        return
    fi

    if $SUDO systemctl is-enabled docker >/dev/null 2>&1; then
        log_info "Docker service already enabled at boot"
    else
        log_info "Enabling Docker service to start at boot..."
        if $SUDO systemctl enable docker >/dev/null 2>&1; then
            log_success "Docker service enabled for automatic startup"
        else
            log_warning "Failed to enable Docker service automatically. You may need to enable it manually with 'systemctl enable docker'."
        fi
    fi
}

ensure_docker_group_membership() {
    local target_user="${SUDO_USER:-$USER}"

    if [[ -z "$target_user" ]]; then
        log_warning "Cannot determine target user for docker group membership"
        return
    fi

    if ! getent group docker >/dev/null 2>&1; then
        log_info "Docker group not found; creating it..."
        if $SUDO groupadd docker >/dev/null 2>&1; then
            log_success "Docker group created"
        else
            log_warning "Unable to create docker group automatically. Please run 'sudo groupadd docker' manually."
            return
        fi
    fi

    if id -nG "$target_user" 2>/dev/null | tr ' ' '\n' | grep -qx "docker"; then
        log_info "User $target_user already belongs to docker group"
    else
        log_info "Adding $target_user to docker group for passwordless Docker usage..."
        if $SUDO usermod -aG docker "$target_user" >/dev/null 2>&1; then
            log_success "User $target_user added to docker group"
            log_info "Please have $target_user log out and back in (or run 'newgrp docker') to apply group changes."
        else
            log_warning "Unable to add $target_user to docker group automatically. Please run 'sudo usermod -aG docker $target_user' manually."
        fi
    fi
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# Detect whether the CPU supports the x86-64-v3 microarchitecture level.
# The default Core Hub image requires x86-64-v3; older CPUs (x86-64-v2 or lower)
# must use the "-debian" image variant instead.
cpu_supports_x86_64_v3() {
    local arch
    arch=$(uname -m 2>/dev/null || echo "")

    # Non-x86 architectures (e.g. arm64) are unaffected
    if [[ "$arch" != "x86_64" && "$arch" != "amd64" ]]; then
        return 0
    fi

    # Preferred: ask the glibc dynamic loader (glibc >= 2.33 reports microarch levels)
    local ld_so ld_out
    for ld_so in /lib64/ld-linux-x86-64.so.2 /lib/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2; do
        if [[ -x "$ld_so" ]]; then
            ld_out=$("$ld_so" --help 2>/dev/null || true)
            if [[ "$ld_out" == *"x86-64-v3"* ]]; then
                if echo "$ld_out" | grep -q "x86-64-v3 (supported"; then
                    return 0
                fi
                return 1
            fi
            break
        fi
    done

    # Fallback: check the CPU flags required by x86-64-v3
    local flags flag
    flags=$(grep -m1 '^flags' /proc/cpuinfo 2>/dev/null | cut -d: -f2 || true)
    if [[ -z "$flags" ]]; then
        # Cannot determine: assume supported (default image)
        return 0
    fi
    for flag in avx avx2 bmi1 bmi2 f16c fma movbe xsave abm; do
        if ! grep -qw "$flag" <<< "$flags"; then
            return 1
        fi
    done
    return 0
}

# Switch the Core Hub image to the "-debian" variant in the installed
# docker-compose file when the CPU cannot run x86-64-v3 binaries.
apply_core_hub_cpu_compatibility() {
    if cpu_supports_x86_64_v3; then
        log_info "CPU supports x86-64-v3: using the default Gluesync Core Hub image."
        return 0
    fi

    log_warning "CPU without x86-64-v3 support detected (x86-64-v2 or older)."
    log_warning "Switching Gluesync Core Hub to the \"-debian\" image variant."

    local compose_file
    if ! compose_file=$(find_install_compose_file); then
        log_warning "No docker-compose file found to apply the Core Hub -debian variant."
        return 0
    fi

    if grep -qE 'gluesync-core-hub[A-Za-z0-9._-]*:[A-Za-z0-9._-]*-debian' "$compose_file"; then
        log_info "Core Hub image already uses the -debian variant in $(basename "$compose_file")"
        return 0
    fi

    if $SUDO sed -i.bak -E 's#(molo17/gluesync-core-hub[A-Za-z0-9._-]*:[A-Za-z0-9._-]+)#\1-debian#g; s#-debian-debian#-debian#g' "$compose_file"; then
        $SUDO rm -f "${compose_file}.bak"
        log_success "Updated Core Hub image to -debian variant in $(basename "$compose_file")"
    else
        log_warning "Failed to update Core Hub image reference in $compose_file"
    fi
}

# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLATFORM_ARCHIVE_PATH=""
KIT_FILE_PATH=""
declare -Ag IMAGE_TAG_MAP=()

find_platform_archive() {
    local uname_arch
    uname_arch=$(uname -m 2>/dev/null || echo "")
    local normalized_arch=""

    case "$uname_arch" in
        x86_64|amd64)
            normalized_arch="amd64"
            ;;
        aarch64|arm64)
            normalized_arch="arm64"
            ;;
    esac

    local candidates=()

    if [[ -n "$normalized_arch" ]]; then
        candidates+=(
            "gluesync-platform-linux-${normalized_arch}.tar.gz"
            "gluesync-platform-linux-${normalized_arch}.zip"
        )
    fi

    candidates+=(
        "gluesync-platform-linux.tar.gz"
        "gluesync-platform-linux.zip"
    )

    local candidate
    for candidate in "${candidates[@]}"; do
        if [[ -f "${SCRIPT_DIR}/${candidate}" ]]; then
            echo "${SCRIPT_DIR}/${candidate}"
            return 0
        fi
    done

    return 1
}

# Check if running with sufficient privileges
check_permissions() {
    if [[ $EUID -eq 0 ]]; then
        log_success "Running as root"
        SUDO=""
    elif sudo -n true 2>/dev/null; then
        log_success "Running with sudo privileges"
        SUDO="sudo"
    else
        log_error "This script requires root privileges or sudo access"
        log_info "Please run as root or ensure you have sudo privileges"
        exit 1
    fi
}

configure_ubuntu_network_tuning() {
    if [[ ! -r /etc/os-release ]]; then
        return
    fi

    # shellcheck disable=SC1091
    . /etc/os-release

    local distro_id="${ID:-}"
    local distro_like="${ID_LIKE:-}"

    if [[ "$distro_id" != "ubuntu" && "$distro_id" != "debian" && "$distro_like" != *"ubuntu"* && "$distro_like" != *"debian"* ]]; then
        log_info "Skipping sysctl tuning: system is not Ubuntu/Debian-based."
        return
    fi

    local sysctl_file="/etc/sysctl.d/99-gluesync.conf"
    log_info "Applying Gluesync sysctl tuning at $sysctl_file..."

    cat << EOF | $SUDO tee "$sysctl_file" > /dev/null
net.netfilter.nf_conntrack_max = 131072
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
EOF

    local sysctl_cmd=""
    if command -v sysctl >/dev/null 2>&1; then
        sysctl_cmd=$(command -v sysctl)
    elif [[ -x /sbin/sysctl ]]; then
        sysctl_cmd="/sbin/sysctl"
    elif [[ -x /usr/sbin/sysctl ]]; then
        sysctl_cmd="/usr/sbin/sysctl"
    fi

    if [[ -n "$sysctl_cmd" ]]; then
        log_info "Reloading sysctl parameters via $sysctl_cmd --system"
        if $SUDO "$sysctl_cmd" --system; then
            log_success "Sysctl parameters applied successfully."
        else
            log_warning "Failed to reload sysctl settings automatically. Please run 'sudo sysctl --system'."
        fi
    else
        log_warning "sysctl command not found; please reload kernel parameters manually."
    fi
}

ensure_required_cli_tools() {
    local missing=()
    local tool

    for tool in tar unzip gunzip jq python3; do
        if ! command -v "$tool" >/dev/null 2>&1; then
            missing+=("$tool")
        fi
    done

    if (( ${#missing[@]} == 0 )); then
        return
    fi

    log_error "Required command(s) missing: ${missing[*]}"

    if command -v apt-get >/dev/null 2>&1; then
        log_info "Install them with: sudo apt-get update && sudo apt-get install -y ${missing[*]}"
    elif command -v dnf >/dev/null 2>&1; then
        log_info "Install them with: sudo dnf install -y ${missing[*]}"
    elif command -v yum >/dev/null 2>&1; then
        log_info "Install them with: sudo yum install -y ${missing[*]}"
    elif command -v zypper >/dev/null 2>&1; then
        log_info "Install them with: sudo zypper install -y ${missing[*]}"
    elif command -v pacman >/dev/null 2>&1; then
        log_info "Install them with: sudo pacman -S --needed ${missing[*]}"
    else
        log_info "Please install the missing packages using your distribution's package manager."
    fi

    exit 1
}

# Image tag helpers
get_image_tags_from_tar() {
    local tar_file="$1"
    local manifest_json=""

    if [[ "$tar_file" == *.tar.gz ]]; then
        manifest_json=$(tar -xOzf "$tar_file" manifest.json 2>/dev/null || true)
    else
        manifest_json=$(tar -xOf "$tar_file" manifest.json 2>/dev/null || true)
    fi

    if [[ -z "$manifest_json" ]]; then
        echo ""
        return
    fi

    printf '%s\n' "$manifest_json" | jq -r '.[].RepoTags[]?' 2>/dev/null | sort -u
}

log_image_tags() {
    local tags="$1"

    if [[ -z "$tags" ]]; then
        log_warning "  Unable to read manifest.json to determine image tags."
        return
    fi

    log_info "  Repo tags:"
    while IFS= read -r tag; do
        [[ -z "$tag" ]] && continue
        log_info "    - $tag"
    done <<< "$tags"
}

record_image_tags_from_list() {
    local tags="$1"

    if [[ -z "$tags" ]]; then
        return
    fi

    while IFS= read -r tag; do
        [[ -z "$tag" ]] && continue
        local repo="${tag%%:*}"
        if [[ "$repo" != "$tag" ]]; then
            # Core Hub CPU compatibility: keep only the image variant matching this CPU
            if [[ "$repo" == *"gluesync-core-hub"* ]]; then
                if cpu_supports_x86_64_v3; then
                    [[ "$tag" == *"-debian"* ]] && continue
                else
                    [[ "$tag" != *"-debian"* ]] && continue
                fi
            fi
            IMAGE_TAG_MAP["$repo"]="$tag"
        fi
    done <<< "$tags"
}

find_install_compose_file() {
    local install_root="/opt/gluesync"
    if [[ ! -d "$install_root" ]]; then
        return 1
    fi

    local candidate
    while IFS= read -r -d '' candidate; do
        echo "$candidate"
        return 0
    done < <(find "$install_root" -maxdepth 3 -type f \( -name "docker-compose.yaml" -o -name "docker-compose.yml" \) -print0 2>/dev/null)

    return 1
}

update_single_compose_image() {
    local compose_file="$1"
    local repo="$2"
    local new_ref="$3"

    local substitution_output
    if ! substitution_output=$($SUDO python3 - "$compose_file" "$repo" "$new_ref" <<'PY'
import sys
import re

path, repo, new_ref = sys.argv[1:]
with open(path, encoding="utf-8") as fh:
    content = fh.read()

pattern = re.compile(r'(image\s*:\s*["\']?)' + re.escape(repo) + r'(?:[:@][^"\'""\s]+)?(["\']?)')

def repl(match):
    prefix, quote = match.group(1), match.group(2) or ''
    return f"{prefix}{new_ref}{quote}"

new_content, count = pattern.subn(repl, content)
if count:
    with open(path, 'w', encoding="utf-8") as fh:
        fh.write(new_content)

print(count)
PY
); then
        log_warning "  Failed to update image reference for $repo."
        return 1
    fi

    local substitution_count
    substitution_count=$(echo "$substitution_output" | tr -d '[:space:]')
    if [[ "$substitution_count" =~ ^[0-9]+$ && "$substitution_count" -gt 0 ]]; then
        log_info "  Updated $repo to use $new_ref"
        return 0
    fi

    log_info "  No compose entries found for $repo"
    return 1
}

update_compose_images_with_tags() {
    if (( ${#IMAGE_TAG_MAP[@]} == 0 )); then
        log_warning "No image tags collected to update docker-compose file."
        return
    fi

    local compose_file=""
    if ! compose_file=$(find_install_compose_file); then
        log_warning "Unable to locate docker-compose file to update image tags."
        return
    fi

    log_info "Updating docker-compose image tags in $(basename "$compose_file")..."
    local updated=0
    local repo
    for repo in "${!IMAGE_TAG_MAP[@]}"; do
        local ref="${IMAGE_TAG_MAP[$repo]}"
        if update_single_compose_image "$compose_file" "$repo" "$ref"; then
            updated=$((updated + 1))
        fi
    done

    if (( updated > 0 )); then
        log_success "Updated $updated image reference(s) in $(basename "$compose_file")."
    else
        log_warning "No compose image references matched the extracted tags."
    fi
}

# Allocate a 10 GB swap file if the system doesn't already have enough swap.
# Idempotent: skips creation when total swap already meets the target.
setup_swap() {
    local TARGET_SWAP_GB=10
    local TARGET_SWAP_KB=$(( TARGET_SWAP_GB * 1024 * 1024 ))
    local SWAP_FILE="/swapfile"

    log_info "Checking swap configuration (target: ${TARGET_SWAP_GB}GB)..."

    # Sum all existing swap areas from /proc/swaps (column 3 = size in KiB)
    local current_swap_kb=0
    if [[ -r /proc/swaps ]]; then
        current_swap_kb=$(awk 'NR>1 {sum+=$3} END {print sum+0}' /proc/swaps)
    fi

    local current_swap_gb
    current_swap_gb=$(awk -v kb="$current_swap_kb" 'BEGIN { printf "%.1f", kb/1048576 }')

    if (( current_swap_kb >= TARGET_SWAP_KB )); then
        log_success "Swap already configured (${current_swap_gb}GB). Skipping swap allocation."
        return 0
    fi

    log_info "Current swap: ${current_swap_gb}GB — allocating ${TARGET_SWAP_GB}GB swap file at ${SWAP_FILE}..."

    # If a swap file already exists but is too small, turn it off and remove it
    if [[ -f "$SWAP_FILE" ]]; then
        log_info "Removing existing (undersized) swap file..."
        $SUDO swapoff "$SWAP_FILE" 2>/dev/null || true
        $SUDO rm -f "$SWAP_FILE"
    fi

    # Allocate the file — prefer fallocate (fast), fall back to dd
    if command -v fallocate >/dev/null 2>&1; then
        log_info "Allocating ${TARGET_SWAP_GB}GB with fallocate..."
        if ! $SUDO fallocate -l "${TARGET_SWAP_GB}G" "$SWAP_FILE"; then
            log_warning "fallocate failed, falling back to dd..."
            $SUDO dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$(( TARGET_SWAP_GB * 1024 )) status=progress
        fi
    else
        log_info "Allocating ${TARGET_SWAP_GB}GB with dd (this may take a moment)..."
        $SUDO dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$(( TARGET_SWAP_GB * 1024 )) status=progress
    fi

    # Secure the file: only root can read/write
    $SUDO chmod 600 "$SWAP_FILE"

    # Format and activate
    $SUDO mkswap "$SWAP_FILE"
    $SUDO swapon "$SWAP_FILE"

    # Persist across reboots — add only if not already present
    if ! grep -q "^${SWAP_FILE}" /etc/fstab 2>/dev/null; then
        log_info "Persisting swap in /etc/fstab..."
        echo "${SWAP_FILE} none swap sw 0 0" | $SUDO tee -a /etc/fstab > /dev/null
    fi

    local new_swap_kb
    new_swap_kb=$(awk 'NR>1 {sum+=$3} END {print sum+0}' /proc/swaps 2>/dev/null || echo 0)
    local new_swap_gb
    new_swap_gb=$(awk -v kb="$new_swap_kb" 'BEGIN { printf "%.1f", kb/1048576 }')
    log_success "Swap configured successfully (${new_swap_gb}GB total active swap)."
}

# Check local system requirements (no internet checks)
check_system_requirements() {
    log_info "Running local system pre-flight checks..."
    local warnings=0

    # CPU cores check
    local cores=""
    if command -v nproc >/dev/null 2>&1; then
        cores=$(nproc 2>/dev/null)
    elif command -v sysctl >/dev/null 2>&1; then
        cores=$(sysctl -n hw.ncpu 2>/dev/null)
    fi

    if [[ "$cores" =~ ^[0-9]+$ ]]; then
        log_info "Detected CPU cores: $cores"
        if (( cores < 4 )); then
            log_warning "System has $cores CPU core(s). At least 4 cores are recommended."
            warnings=1
        elif (( cores == 4 )); then
            log_warning "System has 4 CPU cores. This is the bare minimum and may limit performance."
        else
            log_success "CPU requirement satisfied ($cores cores available)."
        fi
    else
        log_warning "Unable to determine CPU core count. Skipping CPU requirement check."
    fi

    # RAM check
    local total_ram_kb=""
    if [[ -r /proc/meminfo ]]; then
        total_ram_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null)
    elif command -v sysctl >/dev/null 2>&1; then
        total_ram_kb=$(sysctl -n hw.memsize 2>/dev/null)
        if [[ "$total_ram_kb" =~ ^[0-9]+$ ]]; then
            total_ram_kb=$(( total_ram_kb / 1024 ))
        fi
    fi

    if [[ "$total_ram_kb" =~ ^[0-9]+$ ]]; then
        local total_ram_gb
        total_ram_gb=$(awk -v kb="$total_ram_kb" 'BEGIN { printf "%.1f", kb/1048576 }')
        local min_ram_kb=$(( (8 * 1000 * 1000 * 1000) / 1024 ))
        if (( total_ram_kb < min_ram_kb )); then
            log_warning "Only ${total_ram_gb}GB of RAM detected. At least 8GB is recommended."
            warnings=1
        else
            log_success "RAM requirement satisfied (${total_ram_gb}GB available)."
        fi
    else
        log_warning "Unable to determine total RAM. Continuing without enforcing RAM requirement."
    fi

    # Disk space check
    local avail_space_kb=""
    avail_space_kb=$(df -Pk / 2>/dev/null | awk 'NR==2 {print $4}')
    if [[ "$avail_space_kb" =~ ^[0-9]+$ ]]; then
        local avail_space_gb
        avail_space_gb=$(awk -v kb="$avail_space_kb" 'BEGIN { printf "%.1f", kb/1048576 }')
        if (( avail_space_kb < 52428800 )); then
            log_warning "Only ${avail_space_gb}GB free on /. At least 200GB is recommended for Gluesync."
            warnings=1
        elif (( avail_space_kb < 104857600 )); then
            log_warning "Only ${avail_space_gb}GB free on /. MOLO17 recommends at least 200GB."
        else
            log_success "Disk space requirement satisfied (${avail_space_gb}GB free)."
        fi
    else
        log_warning "Unable to determine available disk space on /. Continuing without enforcing disk requirement."
    fi

    if (( warnings > 0 )); then
        log_warning "System checks completed with warnings. Installation can continue but performance may be affected."
    else
        log_success "Local system pre-flight checks passed."
    fi
}

# Verify Docker installation
verify_docker_installation() {
    log_info "Verifying Docker installation..."

    # Check if docker command exists
    if ! command -v docker >/dev/null 2>&1; then
        log_error "Docker is not installed or not in PATH"
        log_info "Please install Docker before running this offline installer"
        exit 1
    fi
    log_success "Docker command found"

    # Check if Docker daemon is running
    if ! $SUDO docker info >/dev/null 2>&1; then
        log_error "Docker daemon is not running"
        log_info "Please start Docker service: sudo systemctl start docker"
        exit 1
    fi
    log_success "Docker daemon is running"

    # Check Docker version
    DOCKER_VERSION=$($SUDO docker version --format '{{.Server.Version}}' 2>/dev/null || echo "unknown")
    log_info "Docker version: $DOCKER_VERSION"

    # Check for Docker Compose
    local compose_available=false
    if command -v docker-compose >/dev/null 2>&1; then
        COMPOSE_VERSION=$(docker-compose version --short 2>/dev/null || echo "unknown")
        log_info "Docker Compose (standalone) version: $COMPOSE_VERSION"
        compose_available=true
    fi
    
    if $SUDO docker compose version >/dev/null 2>&1; then
        COMPOSE_VERSION=$($SUDO docker compose version --short 2>/dev/null || echo "unknown")
        log_info "Docker Compose (plugin) version: $COMPOSE_VERSION"
        compose_available=true
    fi

    if [[ "$compose_available" == "false" ]]; then
        log_error "Docker Compose is not installed"
        log_info "Please install Docker Compose before running this offline installer"
        exit 1
    fi
    log_success "Docker Compose is available"

    log_success "Docker installation verification completed"
}

# Check if platform archive exists
check_platform_archive() {
    log_info "Checking for platform archive file..."

    if PLATFORM_ARCHIVE_PATH=$(find_platform_archive); then
        log_success "Platform archive found: $(basename "$PLATFORM_ARCHIVE_PATH")"
    else
        log_error "Platform archive not found in $SCRIPT_DIR"
        log_info "Expected gluesync-platform-linux.tar.gz or gluesync-platform-linux.zip"
        exit 1
    fi
}

# Validate kit ID against local files
validate_kit_id() {
    local kit_id="$1"
    
    log_info "Validating kit ID against local kit files..."
    
    # Look for kit files in the script directory
    local kit_files=()
    while IFS= read -r -d '' file; do
        kit_files+=("$file")
    done < <(find "$SCRIPT_DIR" -maxdepth 1 -type f -name "*-trial-kit.zip" -print0 2>/dev/null)
    
    if [[ ${#kit_files[@]} -eq 0 ]]; then
        log_warning "No kit files found in $SCRIPT_DIR"
        log_warning "Skipping kit ID validation"
        return 0
    fi
    
    # Check if the kit ID matches any kit file
    local expected_filename="${kit_id}-trial-kit.zip"
    local found=false
    
    for kit_file in "${kit_files[@]}"; do
        local basename_file=$(basename "$kit_file")
        if [[ "$basename_file" == "$expected_filename" ]]; then
            KIT_FILE_PATH="$kit_file"
            log_success "Kit ID validated: $basename_file"
            found=true
            break
        fi
    done
    
    if [[ "$found" == "false" ]]; then
        log_warning "Kit ID does not match any local kit files"
        log_info "Available kit files:"
        for kit_file in "${kit_files[@]}"; do
            log_info "  - $(basename "$kit_file")"
        done
        echo ""
        read -p "Continue anyway? (y/N): " -n 1 -r
        echo ""
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            log_info "Installation cancelled"
            exit 1
        fi
    fi
}

set_gluesync_permissions() {
    local owner="${SUDO_USER:-$USER}"

    if [[ -z "$owner" ]]; then
        owner="$USER"
    fi

    log_info "Setting ownership of /opt/gluesync to $owner..."
    $SUDO chown -R "$owner:$owner" /opt/gluesync 2>/dev/null || true
}

# Extract and print image tags helper
print_image_tags() {
    local tar_file="$1"
    local manifest_json=""

    if [[ "$tar_file" == *.tar.gz ]]; then
        manifest_json=$(tar -xOzf "$tar_file" manifest.json 2>/dev/null || true)
    else
        manifest_json=$(tar -xOf "$tar_file" manifest.json 2>/dev/null || true)
    fi

    if [[ -z "$manifest_json" ]]; then
        log_warning "  Unable to read manifest.json to determine image tags."
        return
    fi

    local tags
    tags=$(printf '%s\n' "$manifest_json" | jq -r '.[].RepoTags[]?' 2>/dev/null | sort -u)

    if [[ -n "$tags" ]]; then
        log_info "  Repo tags:"
        while IFS= read -r tag; do
            [[ -z "$tag" ]] && continue
            log_info "    - $tag"
        done <<< "$tags"
    else
        log_warning "  No RepoTags entries found in manifest.json."
    fi
}

# Extract platform zip and load Docker images
extract_and_load_images() {
    log_info "Extracting platform archive..."
    
    local extract_dir="/tmp/gluesync-platform-$$"
    mkdir -p "$extract_dir"

    IMAGE_TAG_MAP=()

    local archive_type=""
    if [[ "$PLATFORM_ARCHIVE_PATH" == *.tar.gz ]]; then
        archive_type="tar.gz"
    elif [[ "$PLATFORM_ARCHIVE_PATH" == *.zip ]]; then
        archive_type="zip"
    fi

    if [[ -z "$archive_type" ]] && command -v file >/dev/null 2>&1; then
        local file_output
        file_output=$(file -b "$PLATFORM_ARCHIVE_PATH" 2>/dev/null || true)
        if [[ "$file_output" == *"gzip compressed data"* ]]; then
            archive_type="tar.gz"
        elif [[ "$file_output" == *"Zip archive data"* ]]; then
            archive_type="zip"
        fi
    fi

    if [[ -z "$archive_type" ]]; then
        log_error "Unsupported archive format for $(basename "$PLATFORM_ARCHIVE_PATH")"
        rm -rf "$extract_dir"
        exit 1
    fi

    if [[ "$archive_type" == "tar.gz" ]]; then
        if ! command -v tar >/dev/null 2>&1; then
            log_error "tar command not found. Please install tar"
            rm -rf "$extract_dir"
            exit 1
        fi
        if ! tar -xzf "$PLATFORM_ARCHIVE_PATH" -C "$extract_dir"; then
            log_error "Failed to extract platform archive"
            rm -rf "$extract_dir"
            exit 1
        fi
    else
        if ! command -v unzip >/dev/null 2>&1; then
            log_error "unzip command not found. Please install unzip"
            rm -rf "$extract_dir"
            exit 1
        fi
        if ! unzip -q "$PLATFORM_ARCHIVE_PATH" -d "$extract_dir"; then
            log_error "Failed to extract platform archive"
            rm -rf "$extract_dir"
            exit 1
        fi
    fi
    
    log_success "Platform archive extracted to $extract_dir"

    log_info "Debug: listing extracted contents (top level)"
    ls -lah "$extract_dir" || log_warning "Unable to list contents of $extract_dir"
    
    # Find all .tar/.tar.gz files (Docker images)
    log_info "Searching for Docker image files (.tar /.tar.gz)..."
    local tar_files=()
    while IFS= read -r -d '' file; do
        tar_files+=("$file")
    done < <(find "$extract_dir" -type f \( -name "*.tar" -o -name "*.tar.gz" \) -print0 2>/dev/null)
    
    if [[ ${#tar_files[@]} -eq 0 ]]; then
        log_error "No Docker image (.tar/.tar.gz) files found inside $(basename "$PLATFORM_ARCHIVE_PATH"). Installation cannot continue."
        log_info "Collected directory tree for debugging:"
        find "$extract_dir" -maxdepth 3 -print
        rm -rf "$extract_dir"
        exit 1
    else
        log_info "Found ${#tar_files[@]} Docker image(s) to load"
        echo ""
        
        local loaded=0
        local failed=0
        local total=${#tar_files[@]}
        
        for tar_file in "${tar_files[@]}"; do
            local filename=$(basename "$tar_file")
            loaded=$((loaded + 1))

            log_info "[$loaded/$total] Loading image: $filename"
            local tags=""
            tags=$(get_image_tags_from_tar "$tar_file")
            log_image_tags "$tags"
            record_image_tags_from_list "$tags"
            local load_success=false

            if [[ "$filename" == *.tar.gz ]]; then
                if command -v gunzip >/dev/null 2>&1; then
                    if gunzip -c "$tar_file" | $SUDO docker load >/dev/null 2>&1; then
                        load_success=true
                    fi
                else
                    log_error "  gunzip command not available; cannot load $filename"
                fi
            else
                if $SUDO docker load < "$tar_file" >/dev/null 2>&1; then
                    load_success=true
                fi
            fi

            if [[ "$load_success" == true ]]; then
                log_success "  Loaded successfully"
            else
                log_error "  Failed to load"
                failed=$((failed + 1))
            fi
        done
        
        echo ""
        if [[ $failed -eq 0 ]]; then
            log_success "All $total Docker image(s) loaded successfully!"
        else
            log_warning "$((total - failed))/$total images loaded successfully, $failed failed"
        fi
    fi
    
    # Cleanup
    log_info "Cleaning up temporary files..."
    rm -rf "$extract_dir"
    
    log_success "Platform installation completed"
}

install_kit_contents() {
    if [[ -z "$KIT_FILE_PATH" || ! -f "$KIT_FILE_PATH" ]]; then
        log_error "Required kit file not found. Expected a file like <KIT_ID>-trial-kit.zip in $SCRIPT_DIR"
        exit 1
    fi

    local kit_filename=$(basename "$KIT_FILE_PATH")
    log_info "Installing Gluesync kit from $kit_filename..."

    if ! command -v unzip >/dev/null 2>&1; then
        log_error "unzip command not found. Please install unzip package."
        exit 1
    fi

    local extract_dir="/tmp/gluesync-kit-$$"
    mkdir -p "$extract_dir"

    if ! unzip -q "$KIT_FILE_PATH" -d "$extract_dir"; then
        log_error "Failed to extract kit file $kit_filename"
        rm -rf "$extract_dir"
        exit 1
    fi

    # Clean and recreate /opt/gluesync to avoid conflicts from previous installations
    log_info "Preparing installation directory..."
    if [[ -d /opt/gluesync ]]; then
        log_info "Removing existing /opt/gluesync directory..."
        $SUDO rm -rf /opt/gluesync
    fi
    $SUDO mkdir -p /opt/gluesync

    local kit_gluesync_dir=""
    if [[ -d "$extract_dir/gluesync-docker" ]]; then
        kit_gluesync_dir="$extract_dir/gluesync-docker"
    elif [[ -d "$extract_dir/gluesync-podman" ]]; then
        kit_gluesync_dir="$extract_dir/gluesync-podman"
    fi

    if [[ -n "$kit_gluesync_dir" ]]; then
        log_info "Copying Gluesync runtime files from $(basename "$kit_gluesync_dir")..."
        log_info "Debug: Contents of $kit_gluesync_dir:"
        ls -lah "$kit_gluesync_dir" || log_warning "Unable to list kit directory"
        
        local copy_error
        if ! copy_error=$($SUDO cp -r "$kit_gluesync_dir"/* /opt/gluesync/ 2>&1); then
            log_error "Failed to copy Gluesync runtime files"
            log_error "Copy error output: $copy_error"
            log_info "Debug: Checking if source has files:"
            find "$kit_gluesync_dir" -maxdepth 1 -print || true
            rm -rf "$extract_dir"
            exit 1
        fi

        # Copy shared/root files excluding platform-specific directories
        log_info "Installing Gluesync configuration files..."
        for item in "$extract_dir"/*; do
            local base=$(basename "$item")
            case "$base" in
                gluesync-docker|gluesync-podman|gluesync-docker-windows|gluesync-docker-windows-2019|gluesync-kubernetes)
                    continue
                    ;;
                *)
                    if [[ -f "$item" || -d "$item" ]]; then
                        if $SUDO cp -r "$item" /opt/gluesync/ 2>/dev/null; then
                            log_info "Installed: $base"
                        fi
                    fi
                    ;;
            esac
        done
    else
        log_error "No gluesync-docker or gluesync-podman folder found inside kit archive"
        log_info "Expected kit structure: <KIT_ID>-trial-kit.zip containing gluesync-docker/ or gluesync-podman/"
        rm -rf "$extract_dir"
        exit 1
    fi

    update_compose_images_with_tags

    # Ensure the Core Hub image variant matches the CPU capabilities
    apply_core_hub_cpu_compatibility

    rm -rf "$extract_dir"

    set_gluesync_permissions

    log_success "Gluesync kit content installed"
}

# Setup Gluesync
setup_gluesync() {
    log_info "🔧 Starting Gluesync offline setup..."
    
    # Ask for kit identifier
    echo ""
    log_info "📦 Gluesync Kit Setup"
    echo "   Please enter your Gluesync kit's unique ID."
    echo "   Format: a1b2c3d4e5f6 (12 hexadecimal characters)"
    echo ""
    
    while true; do
        read -p "Enter your kit ID (or press Enter to cancel): " KIT_ID
        echo ""
        
        # Allow cancel
        if [[ -z "$KIT_ID" ]]; then
            log_info "Gluesync setup cancelled."
            exit 0
        fi
        
        # Validate kit ID format (12 hex characters)
        if [[ ! $KIT_ID =~ ^[a-f0-9]{12}$ ]]; then
            log_error "Invalid kit ID format. Expected 12 hexadecimal characters (0-9, a-f)."
            continue
        fi
        
        break
    done
    
    # Validate kit ID against local files
    validate_kit_id "$KIT_ID"
    
    # Extract and load images
    extract_and_load_images
    install_kit_contents

    log_success "🎉 Gluesync offline setup completed successfully!"
    log_info "Installation location: /opt/gluesync"
    
    echo ""
    read -p "Would you like to start Gluesync now? (y/N): " -n 1 -r
    echo ""
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        start_gluesync
    else
        log_info "You can start Gluesync later by running /opt/gluesync/run.sh"
    fi
}

# Start Gluesync
start_gluesync() {
    log_info "Starting Gluesync..."
    
    if [[ ! -f /opt/gluesync/run.sh ]]; then
        log_error "run.sh not found in /opt/gluesync"
        log_info "Please check the installation and try manually"
        return 1
    fi
    
    if (cd /opt/gluesync && ./run.sh); then
        log_success "Gluesync started successfully!"
        
        HOST_IP=$(hostname -I | awk '{print $1}' 2>/dev/null || echo "localhost")
        echo ""
        echo "╔══════════════════════════════════════════════════════════════╗"
        echo "║                                                              ║"
        echo "║                 🎉 GLUESYNC IS NOW RUNNING!                  ║"
        echo "║                                                              ║"
        echo "║  Access URL: https://$HOST_IP                           ║"
        echo "║                                                              ║"
        echo "║  Default username: admin                                     ║"
        echo "║  Default password: admin                                     ║"
        echo "║                                                              ║"
        echo "║  Keep this URL safe and share only with authorized users.    ║"
        echo "║                                                              ║"
        echo "╚══════════════════════════════════════════════════════════════╝"
        echo ""
    else
        log_error "Failed to start Gluesync. Please check the run.sh script and try manually."
        return 1
    fi
}

# Main function
main() {
    log_info "Starting Gluesync Offline Installation Script"
    log_info "=============================================="
    echo ""
    
    # Check permissions
    check_permissions

    # Allocate 10 GB swap (idempotent — skipped if already sufficient)
    setup_swap

    # Check system requirements (local only)
    check_system_requirements
    
    echo ""
    
    # Verify Docker installation
    verify_docker_installation
    
    echo ""
    
    # Apply offline Docker maintenance tasks
    configure_docker_logging
    ensure_docker_service_enabled
    ensure_docker_group_membership
    configure_ubuntu_network_tuning

    echo ""

    ensure_required_cli_tools

    # Check platform archive exists
    check_platform_archive
    
    echo ""
    
    # Setup Gluesync
    setup_gluesync
}

# Run main function
main "$@"
