#!/bin/bash
#
# LetsBe Cloud Environment Setup Script
# Non-interactive version for Orchestrator/SysAdmin Agent integration
#
# Usage:
#   ./env_setup.sh --customer "acme" --domain "acme.com" --company "Acme Corp"
#   ./env_setup.sh --json '{"customer":"acme","domain":"acme.com","company_name":"Acme Corp"}'
#   ./env_setup.sh --config /path/to/config.json
#

set -euo pipefail

# ============================================================================
# CONFIGURATION
# ============================================================================

LETSBE_BASE="/opt/letsbe"
STACKS_DIR="${LETSBE_BASE}/stacks"
NGINX_DIR="${LETSBE_BASE}/nginx"
ENV_DIR="${LETSBE_BASE}/env"
SCRIPTS_DIR="${LETSBE_BASE}/scripts"

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

usage() {
    cat <<EOF
Usage: $0 [OPTIONS]

Required (one of):
  --customer NAME      Customer name (lowercase, no spaces/hyphens/numbers)
  --domain DOMAIN      Main domain without subdomains (lowercase)
  --company NAME       Company name (can include spaces)

Or provide all via JSON:
  --json JSON_STRING   JSON object with customer, domain, company_name
  --config FILE        Path to JSON config file

Example:
  $0 --customer acme --domain acme.com --company "Acme Corporation"
  $0 --json '{"customer":"acme","domain":"acme.com","company_name":"Acme Corp"}'
  $0 --config /opt/letsbe/config/setup.json

EOF
    exit 1
}

log_info() {
    echo "[INFO] $*"
}

log_error() {
    echo "[ERROR] $*" >&2
}

die() {
    log_error "$*"
    exit 1
}

# Generate random string of specified length
generate_random_string() {
    local length=$1
    tr -dc A-Za-z0-9 </dev/urandom | head -c "${length}"
    echo ''
}

# Validate required variables are set
validate_required() {
    local var_name=$1
    local var_value=$2
    if [[ -z "${var_value}" ]]; then
        die "Required variable '${var_name}' is not set"
    fi
}

# Parse JSON input using jq
parse_json() {
    local json_input=$1

    if ! command -v jq &> /dev/null; then
        die "jq is required for JSON parsing. Install with: apt-get install jq"
    fi

    customer=$(echo "${json_input}" | jq -r '.customer // empty')
    domain=$(echo "${json_input}" | jq -r '.domain // empty')
    company_name=$(echo "${json_input}" | jq -r '.company_name // empty')

    # Hub / Licensing configuration
    license_key=$(echo "${json_input}" | jq -r '.license_key // empty')
    hub_url=$(echo "${json_input}" | jq -r '.hub_url // "https://hub.letsbe.biz"')
    hub_telemetry_enabled=$(echo "${json_input}" | jq -r '.hub_telemetry_enabled // true')

    # Optional server IP override (auto-detected if not provided)
    local json_server_ip
    json_server_ip=$(echo "${json_input}" | jq -r '.server_ip // empty')
    if [[ -n "${json_server_ip}" ]]; then
        server_ip_override="${json_server_ip}"
    fi
}

# ============================================================================
# ARGUMENT PARSING
# ============================================================================

customer=""
domain=""
company_name=""
docker_user=""
server_ip_override=""

# Hub / Licensing configuration
license_key=""
hub_api_key=""
hub_url="https://hub.letsbe.biz"
hub_telemetry_enabled="true"

