#!/bin/bash
set -e

# Source diagnostic logger if available
if [ -f "/usr/local/bin/diagnostic-logger.sh" ]; then
    . /usr/local/bin/diagnostic-logger.sh
fi

# Helper function to log messages
log() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[${timestamp}] $*" >&2
}

# Sanitize environment variables by removing invisible Unicode characters
# This fixes the Portainer deployment issue where U+200E and similar characters
# cause "unexpected character" errors in variable names
sanitize_env_vars() {
    log "Sanitizing environment variables..."
    
    # Create a secure temporary file
    local temp_env
    temp_env=$(mktemp /tmp/sanitized_env.XXXXXX)
    
    # Export current environment to a file, then clean it
    export -p > "$temp_env"
    
    # Remove common invisible Unicode characters in a single sed command:
    # - U+200E (Left-to-Right Mark) - E2 80 8E
    # - U+200F (Right-to-Left Mark) - E2 80 8F
    # - U+200B (Zero Width Space) - E2 80 8B
    # - U+FEFF (Zero Width No-Break Space / BOM) - EF BB BF
    # - U+202A-202E (Directional formatting characters)
    sed -i \
        -e 's/\xE2\x80\x8E//g' \
        -e 's/\xE2\x80\x8F//g' \
        -e 's/\xE2\x80\x8B//g' \
        -e 's/\xEF\xBB\xBF//g' \
        -e 's/\xE2\x80\xAA//g' \
        -e 's/\xE2\x80\xAB//g' \
        -e 's/\xE2\x80\xAC//g' \
        -e 's/\xE2\x80\xAD//g' \
        -e 's/\xE2\x80\xAE//g' \
        "$temp_env" 2>/dev/null
    
    # Source the sanitized environment
    # If sourcing fails, log a warning but continue (environment may be partially set)
    if ! source "$temp_env" 2>/dev/null; then
        log "WARNING: Failed to source sanitized environment. Some variables may not be set correctly."
    fi
    
    # Clean up temporary file
    rm -f "$temp_env"
    
    log "Environment variables sanitized successfully"
}

# Sanitize environment variables on startup
log "=== STARTUP INITIALIZATION ==="
log "Container ID: $(hostname 2>/dev/null || 'unknown')"
log "Timestamp: $(date '+%Y-%m-%d %H:%M:%S %Z')"

# Initialize diagnostic logging only if file exists and we're in debug mode
if [ -f "/usr/local/bin/diagnostic-logger.sh" ] && [ "${DEBUG_LOGGING:-false}" = "true" ]; then
    . /usr/local/bin/diagnostic-logger.sh
    log_system_info
    validate_environment
    rotate_logs
fi

sanitize_env_vars

log "=== ENVIRONMENT SANITIZATION COMPLETE ==="

# Repository configuration
REPO_URL="${REPO_URL:-}"
REPO_DIR="/home/web/data"
REPO_BRANCH="${REPO_BRANCH:-main}"

log "Initializing Shopify AI App Builder..."

# Log repository configuration details only when cloning is enabled
if [ -n "$REPO_URL" ]; then
    log "Repository Configuration:"
    log "  URL: ${REPO_URL}"
    log "  Branch: ${REPO_BRANCH}"
    log "  Directory: ${REPO_DIR}"
fi

# Only clone/pull if REPO_URL is set
if [ -n "$REPO_URL" ]; then
    log "Repository URL: $REPO_URL"
    log "Repository directory: $REPO_DIR"
    log "Default branch: $REPO_BRANCH"

    # Check if git is available
    if ! command -v git &> /dev/null; then
        log "ERROR: git is not available in the container"
        exit 1
    fi

    # Check if the repository directory is empty or doesn't have .git
    if [ ! -d "$REPO_DIR/.git" ]; then
        # Directory doesn't exist or .git is missing - need to clone
        if [ -d "$REPO_DIR" ] && [ "$(ls -A "$REPO_DIR")" ]; then
            # Directory exists but is not a git repo - back it up
            log "WARNING: $REPO_DIR exists but is not a git repository. Backing up to ${REPO_DIR}.backup"
            mv "$REPO_DIR" "${REPO_DIR}.backup"
        fi
        
        log "Repository not found. Cloning $REPO_URL into $REPO_DIR..."
        git clone "$REPO_URL" "$REPO_DIR"
        cd "$REPO_DIR"
        log "Successfully cloned repository"
    else
        # Repository exists, pull latest changes
        cd "$REPO_DIR"
        log "Repository found at $REPO_DIR. Pulling latest changes from $REPO_BRANCH..."

        # Check if we're on a detached HEAD or have uncommitted changes
        if git diff --quiet && git diff --cached --quiet; then
            git fetch origin
            # Use plain git pull per policy (do not force origin/branch explicitly here)
            git pull || {
                log "WARNING: Failed to pull from $REPO_BRANCH, attempting to pull from any available branch"
                git pull || log "WARNING: Pull operation failed, continuing anyway"
            }
            log "Successfully pulled latest changes"
        else
            log "WARNING: Repository has uncommitted changes. Skipping pull to avoid conflicts."
            log "Run 'git status' to see changes, then 'git pull' manually if desired"
        fi
    fi
    log "Repository is ready at $REPO_DIR"
