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

APP_NAME="${APP_NAME:-limristem-monitor}"
SERVICE_NAME="${SERVICE_NAME:-limristem-monitor}"
INSTALL_ROOT="${INSTALL_ROOT:-/opt/limristem-monitor}"
CONFIG_FILE="${CONFIG_FILE:-${INSTALL_ROOT}/config.json}"
EXAMPLE_CONFIG_FILE="${EXAMPLE_CONFIG_FILE:-${INSTALL_ROOT}/config.json.example}"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
CERTS_DIR="${CERTS_DIR:-${INSTALL_ROOT}/certs}"
HISTORY_DIR="${HISTORY_DIR:-${INSTALL_ROOT}/history}"
HOSTS_HISTORY_DIR="${HOSTS_HISTORY_DIR:-${HISTORY_DIR}/hosts}"
SITES_HISTORY_DIR="${SITES_HISTORY_DIR:-${HISTORY_DIR}/sites}"
SYSTEM_USER="${SYSTEM_USER:-limristem-monitor}"
SYSTEM_GROUP="${SYSTEM_GROUP:-limristem-monitor}"
PYTHON_BIN="${PYTHON_BIN:-$(command -v python3 || true)}"
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NOLOGIN_BIN="$(command -v nologin || true)"

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

warn() {
  printf '[WARN] %s\n' "$*" >&2
}

die() {
  printf '[ERROR] %s\n' "$*" >&2
  exit 1
}

require_root() {
  if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
    die "Esegui questo script come root."
  fi
}

require_command() {
  command -v "$1" >/dev/null 2>&1 || die "Comando richiesto non trovato: $1"
}

create_system_account() {
  if ! getent group "$SYSTEM_GROUP" >/dev/null 2>&1; then
    log "Creo il gruppo di sistema ${SYSTEM_GROUP}"
    groupadd --system "$SYSTEM_GROUP"
  fi

  if ! id -u "$SYSTEM_USER" >/dev/null 2>&1; then
    log "Creo l'utente di sistema ${SYSTEM_USER}"
    useradd \
      --system \
      --gid "$SYSTEM_GROUP" \
      --home-dir "$INSTALL_ROOT" \
      --no-create-home \
      --shell "${NOLOGIN_BIN:-/usr/sbin/nologin}" \
      "$SYSTEM_USER"
  fi
}

validate_source_files() {
  [[ -n "$PYTHON_BIN" ]] || die "python3 non trovato."
  [[ -f "${SOURCE_DIR}/monitor.py" ]] || die "monitor.py non trovato in ${SOURCE_DIR}"
  [[ -f "${SOURCE_DIR}/config.json" ]] || die "config.json non trovato in ${SOURCE_DIR}"
  "$PYTHON_BIN" -m py_compile "${SOURCE_DIR}/monitor.py"
}

create_directories() {
  install -d -m 0755 "$INSTALL_ROOT"
  install -d -m 0750 -o root -g "$SYSTEM_GROUP" "$CERTS_DIR"
  install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$HISTORY_DIR"
  install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$HOSTS_HISTORY_DIR"
  install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$SITES_HISTORY_DIR"
}

install_application_files() {
  log "Installo monitor.py in ${INSTALL_ROOT}"
  install -m 0755 "${SOURCE_DIR}/monitor.py" "${INSTALL_ROOT}/monitor.py"

  log "Aggiorno il file di configurazione di esempio"
  install -m 0640 -o root -g "$SYSTEM_GROUP" "${SOURCE_DIR}/config.json" "$EXAMPLE_CONFIG_FILE"

  if [[ ! -f "$CONFIG_FILE" ]]; then
    log "Creo il file di configurazione attivo in ${CONFIG_FILE}"
    install -m 0640 -o root -g "$SYSTEM_GROUP" "${SOURCE_DIR}/config.json" "$CONFIG_FILE"
  else
    log "Configurazione esistente trovata in ${CONFIG_FILE}: la lascio invariata"
  fi
}

