#!/usr/bin/env bash
set -euo pipefail
umask 027

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=/dev/null
source "$SCRIPT_DIR/libenv.sh"

LOG_FILE=${LIMRISTEM_MAIL_LOG_FILE:-/var/log/limristem-mail-install.log}
INSTALL_WARNINGS=()

_setup_logging() {
  mkdir -p "$(dirname "$LOG_FILE")"
  : > "$LOG_FILE"
  chmod 600 "$LOG_FILE"
  exec > >(tee -a "$LOG_FILE") 2>&1
}

log() { printf "[limristem-mail] %s\n" "$*"; }

warn() {
  log "ATTENZIONE: $*"
  INSTALL_WARNINGS+=("$*")
}

require_root() {
  if [[ $EUID -ne 0 ]]; then
    log "Questo script va eseguito come root (sudo)."
    exit 1
  fi
}

require_supported_os() {
  if [[ ! -f /etc/os-release ]]; then
    log "Impossibile determinare il sistema operativo."
    exit 1
  fi
  # shellcheck disable=SC1091
  source /etc/os-release
  if [[ ${ID:-} != "debian" ]]; then
    log "Limristem eMail supporta solo Debian 13+ in questo installer."
    exit 1
  fi
  local version_major=${VERSION_ID%%.*}
  if [[ -z "$version_major" || "$version_major" -lt 13 ]]; then
    log "Debian 13+ richiesto. Versione rilevata: ${VERSION_ID:-unknown}"
    exit 1
  fi
}

repair_dovecot_24_compat() {
  local tmp_file
  if [[ -f /etc/dovecot/conf.d/10-ssl-limristem-mail.conf ]] && grep -qE '^(ssl_(cert|key) = <|ssl_min_protocol[[:space:]]*=|ssl_cipher_list[[:space:]]*=)' /etc/dovecot/conf.d/10-ssl-limristem-mail.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 10-ssl-limristem-mail.conf"
    backup_file /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
    sed -i -E \
      -e 's/^ssl_cert = <(.*)$/ssl_server_cert_file = \1/' \
      -e 's/^ssl_key = <(.*)$/ssl_server_key_file = \1/' \
      -e '/^ssl_min_protocol[[:space:]]*=.*/d' \
      -e '/^ssl_cipher_list[[:space:]]*=.*/d' \
      /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
  fi

  if [[ -f /etc/dovecot/conf.d/10-auth.conf ]] && grep -qE '^disable_plaintext_auth[[:space:]]*=' /etc/dovecot/conf.d/10-auth.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 10-auth.conf"
    backup_file /etc/dovecot/conf.d/10-auth.conf
    tmp_file=$(mktemp)
    awk '
      /^disable_plaintext_auth[[:space:]]*=/ {
        value = $0
        sub(/^[^=]*=[[:space:]]*/, "", value)
        value = tolower(value)
        if (value == "yes" || value == "true") {
          print "auth_allow_cleartext = no"
        } else {
          print "auth_allow_cleartext = yes"
        }
        next
      }
      { print }
    ' /etc/dovecot/conf.d/10-auth.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/10-auth.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/10-mail.conf ]] && grep -qE '^mail_location[[:space:]]*=' /etc/dovecot/conf.d/10-mail.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 10-mail.conf"
    backup_file /etc/dovecot/conf.d/10-mail.conf
    tmp_file=$(mktemp)
    awk '
      /^mail_location[[:space:]]*=/ {
        value = $0
        sub(/^[^=]*=[[:space:]]*/, "", value)
        driver = "maildir"
        path = value
        colon = index(value, ":")
        if (colon > 0) {
          driver = substr(value, 1, colon - 1)
          path = substr(value, colon + 1)
        }
        print "mail_driver = " driver
        print "mail_path = " path
        next
      }
      { print }
    ' /etc/dovecot/conf.d/10-mail.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/10-mail.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/90-quota.conf ]] && grep -qE '^[[:space:]]*plugin[[:space:]]*\{' /etc/dovecot/conf.d/90-quota.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 90-quota.conf (rimuovo blocco plugin {})"
    backup_file /etc/dovecot/conf.d/90-quota.conf
    tmp_file=$(mktemp)
    awk '
      /^[[:space:]]*plugin[[:space:]]*\{/ { in_plugin = 1; next }
      in_plugin && /^[[:space:]]*\}/ { in_plugin = 0; next }
      in_plugin { sub(/^[[:space:]]+/, ""); print; next }
      { print }
    ' /etc/dovecot/conf.d/90-quota.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/90-quota.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/90-quota.conf ]] && grep -qE '^[[:space:]]*quota[[:space:]]*=' /etc/dovecot/conf.d/90-quota.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 90-quota.conf (converto quota= in named list)"
    backup_file /etc/dovecot/conf.d/90-quota.conf
    tmp_file=$(mktemp)
    awk '
      /^[[:space:]]*quota[[:space:]]*=/ {
        val = $0
        sub(/^[[:space:]]*quota[[:space:]]*=[[:space:]]*/, "", val)
        print "quota \"" val "\" {"
        in_block = 1
        next
      }
      in_block && /^[[:space:]]*$/ {
        print "}"
        in_block = 0
        print
        next
      }
      in_block { sub(/^[[:space:]]+/, ""); print "  " $0; next }
      { print }
      END { if (in_block) print "}" }
    ' /etc/dovecot/conf.d/90-quota.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/90-quota.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/90-quota.conf ]] && grep -qE '^[[:space:]]*quota_grace[[:space:]]*=' /etc/dovecot/conf.d/90-quota.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 90-quota.conf (rinomino quota_grace)"
    backup_file /etc/dovecot/conf.d/90-quota.conf
    tmp_file=$(mktemp)
    quota_storage_grace=$(sed -nE 's/^[[:space:]]*quota_grace[[:space:]]*=[[:space:]]*([^#[:space:]]+)([[:space:]]*(#.*)?)?$/\1/p' /etc/dovecot/conf.d/90-quota.conf | head -n1)
    awk '
      /^[[:space:]]*quota_grace[[:space:]]*=/ { next }
      { print }
    ' /etc/dovecot/conf.d/90-quota.conf > "$tmp_file"
    if [[ "$quota_storage_grace" == *%* ]]; then
      warn "Rimuovo valore legacy quota_grace percentuale non supportato da quota_storage_grace: $quota_storage_grace"
      quota_storage_grace=''
    fi
    if [[ -n "$quota_storage_grace" ]] && ! grep -qE '^[[:space:]]*quota_storage_grace[[:space:]]*=' "$tmp_file"; then
      { printf 'quota_storage_grace = %s\n\n' "$quota_storage_grace"; cat "$tmp_file"; } > "${tmp_file}.new"
      mv "${tmp_file}.new" "$tmp_file"
    fi
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/90-quota.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/90-quota.conf ]] && grep -qE '^[[:space:]]*quota_storage_grace[[:space:]]*=.*%' /etc/dovecot/conf.d/90-quota.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 90-quota.conf (rimuovo percentuali quota_storage_grace non valide)"
    backup_file /etc/dovecot/conf.d/90-quota.conf
    tmp_file=$(mktemp)
    awk '
      /^[[:space:]]*quota_storage_grace[[:space:]]*=.*%/ { next }
      { print }
    ' /etc/dovecot/conf.d/90-quota.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/90-quota.conf
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/dovecot-sql.conf.ext ]] && grep -q ' AS user' /etc/dovecot/dovecot-sql.conf.ext; then
    log "Aggiorno compatibilità Dovecot per dovecot-sql.conf.ext (rinomino alias user in username)"
    backup_file /etc/dovecot/dovecot-sql.conf.ext
    tmp_file=$(mktemp)
    sed \
      -e 's/ AS user, / AS username, /g' \
      -e 's/ AS user$/ AS username/g' \
      /etc/dovecot/dovecot-sql.conf.ext > "$tmp_file"
    install -m 0640 "$tmp_file" /etc/dovecot/dovecot-sql.conf.ext
    rm -f "$tmp_file"
  fi

  if [[ -f /etc/dovecot/conf.d/20-lmtp.conf ]] && grep -qE '^[[:space:]]*(sieve_before|sieve_default)[[:space:]]*=' /etc/dovecot/conf.d/20-lmtp.conf; then
    log "Aggiorno compatibilità Dovecot 2.4 per 20-lmtp.conf (rimuovo sieve_before/sieve_default legacy)"
    backup_file /etc/dovecot/conf.d/20-lmtp.conf
    tmp_file=$(mktemp)
    sed \
      -e '/^[[:space:]]*sieve_before[[:space:]]*=.*/d' \
      -e '/^[[:space:]]*sieve_default[[:space:]]*=.*/d' \
      /etc/dovecot/conf.d/20-lmtp.conf > "$tmp_file"
    install -m 0644 "$tmp_file" /etc/dovecot/conf.d/20-lmtp.conf
    rm -f "$tmp_file"
  fi
}

ensure_locales() {
  export DEBIAN_FRONTEND=noninteractive
  export LANG=C.UTF-8
  export LANGUAGE=C.UTF-8
  export LC_ALL=C.UTF-8
  if ! dpkg -s locales-all >/dev/null 2>&1; then
    log "Installo locales-all per evitare problemi LC_ALL/LANG"
    apt-get update
    apt-get install -y locales-all
  fi
  update-locale LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8 >/dev/null 2>&1 || true
}

backup_file() {
  local f=$1
  if [[ -f "$f" ]]; then
    cp "$f" "$f.bak.$(date +%s)"
  fi
}

gen_pass() {
  local len=${1:-32}
  head -c "$len" <(tr -dc 'A-Za-z0-9@_+' < /dev/urandom)
  echo
}

gen_user() {
  local len=${1:-12}
  head -c "$len" <(tr -dc 'abcdefghjkmnpqrstuvwxyz23456789' < /dev/urandom)
  echo
}

resolve_default_hostname() {
  local candidate=''
  if [[ -n ${LIMRISTEM_MAIL_HOSTNAME:-} ]]; then
    printf '%s\n' "$LIMRISTEM_MAIL_HOSTNAME"
    return 0
  fi
  if candidate=$(hostname -f 2>/dev/null) && [[ -n "$candidate" ]]; then
    printf '%s\n' "$candidate"
    return 0
  fi
  if candidate=$(hostname 2>/dev/null) && [[ -n "$candidate" ]]; then
    printf '%s\n' "$candidate"
    return 0
  fi
  if candidate=$(uname -n 2>/dev/null) && [[ -n "$candidate" ]]; then
    printf '%s\n' "$candidate"
    return 0
  fi
  printf 'mail.localdomain\n'
}