else
    mkdir -p "$REPO_DIR"
    log "Workspace directory ready at $REPO_DIR"
fi

log "Starting Shopify AI App Builder service..."

# Use /opt/webchat directly as it contains the actual server.js with node_modules
# /opt/webchat_v2 is just a wrapper and causes module loading failures
CHAT_APP_DIR="${CHAT_APP_DIR:-/opt/webchat}"
CHAT_APP_FALLBACK="/opt/webchat_v2"
CHAT_PORT="${CHAT_PORT:-4500}"
CHAT_HOST="${CHAT_HOST:-0.0.0.0}"
ACCESS_PASSWORD="${ACCESS_PASSWORD:-}"

# Persist opencode installation & user config
# Move/Copy /root/.opencode to $REPO_DIR/.opencode if not already present
# Then symlink /root/.opencode -> $REPO_DIR/.opencode so opencode state (connections, providers)
# are persisted on container restarts while keeping the install accessible for runtime.
PERSISTED_OPENCODE_DIR="$REPO_DIR/.opencode"
OPENCODE_INSTALL_DIR="/root/.opencode"
if [ -d "$OPENCODE_INSTALL_DIR" ]; then
    # If persisted dir does not exist, copy initial files so the app continues to work
    if [ ! -d "$PERSISTED_OPENCODE_DIR" ] || [ -z "$(ls -A $PERSISTED_OPENCODE_DIR 2>/dev/null)" ]; then
        log "Persisting opencode into $PERSISTED_OPENCODE_DIR"
        mkdir -p "$PERSISTED_OPENCODE_DIR"
        # Copy installed files to the persisted folder to preserve both the binary and state
        cp -a "$OPENCODE_INSTALL_DIR/." "$PERSISTED_OPENCODE_DIR/" || true
        chown -R root:root "$PERSISTED_OPENCODE_DIR" || true
    fi
    # Replace the install dir with a symlink to the persisted directory (only if not a symlink already)
    if [ -e "$OPENCODE_INSTALL_DIR" ] && [ ! -L "$OPENCODE_INSTALL_DIR" ]; then
        log "Symlinking $OPENCODE_INSTALL_DIR -> $PERSISTED_OPENCODE_DIR"
        rm -rf "$OPENCODE_INSTALL_DIR"
        ln -s "$PERSISTED_OPENCODE_DIR" "$OPENCODE_INSTALL_DIR"
    elif [ -L "$OPENCODE_INSTALL_DIR" ]; then
        log "$OPENCODE_INSTALL_DIR already symlinked; skipping"
    fi
fi

# Only ensure opencode command exists - qwen and gemini commands should not be aliased to opencode
ensure_cli_wrappers() {
    local bin_dir="$PERSISTED_OPENCODE_DIR/bin"
    mkdir -p "$bin_dir"
    
    # Check if we have a custom-built binary at /usr/local/bin/opencode (not a symlink)
    if [ -f "/usr/local/bin/opencode" ] && [ ! -L "/usr/local/bin/opencode" ]; then
        # Copy the custom-built binary to the persisted directory
        log "Copying custom-built opencode binary to persisted directory"
        cp -f "/usr/local/bin/opencode" "$bin_dir/opencode"
        chmod +x "$bin_dir/opencode"
    fi
    
    # Only create symlink for opencode command itself
    if [ ! -L "/usr/local/bin/opencode" ]; then
        ln -sf "$bin_dir/opencode" "/usr/local/bin/opencode"
    fi
}
ensure_cli_wrappers

