#!/bin/bash
#
# LetsBe Ansible Runner - Hub-Integrated Entrypoint
#
# This script is the entry point for the containerized Ansible runner.
# It streams logs to the Hub API and updates job status on completion/failure.
#

set -euo pipefail

# Configuration
HUB_API_URL="${HUB_API_URL:-https://hub.letsbe.solutions}"
JOB_ID="${JOB_ID:-}"
RUNNER_TOKEN="${RUNNER_TOKEN:-}"
JOB_CONFIG_PATH="${JOB_CONFIG_PATH:-/job/config.json}"
CURRENT_STEP="init"
COMPLETION_MARKED=false

# Portainer credentials (read from remote server)
PORTAINER_USER=""
PORTAINER_PASS=""

# Global file for passing result JSON (avoids bash variable expansion issues)
RESULT_FILE="/tmp/job_result.json"

# Logging to Hub API
log_to_hub() {
    local level="$1" message="$2" step="${3:-$CURRENT_STEP}" progress="${4:-}"
    echo "[$(date '+%H:%M:%S')] [$level] [$step] $message"
    if [[ -n "$JOB_ID" && -n "$RUNNER_TOKEN" ]]; then
        local payload="{\"level\":\"${level}\",\"message\":\"${message}\",\"step\":\"${step}\""
        [[ -n "$progress" ]] && payload="${payload},\"progress\":${progress}"
        payload="${payload}}"
        curl -s -X POST "${HUB_API_URL}/api/v1/jobs/${JOB_ID}/logs" \
            -H "X-Runner-Token: ${RUNNER_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "$payload" > /dev/null 2>&1 || true
    fi
}

log_info() { log_to_hub "info" "$1" "${2:-$CURRENT_STEP}" "${3:-}"; }
log_warn() { log_to_hub "warn" "$1" "${2:-$CURRENT_STEP}" "${3:-}"; }
log_error() { log_to_hub "error" "$1" "${2:-$CURRENT_STEP}" "${3:-}"; }