while [[ $# -gt 0 ]]; do
    case $1 in
        --customer)
            customer="$2"
            shift 2
            ;;
        --domain)
            domain="$2"
            shift 2
            ;;
        --company)
            company_name="$2"
            shift 2
            ;;
        --docker-user)
            docker_user="$2"
            shift 2
            ;;
        --license-key)
            license_key="$2"
            shift 2
            ;;
        --hub-url)
            hub_url="$2"
            shift 2
            ;;
        --hub-api-key)
            hub_api_key="$2"
            shift 2
            ;;
        --hub-telemetry)
            hub_telemetry_enabled="$2"
            shift 2
            ;;
        --json)
            parse_json "$2"
            shift 2
            ;;
        --config)
            if [[ ! -f "$2" ]]; then
                die "Config file not found: $2"
            fi
            parse_json "$(cat "$2")"
            shift 2
            ;;
        --help|-h)
            usage
            ;;
        *)
            log_error "Unknown option: $1"
            usage
            ;;
    esac
done

# ============================================================================
# VALIDATION
# ============================================================================

validate_required "customer" "${customer}"
validate_required "domain" "${domain}"
validate_required "company_name" "${company_name}"

# Validate customer format (lowercase, no spaces/hyphens/numbers)
if [[ ! "${customer}" =~ ^[a-z]+$ ]]; then
    die "Customer name must be lowercase letters only, no spaces/hyphens/numbers: ${customer}"
fi

# Validate domain format
if [[ ! "${domain}" =~ ^[a-z0-9.-]+\.[a-z]{2,}$ ]]; then
    die "Invalid domain format: ${domain}"
fi

log_info "Configuration validated"
log_info "  Customer: ${customer}"
log_info "  Domain: ${domain}"
log_info "  Company: ${company_name}"

# ============================================================================
# DERIVED VARIABLES
# ============================================================================

# Email for Let's Encrypt
letsencrypt_email="postmaster@${domain}"

# Subdomains per tool
domain_html="html.${domain}"
domain_wordpress="${domain}"
domain_squidex="contenthub.${domain}"
domain_chatwoot="support.${domain}"
domain_chatwoot_helpdesk="helpdesk.${domain}"
domain_gitea="code.${domain}"
domain_gitea_drone="ci.${domain}"
domain_glitchtip="debug.${domain}"
domain_listmonk="newsletters.${domain}"
domain_n8n="n8n.${domain}"
domain_nextcloud="cloud.${domain}"
domain_penpot="design.${domain}"
domain_poste="mail.${domain}"
domain_umami="analytics.${domain}"
domain_uptime_kuma="uptime.${domain}"
domain_windmill="flows.${domain}"
domain_calcom="bookings.${domain}"
domain_odoo="crm.${domain}"
domain_collabora="collabora.${domain}"
domain_whiteboard="whiteboard.${domain}"
domain_signaling="signaling.${domain}"
domain_activepieces="automation.${domain}"
domain_minio="minio.${domain}"
domain_s3="s3.${domain}"
domain_librechat="ai.${domain}"
domain_bot_viewer="bots.${domain}"
domain_botlab="botlab.${domain}"
domain_nocodb="database.${domain}"
domain_redash="data.${domain}"
domain_documenso="signatures.${domain}"
domain_keycloak="auth.${domain}"
domain_pdf="pdf.${domain}"
domain_portainer="portainer.${domain}"
domain_ghost="${domain}"

# ============================================================================
# GENERATED SECRETS
# ============================================================================

log_info "Generating secrets and credentials..."

# WordPress
wordpresss_mariadb_root_password=$(generate_random_string 20)
wordpress_db_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
wordpress_db_password=$(generate_random_string 20)

# Squidex
squidex_adminemail="postmaster@${domain}"
squidex_adminpassword=$(generate_random_string 20)

# Listmonk
listmonk_admin_username=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
listmonk_admin_password=$(generate_random_string 20)
listmonk_db_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
listmonk_db_password=$(generate_random_string 20)

# Gitea
gitea_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
gitea_postgres_password=$(generate_random_string 20)

# Umami
umami_app_secret=$(generate_random_string 32)
umami_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
umami_postgres_password=$(generate_random_string 20)

# Drone/Gitea
drone_gitea_rpc_secret=$(generate_random_string 32)

# Windmill
windmill_database_password=$(generate_random_string 20)