# Ensure a root-level opencode.json exists so OpenCode can discover the configured
# Ollama/OpenAI-compatible model. This file lives in the persisted storage root
# (/home/web/data) and is overwritten on every container start.
ensure_root_opencode_config() {
    local config_path="$REPO_DIR/opencode.json"

    # Allow overrides while defaulting to the PluginCompass Ollama gateway + qwen3 model
    export OPENCODE_OLLAMA_BASE_URL="${OPENCODE_OLLAMA_BASE_URL:-https://ollama.plugincompass.com}"
    export OPENCODE_OLLAMA_MODEL="${OPENCODE_OLLAMA_MODEL:-qwen3:0.6b}"

    # Prefer an explicit Ollama key, fall back to OPENCODE_API_KEY (existing env var)
    export OPENCODE_OLLAMA_API_KEY="${OPENCODE_OLLAMA_API_KEY:-${OPENCODE_API_KEY:-}}"

    mkdir -p "$(dirname "$config_path")"

    log "Writing OpenCode config: ${config_path} (baseURL=${OPENCODE_OLLAMA_BASE_URL}, model=${OPENCODE_OLLAMA_MODEL})"

    node - <<'NODE' > "$config_path"
const baseUrl = (process.env.OPENCODE_OLLAMA_BASE_URL || "https://ollama.plugincompass.com").replace(/\/+$/, "");
const modelId = process.env.OPENCODE_OLLAMA_MODEL || "qwen3:0.6b";
const apiKey = (process.env.OPENCODE_OLLAMA_API_KEY || "").trim();
const providerName = process.env.OPENCODE_OLLAMA_PROVIDER || "ollama";

const providerConfig = {
  options: {
    baseURL: baseUrl,
  },
  models: {
    [modelId]: {
      id: modelId,
      name: modelId,
      tool_call: true,
      temperature: true,
    },
  },
};

if (apiKey) {
  providerConfig.options.apiKey = apiKey;
}

const config = {
  $schema: "https://opencode.ai/config.json",
  model: `${providerName}/${modelId}`,
  small_model: `${providerName}/${modelId}`,
  provider: {
    [providerName]: providerConfig,
  },
};

process.stdout.write(`${JSON.stringify(config, null, 2)}\n`);
NODE

    chmod 600 "$config_path" 2>/dev/null || true
}
ensure_root_opencode_config

# Set up signal handlers to properly clean up background processes
cleanup() {
    local signal="$1"
    log "=== SIGNAL RECEIVED: ${signal} ==="
    log "Initiating graceful shutdown..."

    # Log final resource snapshot before shutdown
    if type monitor_resources &>/dev/null; then
        log "Final resource snapshot:"
        monitor_resources
    fi

    if [ -n "$MONITOR_PID" ] && kill -0 "$MONITOR_PID" 2>/dev/null; then
        log "Terminating monitor process (PID: $MONITOR_PID)"
        kill "$MONITOR_PID" 2>/dev/null || true
        wait "$MONITOR_PID" 2>/dev/null || true
        log "Monitor process terminated"
    fi

    if [ -n "$CHAT_PID" ] && kill -0 "$CHAT_PID" 2>/dev/null; then
        log "Terminating chat service (PID: $CHAT_PID) - giving it time for graceful shutdown"
        kill "$CHAT_PID" 2>/dev/null || true

        # Wait up to 25 seconds for graceful shutdown (Docker stop_grace_period is 30s)
        for i in $(seq 1 25); do
            if ! kill -0 "$CHAT_PID" 2>/dev/null; then
                log "Chat service terminated gracefully (${i} seconds)"
                if type diag_log &>/dev/null; then
                    diag_log "INFO" "Chat service shutdown complete"
                fi
                exit 0
            fi
            sleep 1
        done
        log "WARNING: Chat service did not terminate gracefully, forcing exit"
        kill -9 "$CHAT_PID" 2>/dev/null || true
    fi

    log "=== SHUTDOWN COMPLETE ==="
    exit 0
}

# Set up traps for common signals
trap cleanup SIGTERM SIGINT SIGQUIT SIGHUP