generate_default_certificate() {
  if [[ -f "${CERTS_DIR}/master.crt" && -f "${CERTS_DIR}/master.key" ]]; then
    log "Certificato master gia presente in ${CERTS_DIR}"
    return
  fi

  if ! command -v openssl >/dev/null 2>&1; then
    warn "openssl non disponibile: HTTPS restera disabilitato finche non installerai manualmente master.crt e master.key"
    return
  fi

  log "Genero un certificato master self-signed per il primo avvio"
  openssl req \
    -x509 \
    -nodes \
    -newkey rsa:2048 \
    -keyout "${CERTS_DIR}/master.key" \
    -out "${CERTS_DIR}/master.crt" \
    -days 3650 \
    -subj "/CN=limristem-monitor.local" >/dev/null 2>&1

  chown root:"$SYSTEM_GROUP" "${CERTS_DIR}/master.crt" "${CERTS_DIR}/master.key"
  chmod 0640 "${CERTS_DIR}/master.crt" "${CERTS_DIR}/master.key"
}

sync_certificate_permissions() {
  local certificate_file

  chown root:"$SYSTEM_GROUP" "$CERTS_DIR"
  chmod 0750 "$CERTS_DIR"

  shopt -s nullglob
  for certificate_file in "$CERTS_DIR"/*.crt "$CERTS_DIR"/*.key "$CERTS_DIR"/*.pem; do
    chown root:"$SYSTEM_GROUP" "$certificate_file"
    chmod 0640 "$certificate_file"
  done
  shopt -u nullglob
}

write_systemd_service() {
  log "Scrivo l'unita systemd ${SERVICE_FILE}"
  cat >"$SERVICE_FILE" <<EOF
[Unit]
Description=Limristem Monitor monitoring service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${SYSTEM_USER}
Group=${SYSTEM_GROUP}
WorkingDirectory=${INSTALL_ROOT}
Environment=PYTHONUNBUFFERED=1
ExecStart=${PYTHON_BIN} ${INSTALL_ROOT}/monitor.py
Restart=always
RestartSec=5
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=${HISTORY_DIR}
UMask=0027

[Install]
WantedBy=multi-user.target
EOF
  chmod 0644 "$SERVICE_FILE"
}

enable_and_start_service() {
  log "Ricarico systemd"
  systemctl daemon-reload

  log "Abilito il servizio ${SERVICE_NAME}"
  systemctl enable "${SERVICE_NAME}.service" >/dev/null

  log "Avvio o riavvio il servizio ${SERVICE_NAME}"
  systemctl restart "${SERVICE_NAME}.service"
}

print_summary() {
  cat <<EOF

Installazione completata.

Percorsi principali:
  Codice:            ${INSTALL_ROOT}/monitor.py
  Config attivo:     ${CONFIG_FILE}
  Config di esempio: ${EXAMPLE_CONFIG_FILE}
  Certificati:       ${CERTS_DIR}
  Storico runtime:   ${HISTORY_DIR}

Comandi utili:
  systemctl status ${SERVICE_NAME}
  systemctl start ${SERVICE_NAME}
  systemctl stop ${SERVICE_NAME}
  systemctl restart ${SERVICE_NAME}
  journalctl -u ${SERVICE_NAME} -f

Nota:
  - Il servizio parte automaticamente al boot.
  - In caso di crash, systemd lo riavvia automaticamente.
  - Se modifichi ${CONFIG_FILE}, riavvia il servizio.
EOF
}

main() {
  require_root
  require_command systemctl
  require_command install
  require_command getent
  require_command groupadd
  require_command python3
  require_command useradd

  validate_source_files
  create_system_account
  create_directories
  install_application_files
  generate_default_certificate
  sync_certificate_permissions
  write_systemd_service
  enable_and_start_service
  print_summary
}

main "$@"