# Glitchtip
glitchtip_database_password=$(generate_random_string 20)
glitchtip_secret_key=$(generate_random_string 32)

# Penpot
penpot_secret_key=$(generate_random_string 32)
penpot_db_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
penpot_db_password=$(generate_random_string 20)

# Nextcloud
nextcloud_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
nextcloud_postgres_password=$(generate_random_string 20)
nextcloud_jwt_secret=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')
nextcloud_admin_password=$(generate_random_string 20)

# Collabora
collabora_password=$(generate_random_string 20)
collabora_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')

# Nextcloud Talk HPB / TURN
turn_secret=$(openssl rand -hex 32)
signaling_secret=$(openssl rand -hex 32)
internal_secret=$(openssl rand -hex 32)

# Server public IP (needed for coturn external-ip)
if [[ -n "${server_ip_override}" ]]; then
    server_ip="${server_ip_override}"
    log_info "Using provided server IP: ${server_ip}"
else
    server_ip=$(curl -4 -s --max-time 10 ifconfig.co || curl -4 -s --max-time 10 icanhazip.com || echo "REPLACE_WITH_SERVER_IP")
    log_info "Auto-detected server IP: ${server_ip}"
fi

# Chatwoot
chatwoot_secret_key_base=$(generate_random_string 32)
chatwoot_redis_password=$(generate_random_string 20)
chatwoot_postgres_username=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
chatwoot_postgres_password=$(generate_random_string 20)
chatwoot_rails_inbound_email_password=$(generate_random_string 20)

# N8N
n8n_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
n8n_postgres_password=$(generate_random_string 20)

# Cal.com
calcom_nextauth_secret=$(generate_random_string 32)
calcom_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
calcom_postgres_password=$(generate_random_string 20)

# Odoo
odoo_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
odoo_postgres_password=$(generate_random_string 20)

# Activepieces
activepieces_api_key=$(generate_random_string 32)
activepieces_encryption_key=$(generate_random_string 32 | tr '[:upper:]' '[:lower:]')
activepieces_jwt_secret=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')
activepieces_postgres_password=$(generate_random_string 32)

# MinIO
minio_root_user=$(generate_random_string 16)
minio_root_password=$(generate_random_string 32)

# Typebot
typebot_encryption_secret=$(generate_random_string 32)
typebot_postgres_password=$(generate_random_string 20)

# NocoDB
nocodb_postgres_password=$(generate_random_string 32)

# LibreChat
librechat_postgres_password=$(generate_random_string 20)
librechat_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
librechat_jwt_secret=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')
librechat_jwt_refresh_secret=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')

# Redash
redash_secret_key=$(generate_random_string 32)
redash_cookie_secret=$(generate_random_string 32)
redash_postgres_password=$(generate_random_string 20)
redash_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')

# Documenso
documenso_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
documenso_postgres_password=$(generate_random_string 40)
documenso_nextauth_secret=$(generate_random_string 32)
documenso_encryption_key=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')
documenso_encryption_secondary_key=$(generate_random_string 64 | tr '[:upper:]' '[:lower:]')

# Ghost
ghost_mysql_password=$(generate_random_string 40)
ghost_s3_access_key=$(generate_random_string 20)
ghost_s3_secret_key=$(generate_random_string 40)

# Keycloak
keycloak_postgres_password=$(generate_random_string 40)
keycloak_admin_password=$(generate_random_string 40)
keycloak_grafana_password=$(generate_random_string 40)

# Portainer (admin credentials for automated setup)
# Note: --admin-password-file expects PLAIN TEXT password (not bcrypt hash)
# The username is always "admin" - cannot be changed via CLI
portainer_admin_user="admin"
portainer_admin_password=$(generate_random_string 24)

# StirlingPDF
stirlingpdf_postgres_user=$(generate_random_string 10 | tr '[:upper:]' '[:lower:]')
stirlingpdf_postgres_password=$(generate_random_string 40)
stirlingpdf_api_key=$(generate_random_string 40)