# Initialize database before starting chat service
log "=== DATABASE INITIALIZATION ==="
if [ -f "$CHAT_APP_DIR/scripts/init-database.js" ]; then
    log "Running database initialization..."
    if CHAT_DATA_ROOT=$REPO_DIR node "$CHAT_APP_DIR/scripts/init-database.js"; then
        log "Database initialization successful"
    else
        log "WARNING: Database initialization failed, but continuing startup"
    fi
elif [ -f "$CHAT_APP_FALLBACK/scripts/init-database.js" ]; then
    log "Running database initialization from fallback location..."
    if CHAT_DATA_ROOT=$REPO_DIR node "$CHAT_APP_FALLBACK/scripts/init-database.js"; then
        log "Database initialization successful"
    else
        log "WARNING: Database initialization failed, but continuing startup"
    fi
else
    log "Database initialization script not found, skipping..."
fi

if [ -f "$CHAT_APP_DIR/server.js" ]; then
    log "Launching chat service on ${CHAT_HOST}:${CHAT_PORT} from $CHAT_APP_DIR"
    log "Environment: CHAT_PORT=${CHAT_PORT} CHAT_HOST=${CHAT_HOST} CHAT_DATA_ROOT=${REPO_DIR} CHAT_REPO_ROOT=${REPO_DIR}"
    CHAT_PORT=$CHAT_PORT CHAT_HOST=$CHAT_HOST CHAT_DATA_ROOT=$REPO_DIR CHAT_REPO_ROOT=$REPO_DIR node "$CHAT_APP_DIR/server.js" 2>&1 &
    CHAT_PID=$!
    log "Chat service started with PID: $CHAT_PID"
elif [ -f "$CHAT_APP_FALLBACK/server.js" ]; then
    log "Primary chat service not found at $CHAT_APP_DIR, trying fallback at $CHAT_APP_FALLBACK"
    log "Launching chat service on ${CHAT_HOST}:${CHAT_PORT} from $CHAT_APP_FALLBACK"
    log "Environment: CHAT_PORT=${CHAT_PORT} CHAT_HOST=${CHAT_HOST} CHAT_DATA_ROOT=${REPO_DIR} CHAT_REPO_ROOT=${REPO_DIR}"
    CHAT_PORT=$CHAT_PORT CHAT_HOST=$CHAT_HOST CHAT_DATA_ROOT=$REPO_DIR CHAT_REPO_ROOT=$REPO_DIR node "$CHAT_APP_FALLBACK/server.js" 2>&1 &
    CHAT_PID=$!
    log "Chat service started with PID: $CHAT_PID"
else
    log "ERROR: Chat service not found at $CHAT_APP_DIR or $CHAT_APP_FALLBACK; skipping startup"
    exit 1
fi

# Log initial service status after startup
sleep 2
if type check_service_status &>/dev/null; then
    check_service_status "chat" "$CHAT_PORT" "$CHAT_PID"
fi

# Monitor chat service health
if [ -n "$CHAT_PID" ]; then
    (
        # Initial check
        sleep 5
        if type check_service_status &>/dev/null; then
            check_service_status "chat" "$CHAT_PORT" "$CHAT_PID"
        fi

        # Periodic monitoring
        check_count=0
        while kill -0 "$CHAT_PID" 2>/dev/null; do
            sleep 30
            check_count=$((check_count + 1))

            # Every 2 minutes (4 checks), do resource monitoring
            if [ $((check_count % 4)) -eq 0 ] && type monitor_resources &>/dev/null; then
                monitor_resources
            fi

            # Every 5 minutes (10 checks), do service status check
            if [ $((check_count % 10)) -eq 0 ] && type check_service_status &>/dev/null; then
                check_service_status "chat" "$CHAT_PORT" "$CHAT_PID"
            fi
        done
        log "ERROR: Chat service (PID: $CHAT_PID) has exited unexpectedly"
        if type diag_log &>/dev/null; then
            diag_log "ERROR" "Chat service exited unexpectedly after ${check_count} checks"
        fi
    ) &
    MONITOR_PID=$!
    log "Health monitor started with PID: $MONITOR_PID"
fi

# Keep the container running
log "Container started successfully. Waiting for chat service..."
wait "$CHAT_PID" || true