update_job_status() {
    local status="$1" error="${2:-}"
    if [[ -n "$JOB_ID" && -n "$RUNNER_TOKEN" ]]; then
        local payload_file="/tmp/payload_$$.json"

        log_info "update_job_status called with status=$status" "debug"

        # Check if result file exists and has content
        if [[ -f "$RESULT_FILE" && -s "$RESULT_FILE" ]]; then
            log_info "Result file exists, size: $(wc -c < "$RESULT_FILE") bytes" "debug"

            # Validate it's proper JSON
            if jq -e . "$RESULT_FILE" > /dev/null 2>&1; then
                log_info "Result file contains valid JSON" "debug"
                # Build payload with result embedded
                jq -n --arg status "$status" --slurpfile result "$RESULT_FILE" \
                    '{status: $status, result: $result[0]}' > "$payload_file"
            else
                log_error "Result file is not valid JSON" "debug"
                jq -n --arg status "$status" '{status: $status}' > "$payload_file"
            fi
        elif [[ -n "$error" ]]; then
            log_info "Building error payload" "debug"
            jq -n --arg status "$status" --arg error "$error" \
                '{status: $status, error: $error}' > "$payload_file"
        else
            log_info "Building status-only payload" "debug"
            jq -n --arg status "$status" '{status: $status}' > "$payload_file"
        fi

        # Show payload size (not content, to avoid log issues)
        log_info "Payload size: $(wc -c < "$payload_file") bytes" "debug"

        # Send to Hub
        local http_code
        http_code=$(curl -s -w "%{http_code}" -o /tmp/hub_response.txt -X PATCH "${HUB_API_URL}/api/v1/jobs/${JOB_ID}" \
            -H "X-Runner-Token: ${RUNNER_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "@$payload_file") || true

        log_info "Hub responded with HTTP $http_code" "debug"

        # Cleanup
        rm -f "$payload_file" /tmp/hub_response.txt
    fi
}

mark_completed() {
    log_info "mark_completed called" "complete"

    if [[ "$COMPLETION_MARKED" == "true" ]]; then
        log_info "Completion already marked, skipping" "complete"
        return
    fi
    COMPLETION_MARKED=true

    log_info "Job completed" "complete" "100"

    # Check if result file was prepared
    if [[ -f "$RESULT_FILE" ]]; then
        log_info "Result file ready: $(wc -c < "$RESULT_FILE") bytes" "complete"
    else
        log_warn "No result file found" "complete"
    fi

    log_info "Calling update_job_status" "complete"
    update_job_status "completed"
    log_info "update_job_status returned" "complete"
}

mark_failed() {
    if [[ "$COMPLETION_MARKED" == "true" ]]; then
        log_info "Completion already marked, skipping failure" "failed"
        return
    fi
    COMPLETION_MARKED=true

    log_error "Job failed: $1" "failed"
    update_job_status "failed" "$1"
}

cleanup() {
    local exit_code=$?
    if [[ $exit_code -ne 0 && "$COMPLETION_MARKED" != "true" ]]; then
        mark_failed "Script exited with code $exit_code"
    fi
}
trap cleanup EXIT

# SSH helpers
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30"
ssh_run() { sshpass -p "$SERVER_PASSWORD" ssh $SSH_OPTS -p "$SERVER_PORT" root@"$SERVER_IP" "$@"; }
scp_upload() { sshpass -p "$SERVER_PASSWORD" scp $SSH_OPTS -P "$SERVER_PORT" "$1" root@"$SERVER_IP":"$2"; }
scp_upload_dir() { sshpass -p "$SERVER_PASSWORD" scp -r $SSH_OPTS -P "$SERVER_PORT" "$1" root@"$SERVER_IP":"$2"; }

# Docker registry login on target server
step_docker_login() {
    CURRENT_STEP="docker-login"

    # Read Docker Hub credentials from config
    local dockerhub_user=$(jq -r '.dockerHub.username // empty' "$JOB_CONFIG_PATH")
    local dockerhub_token=$(jq -r '.dockerHub.token // empty' "$JOB_CONFIG_PATH")
    local dockerhub_registry=$(jq -r '.dockerHub.registry // empty' "$JOB_CONFIG_PATH")

    # Read Gitea credentials from config
    local gitea_registry=$(jq -r '.gitea.registry // empty' "$JOB_CONFIG_PATH")
    local gitea_user=$(jq -r '.gitea.username // empty' "$JOB_CONFIG_PATH")
    local gitea_token=$(jq -r '.gitea.token // empty' "$JOB_CONFIG_PATH")

    # Login to Docker Hub if credentials provided
    if [[ -n "$dockerhub_user" && -n "$dockerhub_token" ]]; then
        log_info "Logging into Docker Hub" "docker-login" "12"
        if [[ -n "$dockerhub_registry" ]]; then
            ssh_run "echo '$dockerhub_token' | docker login -u '$dockerhub_user' --password-stdin '$dockerhub_registry'" 2>&1 || log_warn "Docker Hub login failed"
        else
            ssh_run "echo '$dockerhub_token' | docker login -u '$dockerhub_user' --password-stdin" 2>&1 || log_warn "Docker Hub login failed"
        fi
        log_info "Docker Hub login complete" "docker-login"
    else
        log_info "No Docker Hub credentials, skipping login" "docker-login"
    fi

    # Login to Gitea registry if credentials provided
    if [[ -n "$gitea_registry" && -n "$gitea_user" && -n "$gitea_token" ]]; then
        log_info "Logging into Gitea registry: $gitea_registry" "docker-login" "14"
        ssh_run "echo '$gitea_token' | docker login -u '$gitea_user' --password-stdin '$gitea_registry'" 2>&1 || log_warn "Gitea registry login failed"
        log_info "Gitea registry login complete" "docker-login"
    else
        log_info "No Gitea credentials, skipping registry login" "docker-login"
    fi
}

# Load config
load_config() {
    CURRENT_STEP="config"
    log_info "Loading job configuration"
    [[ ! -f "$JOB_CONFIG_PATH" ]] && { log_error "Config not found"; exit 1; }
    SERVER_IP=$(jq -r '.server.ip' "$JOB_CONFIG_PATH")
    SERVER_PORT=$(jq -r '.server.port // 22' "$JOB_CONFIG_PATH")
    SERVER_PASSWORD=$(jq -r '.server.rootPassword' "$JOB_CONFIG_PATH")
    CUSTOMER=$(jq -r '.customer' "$JOB_CONFIG_PATH")
    DOMAIN=$(jq -r '.domain' "$JOB_CONFIG_PATH")
    COMPANY_NAME=$(jq -r '.companyName' "$JOB_CONFIG_PATH")
    LICENSE_KEY=$(jq -r '.licenseKey' "$JOB_CONFIG_PATH")
    DASHBOARD_TIER=$(jq -r '.dashboardTier' "$JOB_CONFIG_PATH")
    TOOLS=$(jq -r '.tools | join(",")' "$JOB_CONFIG_PATH")
    log_info "Config: $DOMAIN on $SERVER_IP" "config" "5"
}

# Provisioning steps
step_prepare() { CURRENT_STEP="prepare"; log_info "Creating directories" "prepare" "10"; ssh_run "mkdir -p /opt/letsbe/{scripts,env,stacks,nginx,config}"; }

step_upload() {
    CURRENT_STEP="upload"
    log_info "Uploading files" "upload" "20"
    scp_upload "/workspace/scripts/env_setup.sh" "/opt/letsbe/scripts/"
    scp_upload "/workspace/scripts/setup.sh" "/opt/letsbe/scripts/"
    ssh_run "chmod +x /opt/letsbe/scripts/*.sh"
    log_info "Uploading nginx" "upload" "30"
    scp_upload_dir "/workspace/nginx/." "/opt/letsbe/nginx/"
    log_info "Uploading stacks" "upload" "40"
    scp_upload_dir "/workspace/stacks/." "/opt/letsbe/stacks/"
}

step_env() {
    CURRENT_STEP="env"
    log_info "Setting up environment" "env" "50"
    ssh_run "bash /opt/letsbe/scripts/env_setup.sh --customer '$CUSTOMER' --domain '$DOMAIN' --company '$COMPANY_NAME' --license-key '$LICENSE_KEY'"
}

# Read Portainer credentials from the remote server
read_portainer_credentials() {
    CURRENT_STEP="credentials"
    log_info "Reading Portainer credentials from server" "credentials" "55"

    # Read credentials.env from remote server
    local creds_content
    creds_content=$(ssh_run "cat /opt/letsbe/env/credentials.env 2>/dev/null" || echo "")

    if [[ -n "$creds_content" ]]; then
        # Extract Portainer credentials
        PORTAINER_USER=$(echo "$creds_content" | grep "^PORTAINER_ADMIN_USER=" | cut -d'=' -f2 | tr -d '\r\n' || echo "")
        PORTAINER_PASS=$(echo "$creds_content" | grep "^PORTAINER_ADMIN_PASSWORD=" | cut -d'=' -f2 | tr -d '\r\n' || echo "")

        log_info "Portainer user available: ${PORTAINER_USER:-none}" "credentials"
        log_info "Portainer pass available: $([ -n "$PORTAINER_PASS" ] && echo "yes" || echo "no")" "credentials"
    else
        log_warn "Could not read credentials.env from server" "credentials"
    fi
}

step_setup() {
    CURRENT_STEP="setup"
    log_info "Running setup (10-15 min)" "setup" "60"

    # Run setup script - note: setup.sh restarts SSH at the end which drops the connection
    # This is expected behavior, so we ignore the exit code from the SSH command
    set +e
    ssh_run "bash /opt/letsbe/scripts/setup.sh --tools '$TOOLS' --domain '$DOMAIN'" 2>&1 | while read -r line; do
        [[ -n "$line" ]] && log_info "$line" "setup"
    done
    local setup_exit=$?
    set -e

    # Give SSH time to restart on new port
    sleep 3

    # Verify setup completed by checking if we can connect on new SSH port (22022)
    log_info "Verifying setup completion on new SSH port..." "setup"
    if sshpass -p "$SERVER_PASSWORD" ssh $SSH_OPTS -p 22022 root@"$SERVER_IP" "echo 'SSH reconnected on port 22022'" 2>/dev/null; then
        log_info "Setup complete - SSH accessible on port 22022" "setup" "90"
    else
        # Try original port as fallback (maybe SSH restart didn't happen)
        if ssh_run "echo 'SSH still on original port'" 2>/dev/null; then
            log_info "Setup complete - SSH still on original port" "setup" "90"
        else
            log_warn "Could not verify SSH connectivity, but setup logs indicate completion" "setup"
        fi
    fi
}

step_finalize() {
    CURRENT_STEP="finalize"
    log_info "Finalizing" "finalize" "95"

    log_info "Portainer user: '${PORTAINER_USER:-}'" "finalize"
    log_info "Portainer pass length: ${#PORTAINER_PASS}" "finalize"

    # Write result JSON directly to file (avoids all bash variable issues)
    if [[ -n "$PORTAINER_USER" && -n "$PORTAINER_PASS" ]]; then
        log_info "Building result with Portainer credentials" "finalize"
        jq -n -c \
            --arg dashboard_url "https://dashboard.${DOMAIN}" \
            --arg portainer_url "https://${SERVER_IP}:9443" \
            --arg portainer_username "$PORTAINER_USER" \
            --arg portainer_password "$PORTAINER_PASS" \
            '{dashboard_url:$dashboard_url,portainer_url:$portainer_url,portainer_username:$portainer_username,portainer_password:$portainer_password}' \
            > "$RESULT_FILE"
    else
        log_warn "No Portainer credentials available" "finalize"
        jq -n -c \
            --arg dashboard_url "https://dashboard.${DOMAIN}" \
            --arg portainer_url "https://${SERVER_IP}:9443" \
            '{dashboard_url:$dashboard_url,portainer_url:$portainer_url}' \
            > "$RESULT_FILE"
    fi

    # Verify the file was created and contains valid JSON
    if [[ -f "$RESULT_FILE" ]]; then
        log_info "Result file created: $(wc -c < "$RESULT_FILE") bytes" "finalize"
        if jq -e . "$RESULT_FILE" > /dev/null 2>&1; then
            log_info "Result file validated as JSON" "finalize"
        else
            log_error "Result file is NOT valid JSON!" "finalize"
        fi
    else
        log_error "Failed to create result file!" "finalize"
    fi

    log_info "Calling mark_completed" "finalize"
    mark_completed
    log_info "mark_completed returned" "finalize"
}

# Main
main() {
    log_info "LetsBe Runner starting"
    load_config
    step_prepare
    step_docker_login
    step_upload
    step_env
    read_portainer_credentials
    step_setup
    step_finalize
}

main