# Sysadmin Agent
# Registration token is now auto-generated by local_bootstrap.sh after license validation
# It calls the local orchestrator API to create a token
# Legacy: can still be provided via SYSADMIN_REGISTRATION_TOKEN env var for backwards compatibility
sysadmin_registration_token="${SYSADMIN_REGISTRATION_TOKEN:-PLACEHOLDER_GENERATED_BY_BOOTSTRAP}"
# Legacy token (deprecated, kept for backward compatibility)
sysadmin_agent_token=$(generate_random_string 64)

# ============================================================================
# ORCHESTRATOR / HUB CONFIGURATION
# ============================================================================

# Orchestrator database password (for local orchestrator)
orchestrator_db_password=$(generate_random_string 40)

# Admin API key for orchestrator management
admin_api_key=$(generate_random_string 64)

# Instance ID (must match what's registered in Hub)
instance_id="${customer}-orchestrator"

# Orchestrator URL for sysadmin agent
# LOCAL_MODE uses host.docker.internal to reach orchestrator from agent container
orchestrator_url="http://host.docker.internal:8100"

# Local agent key for LOCAL_MODE authentication
# Shared secret between orchestrator and sysadmin agent
local_agent_key=$(generate_random_string 64)

# Validate license key (required for official installations)
if [[ -z "${license_key}" || "${license_key}" == "lb_inst_YOUR_LICENSE_KEY_HERE" ]]; then
    log_error "=============================================="
    log_error "LICENSE KEY REQUIRED"
    log_error "=============================================="
    log_error ""
    log_error "A valid license key is required for official LetsBe installations."
    log_error ""
    log_error "To obtain a license key:"
    log_error "1. Contact LetsBe to create a client account in Hub"
    log_error "2. Request an instance to be created for your deployment"
    log_error "3. Add the license_key to your config.json:"
    log_error "   {\"license_key\": \"lb_inst_...\"}"
    log_error ""
    log_error "=============================================="
    die "Missing or invalid license_key in config.json"
fi

log_info "Hub configuration:"
log_info "  License Key: ${license_key:0:12}..."
log_info "  Hub URL: ${hub_url}"
log_info "  Instance ID: ${instance_id}"
log_info "  Telemetry: ${hub_telemetry_enabled}"

# ============================================================================
# TEMPLATE REPLACEMENT
# ============================================================================

log_info "Replacing placeholders in template files..."