ensure_fqdn_hostname() {
  HOSTNAME_FQDN=${HOSTNAME_FQDN,,}
  HOSTNAME_FQDN=${HOSTNAME_FQDN%.}
  if [[ ${#HOSTNAME_FQDN} -le 253 && "$HOSTNAME_FQDN" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then
    return 0
  fi
  if [[ -t 0 ]]; then
    log "Hostname rilevato non valido per un server mail: '$HOSTNAME_FQDN'"
    read -r -p "Inserisci un hostname FQDN (es. mail.example.com): " HOSTNAME_FQDN
  fi
  HOSTNAME_FQDN=${HOSTNAME_FQDN,,}
  HOSTNAME_FQDN=${HOSTNAME_FQDN%.}
  if ! [[ ${#HOSTNAME_FQDN} -le 253 && "$HOSTNAME_FQDN" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then
    log "LIMRISTEM_MAIL_HOSTNAME deve essere un FQDN ASCII valido (es. mail.example.com)."
    exit 1
  fi
}

ensure_hosts_entry() {
  local short_hostname=$HOSTNAME_FQDN
  local desired_line
  local tmp_file
  if [[ "$HOSTNAME_FQDN" == *.* ]]; then
    short_hostname=${HOSTNAME_FQDN%%.*}
  fi
  desired_line="127.0.1.1 $HOSTNAME_FQDN"
  if [[ -n "$short_hostname" && "$short_hostname" != "$HOSTNAME_FQDN" ]]; then
    desired_line+=" $short_hostname"
  fi

  backup_file /etc/hosts
  tmp_file=$(mktemp)
  awk -v desired="$desired_line" '
    BEGIN { replaced = 0 }
    /^[[:space:]]*#/ || /^[[:space:]]*$/ { print; next }
    $1 == "127.0.1.1" {
      if (!replaced) {
        print desired
        replaced = 1
      }
      next
    }
    { print }
    END {
      if (!replaced) {
        print desired
      }
    }
  ' /etc/hosts > "$tmp_file"
  install -m 0644 "$tmp_file" /etc/hosts
  rm -f "$tmp_file"
}

render() {
  local tmpl=$1
  local dest=$2
  local index token variable value escaped
  local -a sed_args=()
  local -a substitutions=(
    __HOSTNAME__ HOSTNAME_FQDN
    __SERVER_NAME__ SERVER_NAME
    __MAIL_HOME__ MAIL_HOME
    __VMAIL_UID__ VMAIL_UID
    __VMAIL_GID__ VMAIL_GID
    __DB_HOST__ DB_HOST
    __DB_PORT__ DB_PORT
    __DB_NAME__ DB_NAME
    __DB_USER__ DB_USER
    __DB_PASS__ DB_PASS
    __REDIS_HOST__ REDIS_HOST
    __REDIS_PORT__ REDIS_PORT
    __REDIS_PASSWORD__ REDIS_PASSWORD
    __RSPAMD_HASHED__ RSPAMD_HASHED
    __BASE_DIR__ BASE_DIR
    __TLS_CERT__ TLS_CERT
    __TLS_KEY__ TLS_KEY
    __DKIM_KEYS_DIR__ DKIM_KEYS_DIR
    __API_INTERNAL_BIND__ API_BIND
    __API_INTERNAL_PORT__ API_PORT
    __NGINX_SERVER_NAME__ NGINX_SERVER_NAME
    __NGINX_ROOT_TARGET__ NGINX_ROOT_TARGET
    __MILTER_DEFAULT_ACTION__ MILTER_DEFAULT_ACTION
    __POSTFIX_RATE_TIME_UNIT__ POSTFIX_RATE_TIME_UNIT
    __POSTFIX_CLIENT_CONNECTION_RATE_LIMIT__ POSTFIX_CLIENT_CONNECTION_RATE_LIMIT
    __POSTFIX_CLIENT_MESSAGE_RATE_LIMIT__ POSTFIX_CLIENT_MESSAGE_RATE_LIMIT
    __RSPAMD_GREYLIST_ENABLED__ RSPAMD_GREYLIST_ENABLED
    __RSPAMD_GREYLIST_DELAY__ RSPAMD_GREYLIST_DELAY
    __RSPAMD_GREYLIST_EXPIRE__ RSPAMD_GREYLIST_EXPIRE
    __RSPAMD_ACTION_GREYLIST__ RSPAMD_ACTION_GREYLIST
    __RSPAMD_ACTION_ADD_HEADER__ RSPAMD_ACTION_ADD_HEADER
    __RSPAMD_ACTION_REJECT__ RSPAMD_ACTION_REJECT
    __BACKUP_ONCALENDAR__ BACKUP_ONCALENDAR
    __DELIVERABILITY_ONCALENDAR__ DELIVERABILITY_ONCALENDAR
    __SRS_DOMAIN__ SRS_DOMAIN
    __MTA_STS_HOST__ MTA_STS_HOST
    __MTA_STS_MODE__ MTA_STS_MODE
    __MTA_STS_MAX_AGE__ MTA_STS_MAX_AGE
  )

  for ((index = 0; index < ${#substitutions[@]}; index += 2)); do
    token=${substitutions[index]}
    variable=${substitutions[index + 1]}
    value=${!variable:-}
    if [[ "$value" == *[[:cntrl:]]* ]]; then
      log "Valore non valido per $variable: i caratteri di controllo non sono consentiti."
      exit 1
    fi
    escaped=${value//\\/\\\\}
    escaped=${escaped//&/\\&}
    escaped=${escaped//|/\\|}
    sed_args+=(-e "s|$token|$escaped|g")
  done

  mkdir -p "$(dirname "$dest")"
  sed "${sed_args[@]}" "$tmpl" > "$dest"
}

mysql_root_args() {
  local -n _out=$1
  _out=(-uroot)
  MYSQL_ROOT_PASSWORD=
  if mysql "${_out[@]}" -e "SELECT 1" >/dev/null 2>&1; then
    return 0
  fi
  if [[ -n "$DB_ROOT_PASS" ]] && MYSQL_PWD="$DB_ROOT_PASS" mysql -uroot -e "SELECT 1" >/dev/null 2>&1; then
    MYSQL_ROOT_PASSWORD=$DB_ROOT_PASS
    return 0
  fi
  log "Impossibile collegarsi a MariaDB come root. Imposta LIMRISTEM_MAIL_DB_ROOT_PASS se necessario."
  exit 1
}

mysql_client_cmd() {
  if command -v mariadb >/dev/null 2>&1; then
    printf 'mariadb\n'
  else
    printf 'mysql\n'
  fi
}

sql_identifier() {
  local identifier=${1//\`/\`\`}
  printf '`%s`\n' "$identifier"
}

sql_literal() {
  local value=${1//\\/\\\\}
  value=${value//\'/\'\'}
  printf "'%s'\n" "$value"
}

validate_service_port() {
  local label=$1
  local value=$2
  if [[ ! "$value" =~ ^[0-9]+$ ]] || ((10#$value < 1 || 10#$value > 65535)); then
    log "$label deve essere una porta numerica tra 1 e 65535."
    exit 1
  fi
}

validate_installer_values() {
  local label value
  if [[ ! "$DB_NAME" =~ ^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$ ]]; then
    log "LIMRISTEM_MAIL_DB_NAME contiene caratteri non validi."
    exit 1
  fi
  if [[ ! "$DB_USER" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,79}$ ]]; then
    log "LIMRISTEM_MAIL_DB_USER contiene caratteri non validi."
    exit 1
  fi
  if [[ ! "$DB_HOST" =~ ^[A-Za-z0-9._:-]+$ || ! "$DB_USER_HOST" =~ ^[A-Za-z0-9._:%-]+$ ]]; then
    log "LIMRISTEM_MAIL_DB_HOST o LIMRISTEM_MAIL_DB_USER_HOST contiene caratteri non validi."
    exit 1
  fi
  for label in DB_PASS REDIS_PASSWORD; do
    value=${!label}
    if [[ -z "$value" || ${#value} -gt 512 || "$value" == *[[:space:]]* || "$value" == *"'"* || "$value" == *'"'* || "$value" == *'\'* || "$value" == *'#'* ]]; then
      log "$label deve essere non vuota, senza spazi, virgolette, backslash o #, e lunga al massimo 512 caratteri."
      exit 1
    fi
  done
  validate_service_port LIMRISTEM_MAIL_DB_PORT "$DB_PORT"
  validate_service_port LIMRISTEM_MAIL_REDIS_PORT "$REDIS_PORT"
  validate_service_port LIMRISTEM_MAIL_API_PORT "$API_PORT"
  if [[ ! "$VMAIL_UID" =~ ^[0-9]+$ || ! "$VMAIL_GID" =~ ^[0-9]+$ ]]; then
    log "LIMRISTEM_MAIL_VMAIL_UID e LIMRISTEM_MAIL_VMAIL_GID devono essere numerici."
    exit 1
  fi
}

detect_rspamd_user() {
  if id _rspamd >/dev/null 2>&1; then
    printf '_rspamd\n'
  elif id rspamd >/dev/null 2>&1; then
    printf 'rspamd\n'
  fi
}

runtime_bin_dir() {
  if [[ -d "$BASE_DIR/bin" ]]; then
    printf '%s\n' "$BASE_DIR/bin"
  else
    printf '%s\n' "$BASE_DIR/scripts"
  fi
}

ensure_group_member() {
  local user=$1
  local group=$2
  if id "$user" >/dev/null 2>&1; then
    usermod -a -G "$group" "$user"
  fi
}

install_packages() {
  if [[ "${SKIP_PACKAGE_INSTALL:-no}" == "yes" ]]; then
    log "Salto installazione pacchetti (LIMRISTEM_MAIL_SKIP_PACKAGE_INSTALL=yes)"
    return 0
  fi
  log "Installazione pacchetti core"
  export DEBIAN_FRONTEND=noninteractive
  apt-get update
  apt-get install -y \
    postfix postfix-mysql \
    dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql dovecot-sieve \
    rspamd redis-server mariadb-server mariadb-backup \
    certbot python3-venv python3-pip python3-certbot \
    nginx-light rclone rsync jq postsrsd dnsutils sudo nftables fail2ban locales-all
}

setup_hostname() {
  log "Configuro hostname locale"
  echo "$HOSTNAME_FQDN" > /etc/mailname
  hostnamectl set-hostname "$HOSTNAME_FQDN"
  ensure_hosts_entry
}

create_vmail_user() {
  local existing_uid existing_gid
  if ! getent group vmail >/dev/null 2>&1; then
    groupadd --system -g "$VMAIL_GID" vmail
  else
    existing_gid=$(getent group vmail | awk -F: '{print $3}')
    if [[ -n "$existing_gid" && "$existing_gid" != "$VMAIL_GID" ]]; then
      log "Riutilizzo GID del gruppo vmail esistente: $existing_gid"
      VMAIL_GID=$existing_gid
    fi
  fi
  if ! id vmail >/dev/null 2>&1; then
    log "Creo utente di sistema vmail ($VMAIL_UID:$VMAIL_GID)"
    useradd --system -g vmail -u "$VMAIL_UID" -d "$MAIL_HOME" -m -s /usr/sbin/nologin vmail
  else
    existing_uid=$(id -u vmail)
    existing_gid=$(id -g vmail)
    if [[ "$existing_uid" != "$VMAIL_UID" ]]; then
      log "Riutilizzo UID dell'utente vmail esistente: $existing_uid"
      VMAIL_UID=$existing_uid
    fi
    if [[ "$existing_gid" != "$VMAIL_GID" ]]; then
      log "Riutilizzo GID primario dell'utente vmail esistente: $existing_gid"
      VMAIL_GID=$existing_gid
    fi
  fi
  mkdir -p "$MAIL_HOME"
  chown -R vmail:vmail "$MAIL_HOME"
  chmod 750 "$MAIL_HOME"
}

create_service_user() {
  if ! id limristem-mail >/dev/null 2>&1; then
    useradd --system --create-home --home-dir /var/lib/limristem-mail --shell /usr/sbin/nologin limristem-mail
  fi
}

setup_backup_directories() {
  local backup_dir=${LIMRISTEM_MAIL_BACKUP_LOCAL_DIR:-/var/backups/limristem-mail}
  local state_dir=/var/lib/limristem-mail/backup-state
  log "Configuro directory backup"
  mkdir -p "$backup_dir" "$state_dir"
  chown root:limristem-mail "$backup_dir"
  chown root:root "$state_dir"
  chmod 0750 "$backup_dir"
  chmod 0700 "$state_dir"
}

setup_dkim_permissions() {
  if ! getent group mailkeys >/dev/null 2>&1; then
    groupadd --system mailkeys
  fi
  ensure_group_member limristem-mail mailkeys
  local rspamd_user
  rspamd_user=$(detect_rspamd_user || true)
  if [[ -n "$rspamd_user" ]]; then
    ensure_group_member "$rspamd_user" mailkeys
  fi
  mkdir -p /var/lib/limristem-mail
  chgrp mailkeys /var/lib/limristem-mail
  chmod 0750 /var/lib/limristem-mail
  mkdir -p "$DKIM_KEYS_DIR"
  chown root:mailkeys "$DKIM_KEYS_DIR"
  chmod 2770 "$DKIM_KEYS_DIR"
  : > "$DKIM_KEYS_DIR/selectors.map"
  : > "$DKIM_KEYS_DIR/paths.map"
  chown root:mailkeys "$DKIM_KEYS_DIR/selectors.map" "$DKIM_KEYS_DIR/paths.map"
  chmod 0640 "$DKIM_KEYS_DIR/selectors.map" "$DKIM_KEYS_DIR/paths.map"
}

setup_database() {
  log "Configuro MariaDB e schema"
  systemctl enable --now mariadb
  local root_args=()
  local db_ident db_user_literal db_user_host_literal db_pass_literal
  mysql_root_args root_args
  db_ident=$(sql_identifier "$DB_NAME")
  db_user_literal=$(sql_literal "$DB_USER")
  db_user_host_literal=$(sql_literal "$DB_USER_HOST")
  db_pass_literal=$(sql_literal "$DB_PASS")

  MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql "${root_args[@]}" <<SQL
CREATE DATABASE IF NOT EXISTS ${db_ident} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
SQL

  MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql "${root_args[@]}" "${DB_NAME}" < "$BASE_DIR/database/schema.sql"

  MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql "${root_args[@]}" <<SQL
CREATE OR REPLACE USER ${db_user_literal}@${db_user_host_literal} IDENTIFIED BY ${db_pass_literal};
GRANT ALL PRIVILEGES ON ${db_ident}.* TO ${db_user_literal}@${db_user_host_literal};
SQL

  if [[ "$DB_HOST" == "127.0.0.1" || "$DB_HOST" == "localhost" ]]; then
    MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql "${root_args[@]}" <<SQL
CREATE OR REPLACE USER ${db_user_literal}@'127.0.0.1' IDENTIFIED BY ${db_pass_literal};
GRANT ALL PRIVILEGES ON ${db_ident}.* TO ${db_user_literal}@'127.0.0.1';
CREATE OR REPLACE USER ${db_user_literal}@'localhost' IDENTIFIED BY ${db_pass_literal};
GRANT ALL PRIVILEGES ON ${db_ident}.* TO ${db_user_literal}@'localhost';
SQL
  fi

  MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql "${root_args[@]}" <<'SQL'
FLUSH PRIVILEGES;
SQL
}

configure_redis() {
  log "Imposto password Redis"
  local conf=/etc/redis/redis.conf
  local escaped_password=$REDIS_PASSWORD
  escaped_password=${escaped_password//\\/\\\\}
  escaped_password=${escaped_password//&/\\&}
  escaped_password=${escaped_password//\//\\/}
  backup_file "$conf"
  if grep -qE '^#?\s*requirepass' "$conf"; then
    sed -i "s/^#\?\s*requirepass.*/requirepass \"${escaped_password}\"/" "$conf"
  else
    printf 'requirepass "%s"\n' "$REDIS_PASSWORD" >> "$conf"
  fi
  systemctl enable --now redis-server
  systemctl restart redis-server
}

# Remove SRS-related Postfix maps so stale socket references do not survive service disable/failure.
clear_postfix_srs_maps() {
  postconf -X sender_canonical_maps || true
  postconf -X sender_canonical_classes || true
  postconf -X recipient_canonical_maps || true
  postconf -X recipient_canonical_classes || true
}

apply_postfix_srs_maps() {
  postconf -e 'recipient_canonical_maps = tcp:127.0.0.1:10002'
  postconf -e 'recipient_canonical_classes = envelope_recipient, header_recipient'
}

# Remove Rspamd milter settings so Postfix no longer depends on an unavailable milter endpoint.
clear_postfix_milter_settings() {
  postconf -X smtpd_milters || true
  postconf -X non_smtpd_milters || true
  postconf -X milter_default_action || true
  postconf -X milter_protocol || true
}

# Reload Postfix only when it is already active, avoiding noisy failures during setup/repair paths.
reload_postfix_if_active() {
  if systemctl is-active --quiet postfix; then
    systemctl reload postfix >/dev/null 2>&1 || true
  fi
}

setup_postfix() {
  log "Configuro Postfix"
  backup_file /etc/postfix/main.cf
  backup_file /etc/postfix/master.cf
  render "$BASE_DIR/templates/postfix/main.cf" /etc/postfix/main.cf
  render "$BASE_DIR/templates/postfix/master.cf" /etc/postfix/master.cf
  render "$BASE_DIR/templates/postfix/mysql-virtual-domains-maps.cf" /etc/postfix/mysql-virtual-domains-maps.cf
  render "$BASE_DIR/templates/postfix/mysql-virtual-mailbox-maps.cf" /etc/postfix/mysql-virtual-mailbox-maps.cf
  render "$BASE_DIR/templates/postfix/mysql-virtual-alias-maps.cf" /etc/postfix/mysql-virtual-alias-maps.cf
  render "$BASE_DIR/templates/postfix/mysql-virtual-redirect-maps.cf" /etc/postfix/mysql-virtual-redirect-maps.cf

  clear_postfix_srs_maps
  clear_postfix_milter_settings

  if [[ "$ENABLE_RSPAMD" == "yes" ]]; then
    postconf -e 'smtpd_milters = inet:127.0.0.1:11332'
    postconf -e 'non_smtpd_milters = $smtpd_milters'
    postconf -e "milter_default_action = $MILTER_DEFAULT_ACTION"
    postconf -e 'milter_protocol = 6'
  fi

  systemctl enable --now postfix
}

setup_dovecot() {
  log "Configuro Dovecot"
  mkdir -p /etc/dovecot/conf.d
  if [[ -f /etc/dovecot/conf.d/10-ssl.conf ]]; then
    backup_file /etc/dovecot/conf.d/10-ssl.conf
    rm -f /etc/dovecot/conf.d/10-ssl.conf
  fi
  if [[ -f /etc/dovecot/conf.d/10-ssl-limristem-mail.conf ]]; then
    backup_file /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
    rm -f /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
  fi
  backup_file /etc/dovecot/dovecot.conf
  backup_file /etc/dovecot/dovecot-sql.conf.ext
  render "$BASE_DIR/templates/dovecot/dovecot.conf" /etc/dovecot/dovecot.conf
  render "$BASE_DIR/templates/dovecot/dovecot-sql.conf.ext" /etc/dovecot/dovecot-sql.conf.ext
  render "$BASE_DIR/templates/dovecot/conf.d/10-mail.conf" /etc/dovecot/conf.d/10-mail.conf
  render "$BASE_DIR/templates/dovecot/conf.d/10-auth.conf" /etc/dovecot/conf.d/10-auth.conf
  render "$BASE_DIR/templates/dovecot/conf.d/auth-sql.conf.ext" /etc/dovecot/conf.d/auth-sql.conf.ext
  render "$BASE_DIR/templates/dovecot/conf.d/10-master.conf" /etc/dovecot/conf.d/10-master.conf
  render "$BASE_DIR/templates/dovecot/conf.d/20-lmtp.conf" /etc/dovecot/conf.d/20-lmtp.conf
  render "$BASE_DIR/templates/dovecot/conf.d/20-imap.conf" /etc/dovecot/conf.d/20-imap.conf
  render "$BASE_DIR/templates/dovecot/conf.d/90-quota.conf" /etc/dovecot/conf.d/90-quota.conf
  rm -f /etc/dovecot/sieve-before.d/spam-to-junk.sieve /etc/dovecot/sieve-before.d/spam-to-junk.svbin
  mkdir -p /var/lib/dovecot/sieve /var/lib/dovecot/sieve/before.d
  render "$BASE_DIR/templates/dovecot/sieve-before/spam-to-junk.sieve" /var/lib/dovecot/sieve/before.d/spam-to-junk.sieve
  chown dovecot:dovecot /var/lib/dovecot/sieve /var/lib/dovecot/sieve/before.d /var/lib/dovecot/sieve/before.d/spam-to-junk.sieve
  chmod 0750 /var/lib/dovecot/sieve /var/lib/dovecot/sieve/before.d
  chmod 0640 /var/lib/dovecot/sieve/before.d/spam-to-junk.sieve
  chown root:dovecot /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/conf.d/auth-sql.conf.ext
  chmod 640 /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/conf.d/auth-sql.conf.ext
  systemctl enable --now dovecot
}

setup_rspamd() {
  local rspamd_service_user
  if [[ "$ENABLE_RSPAMD" != "yes" ]]; then
    log "Rspamd disabilitato: fermo il servizio e disattivo il milter"
    systemctl disable --now rspamd >/dev/null 2>&1 || true
    return 0
  fi
  log "Configuro Rspamd con Redis"
  mkdir -p /etc/rspamd/local.d
  render "$BASE_DIR/templates/rspamd/local.d/options.inc" /etc/rspamd/local.d/options.inc
  render "$BASE_DIR/templates/rspamd/local.d/redis.conf" /etc/rspamd/local.d/redis.conf
  render "$BASE_DIR/templates/rspamd/local.d/classifier-bayes.conf" /etc/rspamd/local.d/classifier-bayes.conf
  render "$BASE_DIR/templates/rspamd/local.d/dkim_signing.conf" /etc/rspamd/local.d/dkim_signing.conf
  render "$BASE_DIR/templates/rspamd/local.d/milter_headers.conf" /etc/rspamd/local.d/milter_headers.conf
  rm -f /etc/rspamd/local.d/greylisting.conf
  render "$BASE_DIR/templates/rspamd/local.d/greylist.conf" /etc/rspamd/local.d/greylist.conf
  render "$BASE_DIR/templates/rspamd/local.d/actions.conf" /etc/rspamd/local.d/actions.conf
  render "$BASE_DIR/templates/rspamd/local.d/worker-controller.inc" /etc/rspamd/local.d/worker-controller.inc
  render "$BASE_DIR/templates/rspamd/local.d/worker-proxy.inc" /etc/rspamd/local.d/worker-proxy.inc
  rspamd_service_user=$(detect_rspamd_user || true)
  if [[ -n "$rspamd_service_user" ]]; then
    chown root:"$rspamd_service_user" /etc/rspamd/local.d
    chmod 750 /etc/rspamd/local.d
    chown root:"$rspamd_service_user" \
      /etc/rspamd/local.d/options.inc \
      /etc/rspamd/local.d/redis.conf \
      /etc/rspamd/local.d/classifier-bayes.conf \
      /etc/rspamd/local.d/dkim_signing.conf \
      /etc/rspamd/local.d/milter_headers.conf \
      /etc/rspamd/local.d/greylist.conf \
      /etc/rspamd/local.d/actions.conf \
      /etc/rspamd/local.d/worker-controller.inc \
      /etc/rspamd/local.d/worker-proxy.inc
    chmod 640 \
      /etc/rspamd/local.d/options.inc \
      /etc/rspamd/local.d/redis.conf \
      /etc/rspamd/local.d/classifier-bayes.conf \
      /etc/rspamd/local.d/dkim_signing.conf \
      /etc/rspamd/local.d/milter_headers.conf \
      /etc/rspamd/local.d/greylist.conf \
      /etc/rspamd/local.d/actions.conf \
      /etc/rspamd/local.d/worker-controller.inc \
      /etc/rspamd/local.d/worker-proxy.inc
  fi
  systemctl enable --now rspamd
}

write_rendered_env_file() {
  local target_file=$1
  local tmp_file
  limristem_mail_prepare_managed_dir "$(dirname "$target_file")"
  tmp_file=$(mktemp)
  cat > "$tmp_file"
  limristem_mail_install_managed_file "$tmp_file" "$target_file" 0660
  rm -f "$tmp_file"
}

setup_config_permissions() {
  local config_dir=$BASE_DIR/config
  limristem_mail_prepare_managed_dir "$config_dir"
  find "$config_dir" -mindepth 1 -maxdepth 1 -type d -exec chown root:limristem-mail {} + -exec chmod 2770 {} + 2>/dev/null || true
  find "$config_dir" -mindepth 1 -maxdepth 1 -type f -exec chown root:limristem-mail {} + -exec chmod 0660 {} + 2>/dev/null || true
}

render_backup_timer_unit() {
  local timer_file=${1:-/etc/systemd/system/limristem-mail-backup.timer}
  local schedule_lines
  schedule_lines=$(python3 - "${BACKUP_ONCALENDAR:-*-*-* 03:15:00}" <<'PY'
import sys

value = sys.argv[1].replace("\r", "")
lines = [line.strip() for line in value.splitlines() if line.strip()]
if not lines:
    lines = ["*-*-* 03:15:00"]
for line in lines:
    print(f"OnCalendar={line}")
PY
)
  {
    printf '[Unit]\n'
    printf 'Description=Run Limristem eMail backups on schedule\n\n'
    printf '[Timer]\n'
    printf '%s\n' "$schedule_lines"
    printf 'Persistent=true\n\n'
    printf '[Install]\n'
    printf 'WantedBy=timers.target\n'
  } > "$timer_file"
}

write_env_file() {
  local env_file
  env_file=$(limristem_mail_resolve_main_env_file)
  log "Scrivo $env_file"
  backup_file "$env_file"
  {
    limristem_mail_env_write_var LIMRISTEM_MAIL_BASE_DIR "$BASE_DIR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_VERSION "$PACKAGE_VERSION"
    limristem_mail_env_write_var LIMRISTEM_MAIL_HOSTNAME "$HOSTNAME_FQDN"
    limristem_mail_env_write_var LIMRISTEM_MAIL_SERVER_NAME "$SERVER_NAME"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_AUTO_UPDATES "$ENABLE_AUTO_UPDATES"
    limristem_mail_env_write_var LIMRISTEM_MAIL_SSL_MODE "$SSL_MODE"
    limristem_mail_env_write_var LIMRISTEM_MAIL_LE_EMAIL "$LE_EMAIL"
    limristem_mail_env_write_var LIMRISTEM_MAIL_TLS_CERT_PATH "${TLS_CERT:-}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_TLS_KEY_PATH "${TLS_KEY:-}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_HOST "$DB_HOST"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_PORT "$DB_PORT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_NAME "$DB_NAME"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_USER "$DB_USER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_PASS "$DB_PASS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DB_USER_HOST "$DB_USER_HOST"
    limristem_mail_env_write_var LIMRISTEM_MAIL_REDIS_HOST "$REDIS_HOST"
    limristem_mail_env_write_var LIMRISTEM_MAIL_REDIS_PORT "$REDIS_PORT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_REDIS_PASSWORD "$REDIS_PASSWORD"
    limristem_mail_env_write_var LIMRISTEM_MAIL_CACHE_TTL_SECONDS "$CACHE_TTL_SECONDS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_API "$ENABLE_API"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_NGINX "$ENABLE_NGINX"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_RSPAMD "$ENABLE_RSPAMD"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_BACKUP_TIMER "$ENABLE_BACKUP_TIMER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_WEB_PANEL "$ENABLE_WEB_PANEL"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_ADMIN_USER "$API_ADMIN_USER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_ADMIN_PASS_HASH "$API_ADMIN_PASS_HASH"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_ADMIN_USER "$PANEL_ADMIN_USER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_ADMIN_PASS_HASH "$PANEL_ADMIN_PASS_HASH"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_LOGIN_CSRF_SECRET "$PANEL_LOGIN_CSRF_SECRET"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_SESSION_TTL_SECONDS "$PANEL_SESSION_TTL_SECONDS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_BIND "$API_BIND"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_PORT "$API_PORT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_AUTH_FAIL_LIMIT "$API_AUTH_FAIL_LIMIT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_AUTH_WINDOW_SECONDS "$API_AUTH_WINDOW_SECONDS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_API_AUTH_BLOCK_SECONDS "$API_AUTH_BLOCK_SECONDS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MAIL_HOME "$MAIL_HOME"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DKIM_KEYS_DIR "$DKIM_KEYS_DIR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PRIMARY_DOMAIN "$PRIMARY_DOMAIN"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_MTA_STS "$ENABLE_MTA_STS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MTA_STS_HOST "$MTA_STS_HOST"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MTA_STS_MODE "$MTA_STS_MODE"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MTA_STS_MAX_AGE "$MTA_STS_MAX_AGE"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MTA_STS_ID "$MTA_STS_ID"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_TLS_RPT "$ENABLE_TLS_RPT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_TLS_RPT_MAILBOX "$TLS_RPT_MAILBOX"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DKIM_SELECTOR "$DKIM_SELECTOR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PUBLIC_IP "$PUBLIC_IP"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PUBLIC_IPV6 "$PUBLIC_IPV6"
    limristem_mail_env_write_var LIMRISTEM_MAIL_LIVE_BUNDLE_DIR "$LIVE_BUNDLE_DIR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_DELIVERABILITY_TIMER "$ENABLE_DELIVERABILITY_TIMER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_DELIVERABILITY_ONCALENDAR "$DELIVERABILITY_ONCALENDAR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_VMAIL_UID "$VMAIL_UID"
    limristem_mail_env_write_var LIMRISTEM_MAIL_VMAIL_GID "$VMAIL_GID"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_PASSWORD "$RSPAMD_PASSWORD"
    limristem_mail_env_write_var LIMRISTEM_MAIL_MILTER_DEFAULT_ACTION "$MILTER_DEFAULT_ACTION"
    limristem_mail_env_write_var LIMRISTEM_MAIL_POSTFIX_RATE_TIME_UNIT "$POSTFIX_RATE_TIME_UNIT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_POSTFIX_CLIENT_CONNECTION_RATE_LIMIT "$POSTFIX_CLIENT_CONNECTION_RATE_LIMIT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_POSTFIX_CLIENT_MESSAGE_RATE_LIMIT "$POSTFIX_CLIENT_MESSAGE_RATE_LIMIT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_GREYLIST_ENABLED "$RSPAMD_GREYLIST_ENABLED"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_GREYLIST_DELAY "$RSPAMD_GREYLIST_DELAY"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_GREYLIST_EXPIRE "$RSPAMD_GREYLIST_EXPIRE"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_ACTION_GREYLIST "$RSPAMD_ACTION_GREYLIST"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_ACTION_ADD_HEADER "$RSPAMD_ACTION_ADD_HEADER"
    limristem_mail_env_write_var LIMRISTEM_MAIL_RSPAMD_ACTION_REJECT "$RSPAMD_ACTION_REJECT"
    limristem_mail_env_write_var LIMRISTEM_MAIL_ENABLE_SRS "$ENABLE_SRS"
    limristem_mail_env_write_var LIMRISTEM_MAIL_SRS_DOMAIN "$SRS_DOMAIN"
    limristem_mail_env_write_var LIMRISTEM_MAIL_FIREWALL_ENABLED "${LIMRISTEM_MAIL_FIREWALL_ENABLED:-yes}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_FIREWALL_ALLOWED_TCP_PORTS "${LIMRISTEM_MAIL_FIREWALL_ALLOWED_TCP_PORTS:-22 25 80 110 143 443 465 587 993 995}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_FIREWALL_ALLOWED_UDP_PORTS "${LIMRISTEM_MAIL_FIREWALL_ALLOWED_UDP_PORTS:-}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_FIREWALL_RULES "${LIMRISTEM_MAIL_FIREWALL_RULES:-}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_TITLE "${LIMRISTEM_MAIL_PANEL_TITLE:-}"
    limristem_mail_env_write_var LIMRISTEM_MAIL_PANEL_FAVICON_PATH "${LIMRISTEM_MAIL_PANEL_FAVICON_PATH:-}"
  } | write_rendered_env_file "$env_file"
}

generate_api_admin_hash() {
  if [[ "$ENABLE_API" != "yes" ]]; then
    API_ADMIN_PASS=''
    API_ADMIN_PASS_HASH=''
    return 0
  fi
  if [[ -n "${API_ADMIN_PASS_HASH:-}" ]]; then
    return 0
  fi

  log "Genero hash Argon2 per l'admin API"
  API_ADMIN_PASS_HASH=$(
    LIMRISTEM_MAIL_API_ADMIN_PASS="$API_ADMIN_PASS" "$BASE_DIR/.venv/bin/python" - <<'PY'
from os import environ

from passlib.context import CryptContext

context = CryptContext(schemes=["argon2"], default="argon2", argon2__type="ID")
print(context.hash(environ["LIMRISTEM_MAIL_API_ADMIN_PASS"]))
PY
  )
}

generate_panel_admin_hash() {
  if [[ "$ENABLE_WEB_PANEL" != "yes" ]]; then
    PANEL_ADMIN_PASS=''
    PANEL_ADMIN_PASS_HASH=''
    return 0
  fi
  if [[ -n "${PANEL_ADMIN_PASS_HASH:-}" ]]; then
    return 0
  fi

  log "Genero hash Argon2 per l'admin Panel"
  PANEL_ADMIN_PASS_HASH=$(
    LIMRISTEM_MAIL_PANEL_ADMIN_PASS="$PANEL_ADMIN_PASS" "$BASE_DIR/.venv/bin/python" - <<'PY'
from os import environ

from passlib.context import CryptContext

context = CryptContext(schemes=["argon2"], default="argon2", argon2__type="ID")
print(context.hash(environ["LIMRISTEM_MAIL_PANEL_ADMIN_PASS"]))
PY
  )
}

write_backup_env_file() {
  local backup_env_file
  backup_env_file=$(limristem_mail_resolve_backup_env_file)
  if [[ -f "$backup_env_file" ]]; then
    log "Mantengo $backup_env_file esistente."
    return 0
  fi
  log "Scrivo $backup_env_file"
  {
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_TYPE "auto"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_FULL_WEEKDAY "Sun"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_DB_MODE "logical"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_DB_ROOT_PASSWORD ""
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_LOCAL_DIR "/var/backups/limristem-mail"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_RETENTION_DAYS "14"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_REMOTE_TARGETS ""
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_ONCALENDAR "$BACKUP_ONCALENDAR"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_COMPRESSION "gzip"
    limristem_mail_env_write_var LIMRISTEM_MAIL_BACKUP_INCLUDE_REDIS "yes"
  } | write_rendered_env_file "$backup_env_file"
}

write_postsrsd_secret() {
  local secret_file legacy_secret_file
  secret_file=/etc/postsrsd.secret
  legacy_secret_file=$BASE_DIR/config/postsrsd.secret
  if [[ ! -f "$secret_file" && -f "$legacy_secret_file" ]]; then
    install -m 0600 "$legacy_secret_file" "$secret_file"
    rm -f "$legacy_secret_file"
  elif [[ ! -f "$secret_file" ]]; then
    gen_pass 48 > "$secret_file"
  fi
  chmod 600 "$secret_file"
  chown root:root "$secret_file"
}

ensure_postsrsd_installed() {
  if command -v postsrsd >/dev/null 2>&1; then
    return 0
  fi
  if [[ "${SKIP_PACKAGE_INSTALL:-no}" == "yes" ]]; then
    warn "Pacchetto postsrsd assente e installazione pacchetti disabilitata: SRS non verrà configurato."
    return 1
  fi
  log "PostSRSd non trovato: installo il pacchetto postsrsd"
  export DEBIAN_FRONTEND=noninteractive
  apt-get update
  apt-get install -y postsrsd
  command -v postsrsd >/dev/null 2>&1
}

bootstrap_postsrsd_domains() {
  local mysql_cmd output_file tmp_file managed_config_dir mysql_error attempt last_error
  mysql_cmd=$(mysql_client_cmd)
  managed_config_dir=$(limristem_mail_resolve_managed_config_dir)
  output_file=$managed_config_dir/postsrsd.domains
  tmp_file=$(mktemp)
  mysql_error=$(mktemp)

  limristem_mail_prepare_managed_dir "$managed_config_dir"
  log "Bootstrap domini SRS: interrogo database..."
  : > "$tmp_file"
  for attempt in 1 2 3 4 5; do
    if MYSQL_PWD="$DB_PASS" timeout 30 "$mysql_cmd" --connect-timeout=5 -Nse 'SELECT name FROM domains WHERE is_active=1 ORDER BY name;' \
      -h "$DB_HOST" \
      -P "$DB_PORT" \
      -u"$DB_USER" \
      "$DB_NAME" > "$tmp_file" 2> "$mysql_error"; then
      break
    fi
    last_error=$(tr -d '\r' < "$mysql_error" | tail -n 1)
    if [[ $attempt -lt 5 ]]; then
      log "Tentativo $attempt/5 di lettura domini SRS fallito: ${last_error:-errore sconosciuto}. Riprovo..."
      sleep "$attempt"
      : > "$tmp_file"
      continue
    fi
    warn "Impossibile leggere i domini attivi per PostSRSd: ${last_error:-errore sconosciuto}. Uso bootstrap locale minimale."
    : > "$tmp_file"
  done
  printf '%s\n' "$HOSTNAME_FQDN" >> "$tmp_file"
  if [[ -n "$SRS_DOMAIN" ]]; then
    printf '%s\n' "$SRS_DOMAIN" >> "$tmp_file"
  fi
  if [[ -n "$PRIMARY_DOMAIN" ]]; then
    printf '%s\n' "$PRIMARY_DOMAIN" >> "$tmp_file"
  fi
  sed -i '/^[[:space:]]*$/d' "$tmp_file"
  sort -u "$tmp_file" -o "$tmp_file"
  limristem_mail_install_managed_file "$tmp_file" "$output_file" 0660
  rm -f "$tmp_file"
  rm -f "$mysql_error"
  log "Bootstrap domini SRS completato."
}

wait_for_active_service() {
  local service=$1
  local timeout_seconds=${2:-20}
  local waited=0
  while (( waited < timeout_seconds )); do
    if systemctl is-active --quiet "$service"; then
      return 0
    fi
    if systemctl is-failed --quiet "$service"; then
      journalctl --no-pager -u "$service" -n 50 >&2 || true
      return 1
    fi
    sleep 1
    ((waited+=1))
  done
  journalctl --no-pager -u "$service" -n 50 >&2 || true
  return 1
}

api_health_host() {
  case "${API_BIND:-127.0.0.1}" in
    0.0.0.0|::|"[::]") printf '127.0.0.1\n' ;;
    *) printf '%s\n' "${API_BIND:-127.0.0.1}" ;;
  esac
}

wait_for_http_ok() {
  local url=$1
  local timeout_seconds=${2:-20}
  local host_header=${3:-}
  local insecure_tls=${4:-no}
  local waited=0

  while (( waited < timeout_seconds )); do
    if python3 - "$url" "$host_header" "$insecure_tls" <<'PY'
import ssl
import sys
import urllib.request

url, host_header, insecure_tls = sys.argv[1:4]
request = urllib.request.Request(url)
if host_header:
    request.add_header("Host", host_header)
context = ssl._create_unverified_context() if insecure_tls == "yes" else None
try:
    with urllib.request.urlopen(request, timeout=5, context=context) as response:
        if 200 <= response.status < 400:
            sys.exit(0)
except Exception:
    pass
sys.exit(1)
PY
    then
      return 0
    fi
    sleep 1
    ((waited+=1))
  done
  return 1
}

write_certbot_reload_hook() {
  mkdir -p /etc/letsencrypt/renewal-hooks/deploy
  cat > /etc/letsencrypt/renewal-hooks/deploy/limristem-mail-reload.sh <<'HOOK'
#!/usr/bin/env bash
set -euo pipefail
systemctl reload postfix dovecot nginx >/dev/null 2>&1 || true
HOOK
  chmod 750 /etc/letsencrypt/renewal-hooks/deploy/limristem-mail-reload.sh
}

write_mta_sts_policy() {
  mkdir -p /var/lib/limristem-mail/mta-sts
  render "$BASE_DIR/templates/mta-sts/mta-sts.txt" /var/lib/limristem-mail/mta-sts/mta-sts.txt
  chmod 644 /var/lib/limristem-mail/mta-sts/mta-sts.txt
}

prompt_yes_no() {
  local prompt=$1
  local answer

  if [[ "${LIMRISTEM_MAIL_ASSUME_DNS_READY:-no}" == "yes" ]]; then
    return 0
  fi

  if [[ ! -t 0 ]]; then
    warn "Installazione non interattiva: salto la conferma DNS per Let's Encrypt."
    return 0
  fi

  while true; do
    read -r -p "$prompt [y/N]: " answer
    case "${answer,,}" in
      y|yes|s|si) return 0 ;;
      n|no|"") return 1 ;;
      *) log "Risposta non valida: rispondi yes o no." ;;
    esac
  done
}

confirm_letsencrypt_dns_record() {
  local record_type=$1
  local record_name=$2
  local record_target=${3:-}
  local prompt="Hai creato il record DNS ${record_type} per ${record_name}"

  if [[ -n "$record_target" ]]; then
    prompt+=" puntato a ${record_target}"
  fi
  prompt+="?"

  if ! prompt_yes_no "$prompt"; then
    log "Let's Encrypt richiede il record ${record_type} per ${record_name} prima di continuare."
    exit 1
  fi
}

confirm_letsencrypt_dns_readiness() {
  local ipv4_target=${PUBLIC_IP:-}
  local ipv6_target=${PUBLIC_IPV6:-}

  log "Let's Encrypt selezionato: confermo i record DNS richiesti prima di richiedere il certificato."
  confirm_letsencrypt_dns_record "A" "$HOSTNAME_FQDN" "$ipv4_target"
  if [[ -n "$ipv6_target" ]]; then
    confirm_letsencrypt_dns_record "AAAA" "$HOSTNAME_FQDN" "$ipv6_target"
  fi
  if [[ "$ENABLE_MTA_STS" == "yes" && "$MTA_STS_HOST" != "$HOSTNAME_FQDN" ]]; then
    confirm_letsencrypt_dns_record "A" "$MTA_STS_HOST" "$ipv4_target"
    if [[ -n "$ipv6_target" ]]; then
      confirm_letsencrypt_dns_record "AAAA" "$MTA_STS_HOST" "$ipv6_target"
    fi
  fi
}

setup_ssl() {
  TLS_CERT=''
  TLS_KEY=''
  if [[ "$ENABLE_MTA_STS" == "yes" ]] && [[ "$SSL_MODE" != "letsencrypt" && "$SSL_MODE" != "manual" ]]; then
    log "MTA-STS richiede un certificato pubblico valido: disabilito MTA-STS con SSL_MODE=$SSL_MODE"
    ENABLE_MTA_STS=no
  fi
  case "$SSL_MODE" in
    plain)
      log "Modalita' plain: API esposta solo localmente, TLS disabilitato per i servizi gestiti da Limristem eMail"
      postconf -e 'smtpd_tls_security_level = none'
      postconf -X smtpd_use_tls || true
      postconf -X smtpd_tls_eecdh_grade || true
      postconf -e 'compatibility_level = 3.6'
      postconf -X smtpd_tls_cert_file || true
      postconf -X smtpd_tls_key_file || true
      sed -i '/^submission /,/^[^[:space:]]/ s/^/#/' /etc/postfix/master.cf
      sed -i '/^smtps /,/^[^[:space:]]/ s/^/#/' /etc/postfix/master.cf
      sed -i -E 's/^ssl = .*/ssl = no/' /etc/dovecot/dovecot.conf
      sed -i -E 's/^auth_allow_cleartext = .*/auth_allow_cleartext = yes/' /etc/dovecot/conf.d/10-auth.conf
      sed -i '/inet_listener imaps/,/}/ s/^/#/' /etc/dovecot/conf.d/10-master.conf
      sed -i '/inet_listener pop3s/,/}/ s/^/#/' /etc/dovecot/conf.d/10-master.conf
      rm -f /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
      ;;
    selfsigned)
      mkdir -p /etc/ssl/limristem-mail
      chmod 0755 /etc/ssl/limristem-mail
      if [[ "${LIMRISTEM_MAIL_UPDATE_MODE:-no}" == "yes" && -f /etc/ssl/limristem-mail/limristem-mail.crt && -f /etc/ssl/limristem-mail/limristem-mail.key ]]; then
        log "Riutilizzo certificato autofirmato esistente"
      else
        log "Genero certificato autofirmato"
        openssl req -x509 -nodes -newkey rsa:4096 -days 397 \
          -keyout /etc/ssl/limristem-mail/limristem-mail.key \
          -out /etc/ssl/limristem-mail/limristem-mail.crt \
          -subj "/CN=$HOSTNAME_FQDN" \
          -addext "subjectAltName=DNS:$HOSTNAME_FQDN,DNS:$MTA_STS_HOST"
      fi
      chmod 0600 /etc/ssl/limristem-mail/limristem-mail.key
      chmod 0644 /etc/ssl/limristem-mail/limristem-mail.crt
      TLS_CERT=/etc/ssl/limristem-mail/limristem-mail.crt
      TLS_KEY=/etc/ssl/limristem-mail/limristem-mail.key
      ;;
    manual)
      TLS_CERT=${LIMRISTEM_MAIL_TLS_CERT_PATH:-/etc/ssl/limristem-mail/manual.crt}
      TLS_KEY=${LIMRISTEM_MAIL_TLS_KEY_PATH:-/etc/ssl/limristem-mail/manual.key}
      if [[ ! -f "$TLS_CERT" || ! -f "$TLS_KEY" ]]; then
        log "Certificato manuale non disponibile: servono LIMRISTEM_MAIL_TLS_CERT_PATH e LIMRISTEM_MAIL_TLS_KEY_PATH validi."
        exit 1
      fi
      ;;
    letsencrypt)
      if [[ "${LIMRISTEM_MAIL_UPDATE_MODE:-no}" == "yes" ]]; then
        log "Riutilizzo certificato Let's Encrypt esistente"
      else
        confirm_letsencrypt_dns_readiness
        log "Richiedo certificato Let's Encrypt"
        local cert_domains=("-d" "$HOSTNAME_FQDN")
        if [[ "$ENABLE_MTA_STS" == "yes" && "$MTA_STS_HOST" != "$HOSTNAME_FQDN" ]]; then
          cert_domains+=("-d" "$MTA_STS_HOST")
        fi
        certbot certonly --standalone \
          --non-interactive --agree-tos \
          --pre-hook 'systemctl stop nginx || true' \
          --post-hook 'systemctl start nginx || true' \
          "${cert_domains[@]}" -m "$LE_EMAIL"
      fi
      TLS_CERT="/etc/letsencrypt/live/$HOSTNAME_FQDN/fullchain.pem"
      TLS_KEY="/etc/letsencrypt/live/$HOSTNAME_FQDN/privkey.pem"
      if [[ ! -f "$TLS_CERT" || ! -f "$TLS_KEY" ]]; then
        if [[ "${LIMRISTEM_MAIL_UPDATE_MODE:-no}" == "yes" ]]; then
          warn "Certificato Let's Encrypt non disponibile durante l'update: mantengo i servizi senza rigenerarlo."
          TLS_CERT=''
          TLS_KEY=''
          return 0
        fi
        log "Certificato Let's Encrypt non disponibile."
        exit 1
      fi
      systemctl enable --now certbot.timer
      write_certbot_reload_hook
      ;;
    *)
      log "SSL_MODE non valido: $SSL_MODE"
      exit 1
      ;;
  esac

  if [[ -n "$TLS_CERT" && -f "$TLS_CERT" ]]; then
    render "$BASE_DIR/templates/postfix/master.cf" /etc/postfix/master.cf
    render "$BASE_DIR/templates/dovecot/conf.d/10-master.conf" /etc/dovecot/conf.d/10-master.conf
    postconf -e "smtpd_tls_cert_file=$TLS_CERT"
    postconf -e "smtpd_tls_key_file=$TLS_KEY"
    postconf -e 'smtpd_tls_security_level = may'
    postconf -e 'smtpd_tls_auth_only = yes'
    postconf -e 'smtp_tls_security_level = may'
    postconf -X smtpd_use_tls || true
    postconf -X smtpd_tls_eecdh_grade || true
    postconf -e 'compatibility_level = 3.6'
    sed -i -E 's/^ssl = .*/ssl = required/' /etc/dovecot/dovecot.conf
    sed -i -E 's/^auth_allow_cleartext = .*/auth_allow_cleartext = no/' /etc/dovecot/conf.d/10-auth.conf
    cat > /etc/dovecot/conf.d/10-ssl-limristem-mail.conf <<CONF
ssl_server_cert_file = $TLS_CERT
ssl_server_key_file = $TLS_KEY
CONF
  fi
}

setup_postsrsd() {
  if [[ "$ENABLE_SRS" != "yes" ]]; then
    clear_postfix_srs_maps
    systemctl disable --now postsrsd >/dev/null 2>&1 || true
    systemctl disable --now limristem-mail-postsrsd-sync.timer >/dev/null 2>&1 || true
    return 0
  fi

  log "Configuro PostSRSd"
  if ! ensure_postsrsd_installed; then
    clear_postfix_srs_maps
    reload_postfix_if_active
    return 0
  fi
  log "PostSRSd: backup configurazione precedente..."
  if ! backup_file /etc/default/postsrsd 2>/dev/null; then
    warn "backup /etc/default/postsrsd fallito, continuo."
  fi
  log "PostSRSd: genero configurazione..."
  if ! render "$BASE_DIR/templates/postsrsd/default" /etc/default/postsrsd; then
    clear_postfix_srs_maps
    reload_postfix_if_active
    warn "Render /etc/default/postsrsd fallito: PostSRSd potrebbe non funzionare."
    return 0
  fi
  log "PostSRSd: genero segreto SRS..."
  write_postsrsd_secret
  log "PostSRSd: preparo bootstrap domini SRS..."
  bootstrap_postsrsd_domains || warn "Bootstrap domini SRS fallito."
  log "PostSRSd: installo unità systemd..."
  render "$BASE_DIR/templates/systemd/limristem-mail-postsrsd-sync.service" /etc/systemd/system/limristem-mail-postsrsd-sync.service || true
  render "$BASE_DIR/templates/systemd/limristem-mail-postsrsd-sync.timer" /etc/systemd/system/limristem-mail-postsrsd-sync.timer || true
  systemctl daemon-reload
  systemctl enable postsrsd limristem-mail-postsrsd-sync.timer 2>/dev/null || true
  log "PostSRSd: riavvio servizio..."
  systemctl restart --no-block postsrsd || true
  if ! wait_for_active_service postsrsd 20; then
    clear_postfix_srs_maps
    reload_postfix_if_active
    warn "PostSRSd non è partito correttamente. SRS potrebbe non funzionare. Controlla: journalctl -u postsrsd"
  else
    systemctl start limristem-mail-postsrsd-sync.timer 2>/dev/null || true
    log "PostSRSd: sincronizzo domini SRS iniziali..."
    if ! timeout 30 env \
      LIMRISTEM_MAIL_ENABLE_SRS="$ENABLE_SRS" \
      LIMRISTEM_MAIL_SRS_DOMAIN="$SRS_DOMAIN" \
      LIMRISTEM_MAIL_PRIMARY_DOMAIN="$PRIMARY_DOMAIN" \
      LIMRISTEM_MAIL_DB_HOST="$DB_HOST" \
      LIMRISTEM_MAIL_DB_PORT="$DB_PORT" \
      LIMRISTEM_MAIL_DB_USER="$DB_USER" \
      LIMRISTEM_MAIL_DB_PASS="$DB_PASS" \
      LIMRISTEM_MAIL_DB_NAME="$DB_NAME" \
        "$(runtime_bin_dir)/sync-postsrsd-domains.sh" 2>&1; then
      clear_postfix_srs_maps
      reload_postfix_if_active
      warn "Sync iniziale PostSRSd non riuscito: lascio SRS disabilitato finché i domini esclusi non vengono sincronizzati correttamente."
      return 0
    fi
    apply_postfix_srs_maps
    reload_postfix_if_active
    log "PostSRSd pronto."
  fi
}

configure_limristem_mail_service() {
  log "Genero service file unificato Limristem eMail"
  backup_file /etc/systemd/system/limristem-mail.service
  render "$BASE_DIR/templates/systemd/limristem-mail.service" /etc/systemd/system/limristem-mail.service
  rm -f /etc/systemd/system/limristem-mail-api.service
  systemctl daemon-reload
  if [[ "$ENABLE_API" == "yes" ]]; then
    systemctl enable limristem-mail
  else
    systemctl disable --now limristem-mail >/dev/null 2>&1 || true
  fi
}

configure_admin_helpers() {
  backup_file /etc/sudoers.d/limristem-mail-admin
  render "$BASE_DIR/templates/sudoers/limristem-mail-admin" /etc/sudoers.d/limristem-mail-admin
  chmod 440 /etc/sudoers.d/limristem-mail-admin
  visudo -cf /etc/sudoers.d/limristem-mail-admin >/dev/null
}

setup_firewall() {
  log "Configuro firewall nftables"
  timeout 30 "$(runtime_bin_dir)/manage-firewall.sh" apply || warn "Configurazione firewall non riuscita."
}

setup_fail2ban() {
  log "Configuro Fail2ban"
  mkdir -p /etc/fail2ban/jail.d
  backup_file /etc/fail2ban/jail.d/limristem-mail.local
  render "$BASE_DIR/templates/fail2ban/jail.d/limristem-mail.local" /etc/fail2ban/jail.d/limristem-mail.local
  systemctl enable --now fail2ban >/dev/null 2>&1 || warn "Fail2ban non è partito correttamente."
}

setup_api_runtime() {
  local runtime_bin requirements_file
  log "Preparo runtime Python Limristem eMail"
  runtime_bin=$(runtime_bin_dir)
  chmod 750 "$runtime_bin"/*.sh
  python3 -m venv --clear "$BASE_DIR/.venv"
  requirements_file=$BASE_DIR/bin/api/requirements.txt
  if [[ ! -f "$requirements_file" ]]; then
    requirements_file=$BASE_DIR/api/requirements.txt
  fi
  "$BASE_DIR/.venv/bin/pip" install --quiet -r "$requirements_file"
  chown -R limristem-mail:limristem-mail "$BASE_DIR/.venv"
}

install_cli_launcher() {
  ln -sfn "$BASE_DIR/limristem-mail" /usr/local/bin/limristem-mail
}

configure_nginx_api() {
  if [[ "$ENABLE_NGINX" != "yes" ]]; then
    rm -f /etc/nginx/sites-enabled/limristem-mail-api.conf /etc/nginx/sites-available/limristem-mail-api.conf
    rm -f /etc/nginx/sites-enabled/limristem-mail-mta-sts.conf /etc/nginx/sites-available/limristem-mail-mta-sts.conf
    systemctl disable --now nginx >/dev/null 2>&1 || true
    return 0
  fi
  if [[ -z "${TLS_CERT:-}" || ! -f "${TLS_CERT:-}" ]]; then
    log "Nessun certificato disponibile: lascio l'API solo su bind locale ${API_BIND}:${API_PORT}"
    rm -f /etc/nginx/sites-enabled/limristem-mail-api.conf /etc/nginx/sites-available/limristem-mail-api.conf
    rm -f /etc/nginx/sites-enabled/limristem-mail-mta-sts.conf /etc/nginx/sites-available/limristem-mail-mta-sts.conf
    systemctl disable --now nginx >/dev/null 2>&1 || true
    return 0
  fi

  log "Configuro reverse proxy TLS per API"
  if [[ "$ENABLE_API" == "yes" ]]; then
    backup_file /etc/nginx/sites-available/limristem-mail-api.conf
    render "$BASE_DIR/templates/nginx/limristem-mail-api.conf" /etc/nginx/sites-available/limristem-mail-api.conf
    ln -sf /etc/nginx/sites-available/limristem-mail-api.conf /etc/nginx/sites-enabled/limristem-mail-api.conf
  else
    rm -f /etc/nginx/sites-enabled/limristem-mail-api.conf /etc/nginx/sites-available/limristem-mail-api.conf
  fi
  if [[ "$ENABLE_MTA_STS" == "yes" ]]; then
    write_mta_sts_policy
    backup_file /etc/nginx/sites-available/limristem-mail-mta-sts.conf
    render "$BASE_DIR/templates/nginx/limristem-mail-mta-sts.conf" /etc/nginx/sites-available/limristem-mail-mta-sts.conf
    ln -sf /etc/nginx/sites-available/limristem-mail-mta-sts.conf /etc/nginx/sites-enabled/limristem-mail-mta-sts.conf
  else
    rm -f /etc/nginx/sites-enabled/limristem-mail-mta-sts.conf /etc/nginx/sites-available/limristem-mail-mta-sts.conf
  fi
  if [[ "$ENABLE_API" != "yes" && "$ENABLE_MTA_STS" != "yes" ]]; then
    systemctl disable --now nginx >/dev/null 2>&1 || true
    return 0
  fi
  rm -f /etc/nginx/sites-enabled/default
  nginx -t
  systemctl enable --now nginx
  systemctl restart nginx
}

setup_backup_jobs() {
  log "Configuro backup schedulati"
  render "$BASE_DIR/templates/systemd/limristem-mail-backup.service" /etc/systemd/system/limristem-mail-backup.service
  render_backup_timer_unit /etc/systemd/system/limristem-mail-backup.timer
  systemctl daemon-reload
  write_backup_env_file
  if [[ "$ENABLE_BACKUP_TIMER" == "yes" ]]; then
    systemctl enable --now limristem-mail-backup.timer
  else
    systemctl disable --now limristem-mail-backup.timer >/dev/null 2>&1 || true
  fi
  "$(runtime_bin_dir)/manage-backups.sh" sync-schedules >/dev/null 2>&1 || true
}

setup_live_deployment() {
  log "Genero bundle per deploy live"
  "$(runtime_bin_dir)/export-live-bundle.sh" "$PRIMARY_DOMAIN" "$HOSTNAME_FQDN" "$PUBLIC_IP" "$PUBLIC_IPV6" "$DKIM_SELECTOR" "$LIVE_BUNDLE_DIR" "$MTA_STS_HOST"
  if [[ "$ENABLE_DELIVERABILITY_TIMER" == "yes" ]]; then
    render "$BASE_DIR/templates/systemd/limristem-mail-deliverability-report.service" /etc/systemd/system/limristem-mail-deliverability-report.service
    render "$BASE_DIR/templates/systemd/limristem-mail-deliverability-report.timer" /etc/systemd/system/limristem-mail-deliverability-report.timer
    systemctl daemon-reload
    systemctl enable --now limristem-mail-deliverability-report.timer
  else
    systemctl disable --now limristem-mail-deliverability-report.timer >/dev/null 2>&1 || true
    rm -f /etc/systemd/system/limristem-mail-deliverability-report.service /etc/systemd/system/limristem-mail-deliverability-report.timer
    systemctl daemon-reload
  fi
}

validate_stack() {
  log "Valido configurazione servizi"
  if ! postfix check 2>&1; then
    warn "postfix check ha segnalato errori."
  fi
  if ! doveconf -n >/dev/null 2>&1; then
    warn "doveconf ha segnalato errori nella configurazione Dovecot."
  fi
  if [[ "$ENABLE_RSPAMD" == "yes" ]]; then
    if ! rspamadm configtest >/dev/null 2>&1; then
      warn "rspamadm configtest ha segnalato errori nella configurazione Rspamd."
    fi
  fi
  if [[ "$ENABLE_NGINX" == "yes" ]] && systemctl is-enabled nginx >/dev/null 2>&1; then
    if ! nginx -t >/dev/null 2>&1; then
      warn "nginx -t ha segnalato errori nella configurazione Nginx."
    fi
  fi
  local runtime_bin
  runtime_bin=$(runtime_bin_dir)
  bash -n "$BASE_DIR/install.sh" "$runtime_bin/install.sh" "$runtime_bin/libenv.sh" "$runtime_bin/wget-install.sh" "$runtime_bin/backup.sh" "$runtime_bin/restore.sh" "$runtime_bin/performance-report.sh" "$runtime_bin/sync-postsrsd-domains.sh" "$runtime_bin/deliverability-check.sh" "$runtime_bin/generate-dns-plan.sh" "$runtime_bin/export-live-bundle.sh" "$runtime_bin/deliverability-report.sh" "$runtime_bin/manage-queue.sh" "$runtime_bin/manage-bans.sh" "$runtime_bin/manage-limits.sh" "$runtime_bin/manage-backups.sh" "$runtime_bin/manage-api-credentials.sh" "$runtime_bin/manage-firewall.sh" "$runtime_bin/manage-ssl.sh"
}

restart_managed_services() {
  log "Riavvio servizi core"
  systemctl restart postfix || warn "Postfix non è ripartito. Controlla: journalctl -u postfix"
  systemctl restart dovecot || warn "Dovecot non è ripartito. Controlla: journalctl -u dovecot"
  systemctl restart redis-server || warn "Redis non è ripartito. Controlla: journalctl -u redis-server"
  if [[ "$ENABLE_RSPAMD" == "yes" ]]; then
    systemctl restart rspamd || warn "Rspamd non è ripartito. Controlla: journalctl -u rspamd"
  fi
  if [[ "$ENABLE_API" == "yes" ]]; then
    systemctl restart limristem-mail
    log "Attendo API Limristem eMail"
    if ! wait_for_active_service limristem-mail 30; then
      warn "Limristem eMail non è partito correttamente. Controlla: journalctl -u limristem-mail"
    elif ! wait_for_http_ok "http://$(api_health_host):$API_PORT/health" 20; then
      journalctl --no-pager -u limristem-mail -n 50 >&2 || true
      warn "API Limristem eMail non risponde su http://$(api_health_host):$API_PORT/health"
    fi
  fi
  if [[ "$ENABLE_NGINX" == "yes" ]] && systemctl is-enabled nginx >/dev/null 2>&1; then
    systemctl restart nginx
  fi
  if [[ "$ENABLE_WEB_PANEL" == "yes" ]]; then
    log "Verifico pannello web HTTPS"
    if ! wait_for_http_ok "https://127.0.0.1/panel/login" 20 "$HOSTNAME_FQDN" yes; then
      journalctl --no-pager -u limristem-mail -n 50 >&2 || true
      journalctl --no-pager -u nginx -n 50 >&2 || true
      warn "Pannello web non raggiungibile su https://$HOSTNAME_FQDN/panel/login"
    fi
  fi
}

update_existing_installation() {
  log "Modalità UPDATE: aggiorno template, pannello e servizi senza reinstallazione completa"
  repair_dovecot_24_compat
  create_service_user
  setup_backup_directories
  setup_config_permissions
  setup_database
  create_vmail_user
  setup_dkim_permissions
  setup_postfix
  setup_dovecot
  setup_rspamd
  setup_ssl
  setup_postsrsd
  configure_limristem_mail_service
  configure_admin_helpers
  setup_api_runtime
  generate_api_admin_hash
  generate_panel_admin_hash
  write_env_file
  setup_firewall
  setup_fail2ban
  setup_backup_jobs
  setup_live_deployment
  configure_nginx_api
  validate_stack
  restart_managed_services
}

overwrite_existing_installation() {
  log "Modalità OVERWRITE: rimuovo i file gestiti da Limristem eMail e riparto da zero"
  rm -f "$BASE_DIR/config/limristem-mail.env" "$BASE_DIR/config/limristem-mail-backup.env" "$BASE_DIR/config/postsrsd.secret" "$BASE_DIR/config/postsrsd.domains"
  rm -f /etc/limristem-mail.env /etc/limristem-mail-backup.env
  rm -f /etc/postsrsd.secret
  rm -f /etc/default/postsrsd
  rm -f /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/conf.d/auth-sql.conf.ext /etc/dovecot/conf.d/10-ssl-limristem-mail.conf
  rm -f /etc/postfix/mysql-virtual-domains-maps.cf /etc/postfix/mysql-virtual-mailbox-maps.cf /etc/postfix/mysql-virtual-alias-maps.cf /etc/postfix/mysql-virtual-redirect-maps.cf
  rm -f /etc/nginx/sites-available/limristem-mail-api.conf /etc/nginx/sites-enabled/limristem-mail-api.conf
  rm -f /etc/nginx/sites-available/limristem-mail-mta-sts.conf /etc/nginx/sites-enabled/limristem-mail-mta-sts.conf
  rm -f /etc/systemd/system/limristem-mail.service /etc/systemd/system/limristem-mail-api.service /etc/systemd/system/limristem-mail-backup.service /etc/systemd/system/limristem-mail-backup.timer
  rm -f /etc/systemd/system/limristem-mail-deliverability-report.service /etc/systemd/system/limristem-mail-deliverability-report.timer
  rm -f /etc/systemd/system/limristem-mail-postsrsd-sync.service /etc/systemd/system/limristem-mail-postsrsd-sync.timer
  rm -f /etc/sudoers.d/limristem-mail-admin
  rm -f /etc/rspamd/local.d/options.inc /etc/rspamd/local.d/redis.conf /etc/rspamd/local.d/classifier-bayes.conf
  rm -f /etc/rspamd/local.d/dkim_signing.conf /etc/rspamd/local.d/milter_headers.conf /etc/rspamd/local.d/greylisting.conf
  rm -f /etc/rspamd/local.d/greylist.conf /etc/rspamd/local.d/actions.conf /etc/rspamd/local.d/worker-controller.inc
  rm -f /etc/rspamd/local.d/worker-proxy.inc
  systemctl disable --now limristem-mail limristem-mail-api limristem-mail-backup.timer limristem-mail-deliverability-report.timer limristem-mail-postsrsd-sync.timer >/dev/null 2>&1 || true
  systemctl daemon-reload
}

main() {
  require_root
  _setup_logging
  log "Inizio installazione Limristem eMail — $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
  ensure_locales
  require_supported_os

  _early_base=${LIMRISTEM_MAIL_BASE_DIR:-$(pwd)}
  _early_main_env=$_early_base/config/limristem-mail.env
  _early_backup_env=$_early_base/config/limristem-mail-backup.env
  if [[ "${LIMRISTEM_MAIL_OVERWRITE_MODE:-no}" != "yes" ]]; then
    if [[ -f /etc/limristem-mail.env ]]; then
      _early_main_env=/etc/limristem-mail.env
      _early_backup_env=/etc/limristem-mail-backup.env
    fi
    limristem_mail_normalize_env_file "$_early_main_env"
    limristem_mail_normalize_env_file "$_early_backup_env"
    limristem_mail_load_env_file "$_early_main_env"
    limristem_mail_load_env_file "$_early_backup_env"
  fi

  BASE_DIR=${LIMRISTEM_MAIL_BASE_DIR:-$(pwd)}
  PACKAGE_VERSION=$(python3 - "$BASE_DIR/version.json" <<'PY'
import json
import pathlib
import sys

path = pathlib.Path(sys.argv[1])
payload = json.loads(path.read_text(encoding="utf-8"))
if isinstance(payload, list) and payload:
    payload = payload[0]
version = str(payload.get("version", "")).strip() if isinstance(payload, dict) else ""
if not version:
    raise SystemExit("version.json does not contain a version")
print(version)
PY
)
  chmod 750 "$(runtime_bin_dir)"/*.sh
  HOSTNAME_FQDN=$(resolve_default_hostname)
  ensure_fqdn_hostname
  SSL_MODE=${LIMRISTEM_MAIL_SSL_MODE:-selfsigned}
  LE_EMAIL=${LIMRISTEM_MAIL_LE_EMAIL:-postmaster@${HOSTNAME_FQDN}}
  TLS_CERT=${LIMRISTEM_MAIL_TLS_CERT_PATH:-}
  TLS_KEY=${LIMRISTEM_MAIL_TLS_KEY_PATH:-}
  DB_HOST=${LIMRISTEM_MAIL_DB_HOST:-127.0.0.1}
  DB_PORT=${LIMRISTEM_MAIL_DB_PORT:-3306}
  DB_NAME=${LIMRISTEM_MAIL_DB_NAME:-limristem-mail}
  DB_USER=${LIMRISTEM_MAIL_DB_USER:-$(gen_user 14)}
  DB_PASS=${LIMRISTEM_MAIL_DB_PASS:-$(gen_pass 24)}
  DB_USER_HOST=${LIMRISTEM_MAIL_DB_USER_HOST:-127.0.0.1}
  DB_ROOT_PASS=${LIMRISTEM_MAIL_DB_ROOT_PASS:-}
  REDIS_HOST=${LIMRISTEM_MAIL_REDIS_HOST:-127.0.0.1}
  REDIS_PORT=${LIMRISTEM_MAIL_REDIS_PORT:-6379}
  REDIS_PASSWORD=${LIMRISTEM_MAIL_REDIS_PASSWORD:-$(gen_pass 24)}
  RSPAMD_PASSWORD=${LIMRISTEM_MAIL_RSPAMD_PASSWORD:-$(gen_pass 18)}
  ENABLE_API=${LIMRISTEM_MAIL_ENABLE_API:-yes}
  ENABLE_NGINX=${LIMRISTEM_MAIL_ENABLE_NGINX:-yes}
  ENABLE_RSPAMD=${LIMRISTEM_MAIL_ENABLE_RSPAMD:-yes}
  ENABLE_BACKUP_TIMER=${LIMRISTEM_MAIL_ENABLE_BACKUP_TIMER:-yes}
  ENABLE_AUTO_UPDATES=${LIMRISTEM_MAIL_ENABLE_AUTO_UPDATES:-no}
  ENABLE_WEB_PANEL=${LIMRISTEM_MAIL_ENABLE_WEB_PANEL:-no}
  SERVER_NAME=${LIMRISTEM_MAIL_SERVER_NAME:-Limristem eMail}
  API_ADMIN_USER=${LIMRISTEM_MAIL_API_ADMIN_USER:-$(gen_user 12)}
  if [[ -n ${LIMRISTEM_MAIL_API_ADMIN_PASS_HASH:-} && -z ${LIMRISTEM_MAIL_API_ADMIN_PASS:-} ]]; then
    API_ADMIN_PASS=''
    API_ADMIN_PASS_HASH=$LIMRISTEM_MAIL_API_ADMIN_PASS_HASH
  else
    API_ADMIN_PASS=${LIMRISTEM_MAIL_API_ADMIN_PASS:-$(gen_pass 24)}
    API_ADMIN_PASS_HASH=''
  fi
  PANEL_ADMIN_USER=${LIMRISTEM_MAIL_PANEL_ADMIN_USER:-$(gen_user 12)}
  PANEL_SESSION_TTL_SECONDS=${LIMRISTEM_MAIL_PANEL_SESSION_TTL_SECONDS:-28800}
  PANEL_LOGIN_CSRF_SECRET=${LIMRISTEM_MAIL_PANEL_LOGIN_CSRF_SECRET:-$(gen_pass 48)}
  if [[ -n ${LIMRISTEM_MAIL_PANEL_ADMIN_PASS_HASH:-} && -z ${LIMRISTEM_MAIL_PANEL_ADMIN_PASS:-} ]]; then
    PANEL_ADMIN_PASS=''
    PANEL_ADMIN_PASS_HASH=$LIMRISTEM_MAIL_PANEL_ADMIN_PASS_HASH
  else
    PANEL_ADMIN_PASS=${LIMRISTEM_MAIL_PANEL_ADMIN_PASS:-$(gen_pass 24)}
    PANEL_ADMIN_PASS_HASH=''
  fi
  API_BIND=${LIMRISTEM_MAIL_API_BIND:-127.0.0.1}
  API_PORT=${LIMRISTEM_MAIL_API_PORT:-8080}
  API_AUTH_FAIL_LIMIT=${LIMRISTEM_MAIL_API_AUTH_FAIL_LIMIT:-5}
  API_AUTH_WINDOW_SECONDS=${LIMRISTEM_MAIL_API_AUTH_WINDOW_SECONDS:-300}
  API_AUTH_BLOCK_SECONDS=${LIMRISTEM_MAIL_API_AUTH_BLOCK_SECONDS:-900}
  CACHE_TTL_SECONDS=${LIMRISTEM_MAIL_CACHE_TTL_SECONDS:-3600}
  MAIL_HOME=${LIMRISTEM_MAIL_MAIL_HOME:-/var/mail/vhosts}
  DKIM_KEYS_DIR=${LIMRISTEM_MAIL_DKIM_KEYS_DIR:-/var/lib/limristem-mail/dkim}
  PRIMARY_DOMAIN=${LIMRISTEM_MAIL_PRIMARY_DOMAIN:-${HOSTNAME_FQDN}}
  ENABLE_MTA_STS=${LIMRISTEM_MAIL_ENABLE_MTA_STS:-yes}
  MTA_STS_HOST=${LIMRISTEM_MAIL_MTA_STS_HOST:-mta-sts.${PRIMARY_DOMAIN}}
  MTA_STS_MODE=${LIMRISTEM_MAIL_MTA_STS_MODE:-enforce}
  MTA_STS_MAX_AGE=${LIMRISTEM_MAIL_MTA_STS_MAX_AGE:-604800}
  MTA_STS_ID=${LIMRISTEM_MAIL_MTA_STS_ID:-${MTA_STS_MODE}-${MTA_STS_MAX_AGE}}
  ENABLE_TLS_RPT=${LIMRISTEM_MAIL_ENABLE_TLS_RPT:-yes}
  TLS_RPT_MAILBOX=${LIMRISTEM_MAIL_TLS_RPT_MAILBOX:-postmaster@${PRIMARY_DOMAIN}}
  DKIM_SELECTOR=${LIMRISTEM_MAIL_DKIM_SELECTOR:-default}
  PUBLIC_IP=${LIMRISTEM_MAIL_PUBLIC_IP:-}
  PUBLIC_IPV6=${LIMRISTEM_MAIL_PUBLIC_IPV6:-}
  LIVE_BUNDLE_DIR=${LIMRISTEM_MAIL_LIVE_BUNDLE_DIR:-/var/lib/limristem-mail/live-deployment}
  ENABLE_DELIVERABILITY_TIMER=${LIMRISTEM_MAIL_ENABLE_DELIVERABILITY_TIMER:-no}
  DELIVERABILITY_ONCALENDAR=${LIMRISTEM_MAIL_DELIVERABILITY_ONCALENDAR:-*-*-* 04:30:00}
  VMAIL_UID=${LIMRISTEM_MAIL_VMAIL_UID:-150}
  VMAIL_GID=${LIMRISTEM_MAIL_VMAIL_GID:-150}
  MILTER_DEFAULT_ACTION=${LIMRISTEM_MAIL_MILTER_DEFAULT_ACTION:-accept}
  POSTFIX_RATE_TIME_UNIT=${LIMRISTEM_MAIL_POSTFIX_RATE_TIME_UNIT:-60s}
  POSTFIX_CLIENT_CONNECTION_RATE_LIMIT=${LIMRISTEM_MAIL_POSTFIX_CLIENT_CONNECTION_RATE_LIMIT:-30}
  POSTFIX_CLIENT_MESSAGE_RATE_LIMIT=${LIMRISTEM_MAIL_POSTFIX_CLIENT_MESSAGE_RATE_LIMIT:-100}
  RSPAMD_GREYLIST_ENABLED=${LIMRISTEM_MAIL_RSPAMD_GREYLIST_ENABLED:-true}
  RSPAMD_GREYLIST_DELAY=${LIMRISTEM_MAIL_RSPAMD_GREYLIST_DELAY:-5m}
  RSPAMD_GREYLIST_EXPIRE=${LIMRISTEM_MAIL_RSPAMD_GREYLIST_EXPIRE:-35d}
  RSPAMD_ACTION_GREYLIST=${LIMRISTEM_MAIL_RSPAMD_ACTION_GREYLIST:-4}
  RSPAMD_ACTION_ADD_HEADER=${LIMRISTEM_MAIL_RSPAMD_ACTION_ADD_HEADER:-6}
  RSPAMD_ACTION_REJECT=${LIMRISTEM_MAIL_RSPAMD_ACTION_REJECT:-15}
  ENABLE_SRS=${LIMRISTEM_MAIL_ENABLE_SRS:-yes}
  SRS_DOMAIN=${LIMRISTEM_MAIL_SRS_DOMAIN:-$HOSTNAME_FQDN}
  BACKUP_ONCALENDAR=${LIMRISTEM_MAIL_BACKUP_ONCALENDAR:-*-*-* 03:15:00}
  # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
  NGINX_SERVER_NAME=${LIMRISTEM_MAIL_API_SERVER_NAME:-$HOSTNAME_FQDN}
  if [[ "$ENABLE_WEB_PANEL" == "yes" ]]; then
    # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
    NGINX_ROOT_TARGET=/panel/login
  else
    NGINX_ROOT_TARGET=/health
  fi
  # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
  RSPAMD_HASHED=$RSPAMD_PASSWORD
  validate_installer_values

  if [[ "$ENABLE_WEB_PANEL" == "yes" ]]; then
    ENABLE_API=yes
    ENABLE_NGINX=yes
    if [[ "$SSL_MODE" == "plain" ]]; then
      log "Il pannello richiede HTTPS: imposto automaticamente SSL selfsigned."
      SSL_MODE=selfsigned
    fi
    # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
    NGINX_ROOT_TARGET=/panel/login
  fi
  if [[ "$ENABLE_NGINX" != "yes" ]]; then
    ENABLE_MTA_STS=no
  fi

  if [[ "${LIMRISTEM_MAIL_OVERWRITE_MODE:-no}" == "yes" ]]; then
    overwrite_existing_installation
  fi
  if [[ "${LIMRISTEM_MAIL_UPDATE_MODE:-no}" == "yes" ]]; then
    # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
    RSPAMD_HASHED=$(rspamadm pw -p "$RSPAMD_PASSWORD")
    update_existing_installation
    install_cli_launcher
    log "Aggiornamento completato."
    exit 0
  fi

  repair_dovecot_24_compat
  install_packages
  # shellcheck disable=SC2034  # Used indirectly by render() through variable-name expansion.
  RSPAMD_HASHED=$(rspamadm pw -p "$RSPAMD_PASSWORD")
  create_service_user
  setup_backup_directories
  setup_config_permissions
  create_vmail_user
  setup_dkim_permissions
  setup_hostname
  setup_database
  configure_redis
  setup_postfix
  setup_dovecot
  setup_rspamd
  setup_ssl
  setup_postsrsd
  configure_limristem_mail_service
  configure_admin_helpers
  setup_api_runtime
  install_cli_launcher
  generate_api_admin_hash
  generate_panel_admin_hash
  write_env_file
  setup_firewall
  setup_fail2ban
  setup_backup_jobs
  setup_live_deployment
  configure_nginx_api
  validate_stack

  restart_managed_services

  # --- Final summary ---
  local detected_ip="${PUBLIC_IP:-}"
  if [[ -z "$detected_ip" ]]; then
    detected_ip=$(hostname -I 2>/dev/null | awk '{print $1}') || detected_ip="<server-ip>"
  fi

  log ""
  log "============================================================"
  log " Installazione Limristem eMail completata"
  log "============================================================"
  log ""
  log " Hostname  : $HOSTNAME_FQDN"
  log " Server IP : $detected_ip"
  log " Log file  : $LOG_FILE"
  log ""

  if [[ "$ENABLE_API" == "yes" ]]; then
    log "--- API Limristem eMail ---"
    if [[ "$ENABLE_NGINX" == "yes" ]]; then
      log " Endpoint   : https://$HOSTNAME_FQDN/"
      log " Endpoint IP: https://$detected_ip/"
      log " Health URL  : https://$HOSTNAME_FQDN/health"
    else
      log " Endpoint   : http://$(api_health_host):$API_PORT/"
      log " Health URL  : http://$(api_health_host):$API_PORT/health"
    fi
    log " Admin user : $API_ADMIN_USER"
    if [[ -n ${API_ADMIN_PASS:-} ]]; then
      log " Admin pass : $API_ADMIN_PASS"
      log " (conserva questa password: nel file env viene salvato solo l'hash)"
    else
      log " Admin pass : hash fornito dall'utente, nessuna password in chiaro salvata."
    fi
    log ""
  else
    log " API disabilitata."
    log ""
  fi

  if [[ "$ENABLE_WEB_PANEL" == "yes" ]]; then
    log "--- Pannello Web ---"
    log " URL         : https://$HOSTNAME_FQDN/panel/login"
    log " URL (IP)    : https://$detected_ip/panel/login"
    log " Admin user  : $PANEL_ADMIN_USER"
    if [[ -n ${PANEL_ADMIN_PASS:-} ]]; then
      log " Admin pass  : $PANEL_ADMIN_PASS"
      log " (conserva questa password: nel file env viene salvato solo l'hash)"
    else
      log " Admin pass  : hash fornito dall'utente, nessuna password in chiaro salvata."
    fi
    log ""
  fi

  if (( ${#INSTALL_WARNINGS[@]} > 0 )); then
    log "--- Avvisi durante l'installazione ---"
    for w in "${INSTALL_WARNINGS[@]}"; do
      log " ⚠  $w"
    done
    log ""
  fi

  log " Bundle deploy live : $LIVE_BUNDLE_DIR"
  log " Performance report : $(runtime_bin_dir)/performance-report.sh"
  log "============================================================"
  log ""
}

main "$@"
