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

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

log() { printf '[install] %s\n' "$*"; }

usage() {
  cat <<'EOF'
Usage: ./install.sh [options]

Interactive installer wrapper for Limristem eMail. Missing values are prompted when a TTY is available.

Options:
  -u, --update                        Update an existing Limristem eMail install without reinitializing it
  -o, --overwrite                     Force a fresh reinstall and replace Limristem eMail-managed configs
  --hostname <fqdn>                     Mail hostname (example: mail.example.com)
  --path, --install-path <dir>          Installation path (default: /opt/limristem-mail)
  --ssl-mode <plain|selfsigned|letsencrypt>
  --le-email <email>                    Let's Encrypt email
  --public-ip <ipv4>
  --public-ipv6 <ipv6>
  --dkim-selector <selector>            Default DKIM selector (default: default)
  --enable-api <yes|no>
  --enable-nginx <yes|no>
  --enable-rspamd <yes|no>
  --enable-srs <yes|no>
  --enable-mta-sts <yes|no>
  --enable-tls-rpt <yes|no>
  --enable-backup-timer <yes|no>
  --enable-deliverability-timer <yes|no>
  --enable-web-panel <yes|no>
  --dry-run                            Show resolved settings without installing
  --non-interactive                     Do not prompt; fail if required values are invalid
  --help                                Show this help
EOF
}

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

normalize_bool() {
  local value=${1,,}
  case "$value" in
    y|yes|1|true|on) printf 'yes\n' ;;
    n|no|0|false|off) printf 'no\n' ;;
    *)
      log "Valore booleano non valido: $1"
      exit 1
      ;;
  esac
}

default_hostname() {
  local candidate=''
  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
  printf 'mail.example.com\n'
}