# Process all template files
for file in "${STACKS_DIR}"/*/* "${STACKS_DIR}"/*/.* "${NGINX_DIR}"/* "${SCRIPTS_DIR}"/backups.sh; do
    if [[ -f "${file}" ]]; then
        # Core variables
        sed -i "s/{{ customer }}/${customer}/g" "${file}"
        sed -i "s/{{ domain }}/${domain}/g" "${file}"
        sed -i "s/{{ company_name }}/${company_name}/g" "${file}"
        sed -i "s/{{ letsencrypt_email }}/${letsencrypt_email}/g" "${file}"

        # Domain variables
        sed -i "s/{{ domain_html }}/${domain_html}/g" "${file}"
        sed -i "s/{{ domain_wordpress }}/${domain_wordpress}/g" "${file}"
        sed -i "s/{{ domain_squidex }}/${domain_squidex}/g" "${file}"
        sed -i "s/{{ domain_chatwoot }}/${domain_chatwoot}/g" "${file}"
        sed -i "s/{{ domain_chatwoot_helpdesk }}/${domain_chatwoot_helpdesk}/g" "${file}"
        sed -i "s/{{ domain_gitea }}/${domain_gitea}/g" "${file}"
        sed -i "s/{{ domain_gitea_drone }}/${domain_gitea_drone}/g" "${file}"
        sed -i "s/{{ domain_glitchtip }}/${domain_glitchtip}/g" "${file}"
        sed -i "s/{{ domain_listmonk }}/${domain_listmonk}/g" "${file}"
        sed -i "s/{{ domain_librechat }}/${domain_librechat}/g" "${file}"
        sed -i "s/{{ domain_n8n }}/${domain_n8n}/g" "${file}"
        sed -i "s/{{ domain_nextcloud }}/${domain_nextcloud}/g" "${file}"
        sed -i "s/{{ domain_penpot }}/${domain_penpot}/g" "${file}"
        sed -i "s/{{ domain_poste }}/${domain_poste}/g" "${file}"
        sed -i "s/{{ domain_umami }}/${domain_umami}/g" "${file}"
        sed -i "s/{{ domain_uptime_kuma }}/${domain_uptime_kuma}/g" "${file}"
        sed -i "s/{{ domain_windmill }}/${domain_windmill}/g" "${file}"
        sed -i "s/{{ domain_calcom }}/${domain_calcom}/g" "${file}"
        sed -i "s/{{ domain_odoo }}/${domain_odoo}/g" "${file}"
        sed -i "s/{{ domain_collabora }}/${domain_collabora}/g" "${file}"
        sed -i "s/{{ domain_activepieces }}/${domain_activepieces}/g" "${file}"
        sed -i "s/{{ domain_bot_viewer }}/${domain_bot_viewer}/g" "${file}"
        sed -i "s/{{ domain_botlab }}/${domain_botlab}/g" "${file}"
        sed -i "s/{{ domain_minio }}/${domain_minio}/g" "${file}"
        sed -i "s/{{ domain_s3 }}/${domain_s3}/g" "${file}"
        sed -i "s/{{ domain_nocodb }}/${domain_nocodb}/g" "${file}"
        sed -i "s/{{ domain_whiteboard }}/${domain_whiteboard}/g" "${file}"
        sed -i "s/{{ domain_signaling }}/${domain_signaling}/g" "${file}"
        sed -i "s/{{ domain_redash }}/${domain_redash}/g" "${file}"
        sed -i "s/{{ domain_documenso }}/${domain_documenso}/g" "${file}"
        sed -i "s/{{ domain_keycloak }}/${domain_keycloak}/g" "${file}"
        sed -i "s/{{ domain_pdf }}/${domain_pdf}/g" "${file}"
        sed -i "s/{{ domain_portainer }}/${domain_portainer}/g" "${file}"
        sed -i "s/{{ domain_ghost }}/${domain_ghost}/g" "${file}"

        # Credential variables
        sed -i "s/{{ wordpresss_mariadb_root_password }}/${wordpresss_mariadb_root_password}/g" "${file}"
        sed -i "s/{{ wordpress_db_user }}/${wordpress_db_user}/g" "${file}"
        sed -i "s/{{ wordpress_db_password }}/${wordpress_db_password}/g" "${file}"
        sed -i "s/{{ squidex_adminemail }}/${squidex_adminemail}/g" "${file}"
        sed -i "s/{{ squidex_adminpassword }}/${squidex_adminpassword}/g" "${file}"
        sed -i "s/{{ listmonk_admin_username }}/${listmonk_admin_username}/g" "${file}"
        sed -i "s/{{ listmonk_admin_password }}/${listmonk_admin_password}/g" "${file}"
        sed -i "s/{{ listmonk_db_user }}/${listmonk_db_user}/g" "${file}"
        sed -i "s/{{ listmonk_db_password }}/${listmonk_db_password}/g" "${file}"
        sed -i "s/{{ gitea_postgres_user }}/${gitea_postgres_user}/g" "${file}"
        sed -i "s/{{ gitea_postgres_password }}/${gitea_postgres_password}/g" "${file}"
        sed -i "s/{{ umami_app_secret }}/${umami_app_secret}/g" "${file}"
        sed -i "s/{{ umami_postgres_user }}/${umami_postgres_user}/g" "${file}"
        sed -i "s/{{ umami_postgres_password }}/${umami_postgres_password}/g" "${file}"
        sed -i "s/{{ drone_gitea_rpc_secret }}/${drone_gitea_rpc_secret}/g" "${file}"
        sed -i "s/{{ windmill_database_password }}/${windmill_database_password}/g" "${file}"
        sed -i "s/{{ glitchtip_database_password }}/${glitchtip_database_password}/g" "${file}"
        sed -i "s/{{ glitchtip_secret_key }}/${glitchtip_secret_key}/g" "${file}"
        sed -i "s/{{ penpot_secret_key }}/${penpot_secret_key}/g" "${file}"
        sed -i "s/{{ penpot_db_user }}/${penpot_db_user}/g" "${file}"
        sed -i "s/{{ penpot_db_password }}/${penpot_db_password}/g" "${file}"
        sed -i "s/{{ nextcloud_postgres_user }}/${nextcloud_postgres_user}/g" "${file}"
        sed -i "s/{{ nextcloud_postgres_password }}/${nextcloud_postgres_password}/g" "${file}"
        sed -i "s/{{ nextcloud_admin_password }}/${nextcloud_admin_password}/g" "${file}"
        sed -i "s/{{ nextcloud_jwt_secret }}/${nextcloud_jwt_secret}/g" "${file}"
        sed -i "s/{{ collabora_password }}/${collabora_password}/g" "${file}"
        sed -i "s/{{ collabora_user }}/${collabora_user}/g" "${file}"
        sed -i "s/{{ turn_secret }}/${turn_secret}/g" "${file}"
        sed -i "s/{{ signaling_secret }}/${signaling_secret}/g" "${file}"
        sed -i "s/{{ internal_secret }}/${internal_secret}/g" "${file}"
        sed -i "s/{{ server_ip }}/${server_ip}/g" "${file}"
        sed -i "s/{{ chatwoot_secret_key_base }}/${chatwoot_secret_key_base}/g" "${file}"
        sed -i "s/{{ chatwoot_redis_password }}/${chatwoot_redis_password}/g" "${file}"
        sed -i "s/{{ chatwoot_postgres_username }}/${chatwoot_postgres_username}/g" "${file}"
        sed -i "s/{{ chatwoot_postgres_password }}/${chatwoot_postgres_password}/g" "${file}"
        sed -i "s/{{ chatwoot_rails_inbound_email_password }}/${chatwoot_rails_inbound_email_password}/g" "${file}"
        sed -i "s/{{ n8n_postgres_user }}/${n8n_postgres_user}/g" "${file}"
        sed -i "s/{{ n8n_postgres_password }}/${n8n_postgres_password}/g" "${file}"
        sed -i "s/{{ calcom_nextauth_secret }}/${calcom_nextauth_secret}/g" "${file}"
        sed -i "s/{{ calcom_postgres_user }}/${calcom_postgres_user}/g" "${file}"
        sed -i "s/{{ calcom_postgres_password }}/${calcom_postgres_password}/g" "${file}"
        sed -i "s/{{ odoo_postgres_user }}/${odoo_postgres_user}/g" "${file}"
        sed -i "s/{{ odoo_postgres_password }}/${odoo_postgres_password}/g" "${file}"
        sed -i "s/{{ activepieces_api_key }}/${activepieces_api_key}/g" "${file}"
        sed -i "s/{{ activepieces_encryption_key }}/${activepieces_encryption_key}/g" "${file}"
        sed -i "s/{{ activepieces_jwt_secret }}/${activepieces_jwt_secret}/g" "${file}"
        sed -i "s/{{ activepieces_postgres_password }}/${activepieces_postgres_password}/g" "${file}"
        sed -i "s/{{ minio_root_user }}/${minio_root_user}/g" "${file}"
        sed -i "s/{{ minio_root_password }}/${minio_root_password}/g" "${file}"
        sed -i "s/{{ typebot_encryption_secret }}/${typebot_encryption_secret}/g" "${file}"
        sed -i "s/{{ nocodb_postgres_password }}/${nocodb_postgres_password}/g" "${file}"
        sed -i "s/{{ typebot_postgres_password }}/${typebot_postgres_password}/g" "${file}"
        sed -i "s/{{ redash_secret_key }}/${redash_secret_key}/g" "${file}"
        sed -i "s/{{ redash_cookie_secret }}/${redash_cookie_secret}/g" "${file}"
        sed -i "s/{{ redash_postgres_user }}/${redash_postgres_user}/g" "${file}"
        sed -i "s/{{ redash_postgres_password }}/${redash_postgres_password}/g" "${file}"
        sed -i "s/{{ librechat_postgres_password }}/${librechat_postgres_password}/g" "${file}"
        sed -i "s/{{ librechat_postgres_user }}/${librechat_postgres_user}/g" "${file}"
        sed -i "s/{{ librechat_jwt_secret }}/${librechat_jwt_secret}/g" "${file}"
        sed -i "s/{{ librechat_jwt_refresh_secret }}/${librechat_jwt_refresh_secret}/g" "${file}"
        sed -i "s/{{ documenso_postgres_user }}/${documenso_postgres_user}/g" "${file}"
        sed -i "s/{{ documenso_postgres_password }}/${documenso_postgres_password}/g" "${file}"
        sed -i "s/{{ documenso_nextauth_secret }}/${documenso_nextauth_secret}/g" "${file}"
        sed -i "s/{{ documenso_encryption_key }}/${documenso_encryption_key}/g" "${file}"
        sed -i "s/{{ documenso_encryption_secondary_key }}/${documenso_encryption_secondary_key}/g" "${file}"
        sed -i "s/{{ ghost_mysql_password }}/${ghost_mysql_password}/g" "${file}"
        sed -i "s/{{ ghost_s3_access_key }}/${ghost_s3_access_key}/g" "${file}"
        sed -i "s/{{ ghost_s3_secret_key }}/${ghost_s3_secret_key}/g" "${file}"
        sed -i "s/{{ keycloak_postgres_password }}/${keycloak_postgres_password}/g" "${file}"
        sed -i "s/{{ keycloak_admin_password }}/${keycloak_admin_password}/g" "${file}"
        sed -i "s/{{ keycloak_grafana_password }}/${keycloak_grafana_password}/g" "${file}"
        sed -i "s/{{ stirlingpdf_postgres_user }}/${stirlingpdf_postgres_user}/g" "${file}"
        sed -i "s/{{ stirlingpdf_postgres_password }}/${stirlingpdf_postgres_password}/g" "${file}"
        sed -i "s/{{ stirlingpdf_api_key }}/${stirlingpdf_api_key}/g" "${file}"
        sed -i "s/{{ sysadmin_agent_token }}/${sysadmin_agent_token}/g" "${file}"
        sed -i "s/{{ sysadmin_registration_token }}/${sysadmin_registration_token}/g" "${file}"

        # Hub / Orchestrator variables
        sed -i "s/{{ license_key }}/${license_key}/g" "${file}"
        sed -i "s/{{ hub_api_key }}/${hub_api_key}/g" "${file}"
        sed -i "s|{{ hub_url }}|${hub_url}|g" "${file}"
        sed -i "s/{{ hub_telemetry_enabled }}/${hub_telemetry_enabled}/g" "${file}"
        sed -i "s/{{ instance_id }}/${instance_id}/g" "${file}"
        sed -i "s/{{ orchestrator_db_password }}/${orchestrator_db_password}/g" "${file}"
        sed -i "s/{{ admin_api_key }}/${admin_api_key}/g" "${file}"
        sed -i "s|{{ orchestrator_url }}|${orchestrator_url}|g" "${file}"
        sed -i "s/{{ local_agent_key }}/${local_agent_key}/g" "${file}"
    fi
done

log_info "All placeholders replaced successfully."

# ============================================================================
# GENERATE ENV FILES
# ============================================================================

log_info "Generating centralized environment files..."

mkdir -p "${ENV_DIR}"

# Write master credentials file for reference
cat > "${ENV_DIR}/credentials.env" <<EOF
# LetsBe Cloud Credentials - Generated $(date -Iseconds)
# Customer: ${customer}
# Domain: ${domain}
# Company: ${company_name}
#
# KEEP THIS FILE SECURE - Contains all generated passwords
#

# WordPress
WORDPRESS_DB_USER=${wordpress_db_user}
WORDPRESS_DB_PASSWORD=${wordpress_db_password}
WORDPRESS_MARIADB_ROOT_PASSWORD=${wordpresss_mariadb_root_password}

# Nextcloud
NEXTCLOUD_ADMIN_PASSWORD=${nextcloud_admin_password}
NEXTCLOUD_POSTGRES_USER=${nextcloud_postgres_user}
NEXTCLOUD_POSTGRES_PASSWORD=${nextcloud_postgres_password}

# Nextcloud Talk HPB / TURN
TURN_SECRET=${turn_secret}
SIGNALING_SECRET=${signaling_secret}
INTERNAL_SECRET=${internal_secret}
SERVER_IP=${server_ip}

# Listmonk
LISTMONK_ADMIN_USER=${listmonk_admin_username}
LISTMONK_ADMIN_PASSWORD=${listmonk_admin_password}

# MinIO
MINIO_ROOT_USER=${minio_root_user}
MINIO_ROOT_PASSWORD=${minio_root_password}

# Keycloak
KEYCLOAK_ADMIN_PASSWORD=${keycloak_admin_password}

# Portainer
PORTAINER_ADMIN_USER=${portainer_admin_user}
PORTAINER_ADMIN_PASSWORD=${portainer_admin_password}

# Sysadmin Agent
# Note: Registration token is generated by local_bootstrap.sh after license validation.
# It calls the local orchestrator to create a one-time registration token.
# After initial registration, agent credentials are persisted to ~/.letsbe-agent/credentials.json
# SYSADMIN_REGISTRATION_TOKEN will be written to sysadmin-credentials.env by bootstrap
# SYSADMIN_AGENT_TOKEN=${sysadmin_agent_token}  # Deprecated

# ============ HUB / ORCHESTRATOR ============
# License key (validated during bootstrap)
LICENSE_KEY=${license_key}
HUB_API_KEY=${hub_api_key}
HUB_URL=${hub_url}
HUB_TELEMETRY_ENABLED=${hub_telemetry_enabled}
INSTANCE_ID=${instance_id}

# Local orchestrator credentials
ORCHESTRATOR_DB_PASSWORD=${orchestrator_db_password}
ADMIN_API_KEY=${admin_api_key}

# Local agent authentication (shared between orchestrator and sysadmin agent)
LOCAL_AGENT_KEY=${local_agent_key}
EOF

# Add Docker Hub section if docker_user was provided
if [[ -n "${docker_user}" ]]; then
cat >> "${ENV_DIR}/credentials.env" <<EOF

# Docker Hub
DOCKER_HUB_USER=${docker_user}
# Note: Token not stored for security - regenerate from Docker Hub if needed
EOF
fi

chmod 600 "${ENV_DIR}/credentials.env"

# Write Portainer admin password file (for automated admin setup)
# This file is mounted into the Portainer container and used with --admin-password-file
# NOTE: --admin-password-file expects PLAIN TEXT password, NOT bcrypt hash
# (--admin-password expects bcrypt hash, but --admin-password-file expects plain text)
printf '%s' "${portainer_admin_password}" > "${ENV_DIR}/portainer_admin_password.txt"
chmod 600 "${ENV_DIR}/portainer_admin_password.txt"
log_info "Portainer admin password file saved to: ${ENV_DIR}/portainer_admin_password.txt"

log_info "Environment setup complete."
log_info "Credentials saved to: ${ENV_DIR}/credentials.env"