ensure_fqdn() {
  local value=${1,,}
  value=${value%.}
  if [[ ${#value} -le 253 && "$value" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then
    printf '%s\n' "$value"
    return 0
  fi
  log "Hostname non valido: '$1'. Usa un FQDN ASCII come mail.example.com"
  exit 1
}

validate_install_path() {
  local value=$1
  if [[ "$value" =~ ^/[A-Za-z0-9._/-]+$ && "$value" != "/" && "$value" != *"/../"* && "$value" != *"/.." && "$value" != *"/./"* ]]; then
    printf '%s\n' "${value%/}"
    return 0
  fi
  log "Percorso di installazione non valido: '$value'. Usa un percorso assoluto senza spazi o '..'."
  exit 1
}

validate_ip_if_set() {
  local value=$1
  local family=$2
  [[ -z "$value" ]] && return 0
  python3 - "$value" "$family" <<'PY'
import ipaddress
import sys

address = ipaddress.ip_address(sys.argv[1])
expected = int(sys.argv[2])
if address.version != expected:
    raise SystemExit(f"Expected IPv{expected} address")
PY
}

prompt_value() {
  local prompt=$1
  local default_value=$2
  local current_value=$3
  local input=''
  local resolved_default=${current_value:-$default_value}
  if [[ "$NON_INTERACTIVE" == "yes" ]]; then
    printf '%s\n' "$resolved_default"
    return 0
  fi
  if [ -t 0 ]; then
    read -r -p "$prompt [$resolved_default]: " input
  elif [ -e /dev/tty ]; then
    read -r -p "$prompt [$resolved_default]: " input < /dev/tty
  else
    input=""
  fi
  printf '%s\n' "${input:-$resolved_default}"
}

prompt_bool() {
  local prompt=$1
  local default_value=$2
  local current_value=$3
  local input=''
  local resolved normalized default_hint
  resolved=${current_value:-$default_value}
  normalized=$(normalize_bool "$resolved")
  if [[ "$normalized" == "yes" ]]; then
    default_hint='y'
  else
    default_hint='n'
  fi
  if [[ "$NON_INTERACTIVE" == "yes" ]]; then
    printf '%s\n' "$normalized"
    return 0
  fi
  while true; do
    if [ -t 0 ]; then
      read -r -p "$prompt [y/n, default $default_hint]: " input
    elif [ -e /dev/tty ]; then
      read -r -p "$prompt [y/n, default $default_hint]: " input < /dev/tty
    else
      input=""
    fi
    input=${input:-$default_hint}
    case "${input,,}" in
      y|n)
        normalize_bool "$input"
        return 0
        ;;
      *)
        log "Rispondi solo con y o n."
        ;;
    esac
  done
}

sync_package() {
  local source_real target_real
  source_real=$(readlink -f "$SOURCE_DIR")
  mkdir -p "$INSTALL_PATH"
  target_real=$(readlink -f "$INSTALL_PATH")
  if [[ "$source_real" == "$target_real" ]]; then
    return 0
  fi
  case "$target_real" in
    "$source_real"/*)
      log "Il percorso di installazione non può stare dentro la sorgente del pacchetto."
      exit 1
      ;;
  esac
  log "Sincronizzo Limristem eMail in $INSTALL_PATH"
  mkdir -p "$INSTALL_PATH/bin" "$INSTALL_PATH/bin/api" "$INSTALL_PATH/bin/panel" "$INSTALL_PATH/config" "$INSTALL_PATH/templates" "$INSTALL_PATH/database"
  if command -v rsync >/dev/null 2>&1; then
    install -m 0755 "$SOURCE_DIR/limristem-mail" "$INSTALL_PATH/limristem-mail"
    install -m 0755 "$SOURCE_DIR/install.sh" "$INSTALL_PATH/install.sh"
    install -m 0644 "$SOURCE_DIR/version.json" "$INSTALL_PATH/version.json"
    if [ -f "$SOURCE_DIR/limristem-email.png" ]; then install -m 0644 "$SOURCE_DIR/limristem-email.png" "$INSTALL_PATH/limristem-email.png"; fi
    rsync -a --delete \
      --exclude '.git' \
      --exclude '.venv' \
      --exclude '__pycache__' \
      --exclude '*.pyc' \
      "$SOURCE_DIR/scripts"/ "$INSTALL_PATH/bin"/
    rsync -a --delete \
      --exclude 'templates' \
      --exclude '__pycache__' \
      --exclude '*.pyc' \
      "$SOURCE_DIR/api"/ "$INSTALL_PATH/bin/api"/
    rsync -a --delete \
      --exclude '__pycache__' \
      --exclude '*.pyc' \
      "$SOURCE_DIR/api/templates"/ "$INSTALL_PATH/bin/panel"/
    rsync -a --delete \
      --exclude '__pycache__' \
      --exclude '*.pyc' \
      "$SOURCE_DIR/templates"/ "$INSTALL_PATH/templates"/
    rsync -a --delete \
      --exclude '__pycache__' \
      --exclude '*.pyc' \
      "$SOURCE_DIR/database"/ "$INSTALL_PATH/database"/
  else
    install -m 0755 "$SOURCE_DIR/limristem-mail" "$INSTALL_PATH/limristem-mail"
    install -m 0755 "$SOURCE_DIR/install.sh" "$INSTALL_PATH/install.sh"
    install -m 0644 "$SOURCE_DIR/version.json" "$INSTALL_PATH/version.json"
    tar --exclude='__pycache__' -C "$SOURCE_DIR/scripts" -cf - . | tar -C "$INSTALL_PATH/bin" -xf -
    tar --exclude='templates' --exclude='__pycache__' -C "$SOURCE_DIR/api" -cf - . | tar -C "$INSTALL_PATH/bin/api" -xf -
    tar --exclude='__pycache__' -C "$SOURCE_DIR/api/templates" -cf - . | tar -C "$INSTALL_PATH/bin/panel" -xf -
    tar --exclude='__pycache__' -C "$SOURCE_DIR/templates" -cf - . | tar -C "$INSTALL_PATH/templates" -xf -
    tar --exclude='__pycache__' -C "$SOURCE_DIR/database" -cf - . | tar -C "$INSTALL_PATH/database" -xf -
  fi
  rm -rf "$INSTALL_PATH/api" "$INSTALL_PATH/scripts"
  rm -f "$INSTALL_PATH/config/"*.example
}

existing_install_env_file() {
  if [[ -f "$INSTALL_PATH/config/limristem-mail.env" ]]; then
    printf '%s\n' "$INSTALL_PATH/config/limristem-mail.env"
  elif [[ -f /etc/limristem-mail.env ]]; then
    printf '/etc/limristem-mail.env\n'
  fi
}

NON_INTERACTIVE=no
DRY_RUN=no
INSTALL_PATH=/opt/limristem-mail
INSTALL_PATH_EXPLICIT=no
UPDATE_MODE=no
OVERWRITE_MODE=no
HOSTNAME_VALUE=
SSL_MODE=selfsigned
LE_EMAIL=
PUBLIC_IP=
PUBLIC_IPV6=
DKIM_SELECTOR=default
ENABLE_API=yes
ENABLE_NGINX=yes
ENABLE_RSPAMD=yes
ENABLE_SRS=yes
ENABLE_MTA_STS=no
ENABLE_TLS_RPT=no
ENABLE_BACKUP_TIMER=yes
ENABLE_DELIVERABILITY_TIMER=no
ENABLE_WEB_PANEL=no

while [[ $# -gt 0 ]]; do
  case "$1" in
    -u|--update)
      UPDATE_MODE=yes
      shift
      ;;
    -o|--overwrite)
      OVERWRITE_MODE=yes
      shift
      ;;
    --hostname)
      HOSTNAME_VALUE=${2:?}
      shift 2
      ;;
    --path|--install-path)
      INSTALL_PATH=${2:?}
      INSTALL_PATH_EXPLICIT=yes
      shift 2
      ;;
    --ssl-mode)
      SSL_MODE=${2:?}
      shift 2
      ;;
    --le-email)
      LE_EMAIL=${2:?}
      shift 2
      ;;
    --public-ip)
      PUBLIC_IP=${2:?}
      shift 2
      ;;
    --public-ipv6)
      PUBLIC_IPV6=${2:?}
      shift 2
      ;;
    --dkim-selector)
      DKIM_SELECTOR=${2:?}
      shift 2
      ;;
    --enable-api)
      ENABLE_API=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-nginx)
      ENABLE_NGINX=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-rspamd)
      ENABLE_RSPAMD=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-srs)
      ENABLE_SRS=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-mta-sts)
      ENABLE_MTA_STS=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-tls-rpt)
      ENABLE_TLS_RPT=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-backup-timer)
      ENABLE_BACKUP_TIMER=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-deliverability-timer)
      ENABLE_DELIVERABILITY_TIMER=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --enable-web-panel)
      ENABLE_WEB_PANEL=$(normalize_bool "${2:?}")
      shift 2
      ;;
    --non-interactive)
      NON_INTERACTIVE=yes
      shift
      ;;
    --dry-run)
      DRY_RUN=yes
      shift
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      log "Opzione non riconosciuta: $1"
      usage
      exit 1
      ;;
  esac
done

require_root

if [[ "$UPDATE_MODE" == "yes" && "$OVERWRITE_MODE" == "yes" ]]; then
  log "Le opzioni -u/--update e -o/--overwrite sono mutualmente esclusive."
  exit 1
fi

EXISTING_ENV_FILE=$(existing_install_env_file)
if [[ -n "$EXISTING_ENV_FILE" && "$DRY_RUN" != "yes" ]]; then
  limristem_mail_normalize_env_file "$EXISTING_ENV_FILE"
  if [[ "$EXISTING_ENV_FILE" == /etc/limristem-mail.env ]]; then
    limristem_mail_normalize_env_file /etc/limristem-mail-backup.env
  else
    limristem_mail_normalize_env_file "$INSTALL_PATH/config/limristem-mail-backup.env"
  fi
fi

if [[ "$UPDATE_MODE" == "yes" ]]; then
  if [[ -z "$EXISTING_ENV_FILE" ]]; then
    log "Nessuna installazione Limristem eMail esistente rilevata. Avvia ./install.sh senza -u per una nuova installazione."
    exit 1
  fi
  limristem_mail_load_env_file "$EXISTING_ENV_FILE"
  if [[ "$INSTALL_PATH_EXPLICIT" != "yes" && -n "${LIMRISTEM_MAIL_BASE_DIR:-}" ]]; then
    INSTALL_PATH=$LIMRISTEM_MAIL_BASE_DIR
  fi
  INSTALL_PATH=$(validate_install_path "$INSTALL_PATH")
  log "Limristem eMail risulta già installato: eseguo aggiornamento in modalità -u."
  sync_package
  export LIMRISTEM_MAIL_BASE_DIR=$INSTALL_PATH
  export LIMRISTEM_MAIL_UPDATE_MODE=yes
  export LIMRISTEM_MAIL_OVERWRITE_MODE=no
  export LIMRISTEM_MAIL_LOG_FILE=/var/log/limristem-mail-install.log
  cd "$INSTALL_PATH"
  exec bash ./bin/install.sh
fi

if [[ -n "$EXISTING_ENV_FILE" && "$OVERWRITE_MODE" != "yes" ]]; then
  log "Limristem eMail risulta già installato."
  log "Usa ./install.sh -u per aggiornare template, servizi e pannello senza reinstallazione completa."
  log "Usa ./install.sh -o per forzare una reinstallazione completa e sostituire i file gestiti da Limristem eMail."
  exit 1
fi

if [[ "$DRY_RUN" != "yes" && -f "$INSTALL_PATH/config/limristem-mail.env" ]]; then
  limristem_mail_normalize_env_file "$INSTALL_PATH/config/limristem-mail.env"
  limristem_mail_normalize_env_file "$INSTALL_PATH/config/limristem-mail-backup.env"
elif [[ "$DRY_RUN" != "yes" && -f /etc/limristem-mail.env ]]; then
  limristem_mail_normalize_env_file /etc/limristem-mail.env
  limristem_mail_normalize_env_file /etc/limristem-mail-backup.env
fi

INSTALL_PATH=$(prompt_value "Percorso di installazione" "/opt/limristem-mail" "$INSTALL_PATH")
INSTALL_PATH=$(validate_install_path "$INSTALL_PATH")
DEFAULT_HOSTNAME=$(default_hostname)
if [[ "$DEFAULT_HOSTNAME" != *.* ]]; then
  DEFAULT_HOSTNAME=mail.example.com
fi
HOSTNAME_VALUE=$(prompt_value "Hostname FQDN del server mail" "$DEFAULT_HOSTNAME" "$HOSTNAME_VALUE")
HOSTNAME_VALUE=$(ensure_fqdn "$HOSTNAME_VALUE")
SSL_MODE=$(prompt_value "Modalità SSL (plain|selfsigned|letsencrypt)" "selfsigned" "$SSL_MODE")
case "$SSL_MODE" in
  plain|selfsigned|letsencrypt) ;;
  *)
    log "Modalità SSL non valida: $SSL_MODE"
    exit 1
    ;;
esac
if [[ "$SSL_MODE" == "letsencrypt" ]]; then
  LE_EMAIL=$(prompt_value "Email per Let's Encrypt" "postmaster@${HOSTNAME_VALUE#*.}" "$LE_EMAIL")
fi
PUBLIC_IP=$(prompt_value "IPv4 pubblico (opzionale)" "" "$PUBLIC_IP")
PUBLIC_IPV6=$(prompt_value "IPv6 pubblico (opzionale)" "" "$PUBLIC_IPV6")
DKIM_SELECTOR=$(prompt_value "DKIM selector" "default" "$DKIM_SELECTOR")
validate_ip_if_set "$PUBLIC_IP" 4
validate_ip_if_set "$PUBLIC_IPV6" 6
if [[ ! "$DKIM_SELECTOR" =~ ^[A-Za-z0-9_-]{1,63}$ ]]; then
  log "DKIM selector non valido: $DKIM_SELECTOR"
  exit 1
fi
ENABLE_API=$(prompt_bool "Attivare API di controllo" "yes" "$ENABLE_API")
ENABLE_NGINX=$(prompt_bool "Attivare Nginx per HTTPS / MTA-STS" "yes" "$ENABLE_NGINX")
ENABLE_RSPAMD=$(prompt_bool "Attivare Rspamd anti-spam" "yes" "$ENABLE_RSPAMD")
ENABLE_SRS=$(prompt_bool "Attivare SRS per forwarding" "yes" "$ENABLE_SRS")
ENABLE_WEB_PANEL=$(prompt_bool "Attivare piccolo pannello web HTTPS" "no" "$ENABLE_WEB_PANEL")

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
fi

if [[ "$SSL_MODE" == "letsencrypt" && "$ENABLE_NGINX" == "yes" ]]; then
  ENABLE_MTA_STS=$(prompt_bool "Attivare MTA-STS" "yes" "$ENABLE_MTA_STS")
else
  ENABLE_MTA_STS=no
fi
ENABLE_TLS_RPT=$(prompt_bool "Attivare TLS-RPT" "$ENABLE_MTA_STS" "$ENABLE_TLS_RPT")
ENABLE_BACKUP_TIMER=$(prompt_bool "Attivare timer backup" "yes" "$ENABLE_BACKUP_TIMER")
ENABLE_DELIVERABILITY_TIMER=$(prompt_bool "Attivare timer report deliverability" "no" "$ENABLE_DELIVERABILITY_TIMER")
if [[ "$ENABLE_NGINX" != "yes" ]]; then
  ENABLE_MTA_STS=no
fi

log "Riepilogo installazione"
log "- path: $INSTALL_PATH"
log "- hostname: $HOSTNAME_VALUE"
log "- ssl: $SSL_MODE"
log "- api: $ENABLE_API"
log "- nginx: $ENABLE_NGINX"
log "- rspamd: $ENABLE_RSPAMD"
log "- srs: $ENABLE_SRS"
log "- mta-sts: $ENABLE_MTA_STS"
log "- tls-rpt: $ENABLE_TLS_RPT"
log "- backup-timer: $ENABLE_BACKUP_TIMER"
log "- deliverability-timer: $ENABLE_DELIVERABILITY_TIMER"
log "- web-panel: $ENABLE_WEB_PANEL"

if [[ "$NON_INTERACTIVE" != "yes" ]]; then
  confirm=$(prompt_bool "Procedere con l'installazione" "yes" "yes")
  if [[ "$confirm" != "yes" ]]; then
    log "Installazione annullata."
    exit 1
  fi
fi

if [[ "$DRY_RUN" == "yes" ]]; then
  log "Dry-run: nessuna modifica eseguita."
  exit 0
fi

sync_package

export LIMRISTEM_MAIL_BASE_DIR=$INSTALL_PATH
export LIMRISTEM_MAIL_HOSTNAME=$HOSTNAME_VALUE
export LIMRISTEM_MAIL_SSL_MODE=$SSL_MODE
export LIMRISTEM_MAIL_LE_EMAIL=$LE_EMAIL
export LIMRISTEM_MAIL_PUBLIC_IP=$PUBLIC_IP
export LIMRISTEM_MAIL_PUBLIC_IPV6=$PUBLIC_IPV6
export LIMRISTEM_MAIL_DKIM_SELECTOR=$DKIM_SELECTOR
export LIMRISTEM_MAIL_ENABLE_API=$ENABLE_API
export LIMRISTEM_MAIL_ENABLE_NGINX=$ENABLE_NGINX
export LIMRISTEM_MAIL_ENABLE_RSPAMD=$ENABLE_RSPAMD
export LIMRISTEM_MAIL_ENABLE_SRS=$ENABLE_SRS
export LIMRISTEM_MAIL_ENABLE_MTA_STS=$ENABLE_MTA_STS
export LIMRISTEM_MAIL_ENABLE_TLS_RPT=$ENABLE_TLS_RPT
export LIMRISTEM_MAIL_ENABLE_BACKUP_TIMER=$ENABLE_BACKUP_TIMER
export LIMRISTEM_MAIL_ENABLE_DELIVERABILITY_TIMER=$ENABLE_DELIVERABILITY_TIMER
export LIMRISTEM_MAIL_ENABLE_WEB_PANEL=$ENABLE_WEB_PANEL
export LIMRISTEM_MAIL_UPDATE_MODE=$UPDATE_MODE
export LIMRISTEM_MAIL_OVERWRITE_MODE=$OVERWRITE_MODE
export LIMRISTEM_MAIL_LOG_FILE=/var/log/limristem-mail-install.log

cd "$INSTALL_PATH"
exec bash ./bin/install.sh
