#!/bin/bash

# Gerenciador SQL Anywhere 17 para Linux
# Último ajuste por Maicon Antunes Luiz (maicon.luiz@thomsonreuters.com)
# Versão 8.0 — Validação de Cache + Remoção de Bancos
#
# Changelog v8.0:
#  [NOVO] Função para remover banco de dados configurado (config + serviço + firewall)
#  [NOVO] Validação completa de cache ao adicionar banco:
#         - Bloqueia se cache > RAM total
#         - Bloqueia se sobra < 512 MB para o SO
#         - Avisa se cache > memória disponível
#         - Avisa se sobra < reserva para o SO (2 GB server / 4 GB desktop)
#         - Avisa se cache < 70% do tamanho do banco
#  [NOVO] Funções auxiliares reutilizáveis para cálculos de memória
#
# Changelog v7.0:
#  [CRÍTICO] Symlinks da lib64 são verificados e recriados após extração
#  [CRÍTICO] Erros de extração do tar são detectados e exibidos ao usuário
#  [CRÍTICO] Removido drop_caches; memória calculada via MemAvailable
#  [CRÍTICO] Variáveis de config são limpas (unset) antes de cada source
#  [CORREÇÃO] Arrays usam mapfile -t para evitar word splitting e globbing
#  [CORREÇÃO] SERVER_NAME validado com regex (somente caracteres seguros)
#  [CORREÇÃO] chmod seletivo — apenas bin64/bin64s recebem +x
#  [CORREÇÃO] Verificação pós-instalação do dbsrv17 com detecção de libs ausentes
#  [CORREÇÃO] Resultado do 7z no backup é verificado; falha não é silenciada
#  [CORREÇÃO] Busca de banco oferece caminho manual primeiro, depois pesquisa inteligente
#  [CORREÇÃO] Desinstalação remove regras de firewall configuradas
#  [MELHORIA] Gerenciamento de logs — arquivamento mensal com 7z em pasta de histórico
#  [MELHORIA] wget --show-progress com detecção de compatibilidade
#  [MELHORIA] BACKUP_DIR configurável por banco no .conf
#  [MELHORIA] Textos de interface reescritos para clareza
#  [MELHORIA] Verificação básica de integridade pós-download (tamanho do arquivo)
#  [MELHORIA] Dependência libnsl incluída para Fedora/RHEL 9+
#  [MELHORIA] Verificação de hardware e ambiente exibida no menu (RAM, CPU, disco, rede)

# ==============================================================================
#  MENSAGENS, VALIDAÇÃO E VARIÁVEIS GLOBAIS
# ==============================================================================

msg_info()    { tput setaf 2 2>/dev/null; echo "  [INFO] $1"; tput sgr0 2>/dev/null; }
msg_warning() { tput setaf 3 2>/dev/null; echo "  [AVISO] $1"; tput sgr0 2>/dev/null; }
msg_error()   { tput setaf 1 2>/dev/null; echo "  [ERRO] $1"; tput sgr0 2>/dev/null; }
msg_success() { tput setaf 2 2>/dev/null; echo "  [OK] $1"; tput sgr0 2>/dev/null; }

if [ "$EUID" -ne 0 ]; then
    msg_error "Este script precisa ser executado como root (sudo)."
    exit 1
fi

BASE_DIR="/opt/sqlanywhere17"
CONF_DIR="$BASE_DIR/conf.d"
LOG_DIR="$BASE_DIR/log"
LOG_HIST_DIR="$LOG_DIR/historico"
RUNNER_SCRIPT="/usr/local/sbin/sqlanywhere-runner"
TEMPLATE_SERVICE="/etc/systemd/system/sqlanywhere@.service"
DOWNLOAD_URL='https://download.dominiosistemas.com.br/instalacao/diversos/sybase17_linux_64/ASA-1700-7312-Linux-64.tar.gz'
COMPRESSED_FILE='ASA-1700-7312-Linux-64.tar.gz'

# ==============================================================================
#  FUNÇÕES UTILITÁRIAS
# ==============================================================================

versaoSistema() {
    echo
    msg_info "Versões homologadas do SQL Anywhere 17:"
    echo "  ┌───────────────────────────────────────────────────┐"
    echo "  │  Mínimo: Kernel 2.6.18+ / glibc 2.5+             │"
    echo "  │  Recomendado: Kernel 3.2.0+ / glibc 2.15+        │"
    echo "  └───────────────────────────────────────────────────┘"
    echo
    if command -v ldd &>/dev/null; then
        local glibc
        glibc=$(ldd --version 2>/dev/null | awk '/ldd/{print $NF}')
        msg_info "Este servidor: glibc $glibc | Kernel $(uname -r) | Arch $(uname -m)"
    else
        msg_warning "Não foi possível detectar a versão do glibc."
    fi
    echo
}

# --- Helpers de memória (reutilizáveis) ---

obterRamTotalMB() {
    local total_kb
    total_kb=$(grep '^MemTotal:' /proc/meminfo | awk '{print $2}')
    echo $(( total_kb / 1024 ))
}

obterMemDisponivelMB() {
    local avail_kb
    avail_kb=$(grep '^MemAvailable:' /proc/meminfo 2>/dev/null | awk '{print $2}')
    if [ -z "$avail_kb" ]; then
        local mem_free buffers cached
        mem_free=$(grep '^MemFree:' /proc/meminfo | awk '{print $2}')
        buffers=$(grep '^Buffers:' /proc/meminfo | awk '{print $2}')
        cached=$(grep '^Cached:' /proc/meminfo | awk '{print $2}')
        avail_kb=$(( ${mem_free:-0} + ${buffers:-0} + ${cached:-0} ))
    fi
    echo $(( avail_kb / 1024 ))
}

obterCacheTotalBancos() {
    local total=0
    for f in "$CONF_DIR"/*.conf; do
        [ -f "$f" ] || continue
        local c
        c=$(grep '^CACHE_MB=' "$f" 2>/dev/null | head -1 | cut -d'"' -f2)
        [[ "${c:-0}" =~ ^[0-9]+$ ]] && total=$(( total + c ))
    done
    echo "$total"
}

temAmbienteGrafico() {
    pidof Xorg &>/dev/null || pidof Xwayland &>/dev/null || \
    pidof gnome-shell &>/dev/null || pidof plasmashell &>/dev/null || \
    pidof xfce4-session &>/dev/null
}

verificarMemoriaLivre() {
    local mem_avail
    mem_avail=$(grep '^MemAvailable:' /proc/meminfo 2>/dev/null | awk '{print $2}')

    # Fallback para kernels antigos sem MemAvailable
    if [ -z "$mem_avail" ]; then
        local mem_free buffers cached
        mem_free=$(grep '^MemFree:' /proc/meminfo | awk '{print $2}')
        buffers=$(grep '^Buffers:' /proc/meminfo | awk '{print $2}')
        cached=$(grep '^Cached:' /proc/meminfo | awk '{print $2}')
        mem_avail=$(( ${mem_free:-0} + ${buffers:-0} + ${cached:-0} ))
    fi

    if [ -z "$mem_avail" ] || [ "$mem_avail" -eq 0 ] 2>/dev/null; then
        msg_error "Não foi possível ler informações de memória."
        return 1
    fi

    local mem_rec_mb=$(( (mem_avail * 80 / 100) / 1024 ))
    local total_mb=$(obterRamTotalMB)
    local cache_alocado=$(obterCacheTotalBancos)

    echo
    free -mh 2>/dev/null | grep -E 'Mem|total' || free -m 2>/dev/null | head -2
    echo
    echo "  ┌─────────────────────────────────────────────────┐"
    printf "  │ RAM Total:           %-26s│\n" "${total_mb} MB"
    printf "  │ Disponível p/ uso:   %-26s│\n" "~${mem_rec_mb} MB (80% de MemAvailable)"
    printf "  │ Cache já alocado:    %-26s│\n" "${cache_alocado} MB (outros bancos)"
    echo "  └─────────────────────────────────────────────────┘"
    echo
}

porta_livre() {
    local s="${1:-2638}"
    local p
    for ((p = s; p < s + 100; p++)); do
        if ! ss -lnt 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"; then
            echo "$p"
            return 0
        fi
    done
    return 1
}

listarBancos() {
    [ -d "$CONF_DIR" ] || return 1
    local confs
    confs=$(ls "$CONF_DIR"/*.conf 2>/dev/null)
    [ -z "$confs" ] && return 1
    echo "$confs" | xargs -n1 basename | sed 's/\.conf$//'
}

detectar_wget_opts() {
    if wget --help 2>&1 | grep -q '\-\-show-progress'; then
        echo "-q --show-progress"
    else
        echo ""
    fi
}

# --- Firewall: liberar e remover portas ---

liberar_ufw() {
    msg_info "ufw: Liberando porta $1/tcp e $1/udp..."
    ufw allow "$1"/tcp >/dev/null 2>&1
    ufw allow "$1"/udp >/dev/null 2>&1
    ufw reload >/dev/null 2>&1
}

remover_ufw() {
    msg_info "ufw: Removendo porta $1/tcp e $1/udp..."
    ufw delete allow "$1"/tcp >/dev/null 2>&1
    ufw delete allow "$1"/udp >/dev/null 2>&1
    ufw reload >/dev/null 2>&1
}

liberar_firewalld() {
    msg_info "firewalld: Liberando porta $1/tcp e $1/udp..."
    firewall-cmd --permanent --add-port="$1"/tcp >/dev/null 2>&1
    firewall-cmd --permanent --add-port="$1"/udp >/dev/null 2>&1
    firewall-cmd --reload >/dev/null 2>&1
}

remover_firewalld() {
    msg_info "firewalld: Removendo porta $1/tcp e $1/udp..."
    firewall-cmd --permanent --remove-port="$1"/tcp >/dev/null 2>&1
    firewall-cmd --permanent --remove-port="$1"/udp >/dev/null 2>&1
    firewall-cmd --reload >/dev/null 2>&1
}

configurar_firewall() {
    local PORT="${1:?Porta não informada}"
    if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
        msg_error "Porta inválida: $PORT"
        return 1
    fi
    if command -v ufw &>/dev/null && systemctl is-active --quiet ufw 2>/dev/null; then
        liberar_ufw "$PORT"
    elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld 2>/dev/null; then
        liberar_firewalld "$PORT"
    else
        msg_warning "Nenhum firewall ativo detectado (ufw/firewalld). Porta $PORT não liberada automaticamente."
    fi
}

remover_firewall() {
    local PORT="${1:?Porta não informada}"
    [[ "$PORT" =~ ^[0-9]+$ ]] || return 1
    if command -v ufw &>/dev/null && systemctl is-active --quiet ufw 2>/dev/null; then
        remover_ufw "$PORT"
    elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld 2>/dev/null; then
        remover_firewalld "$PORT"
    fi
}

# ==============================================================================
#  VERIFICAÇÃO DE HARDWARE E AMBIENTE (exibida no cabeçalho do menu)
# ==============================================================================
# Fontes: Thomson Reuters — Requisitos de Hardware e Dicas de Performance
#
# RAM recomendada por tamanho de banco:
#   0–4 GB  →   8 GB  |  5–9 GB  →  16 GB  |  10–24 GB →  32 GB
#   25–49 GB → 64 GB  |  50+ GB  → 128 GB
#
# Cache: mínimo 70% do tamanho do banco; reservar RAM para o SO
# CPU: governor em "performance"; frequência operando no máximo
# Rede: ≥ 1 Gbps  |  Disco: SSD; capacidade ≥ 3× banco
# Servidor: físico, dedicado, reiniciado a cada 30 dias

verificarHardware() {
    local bancos
    mapfile -t bancos < <(listarBancos 2>/dev/null)
    [ ${#bancos[@]} -eq 0 ] && return

    # Verifica se existe ao menos um banco ativo
    local algum_ativo=0
    for bid in "${bancos[@]}"; do
        systemctl is-active --quiet "sqlanywhere@$bid.service" 2>/dev/null && { algum_ativo=1; break; }
    done
    [ $algum_ativo -eq 0 ] && return

    local avisos=()

    # -------------------------------------------------------------------------
    # RAM total do servidor
    # -------------------------------------------------------------------------
    local total_ram_kb total_ram_mb total_ram_gb ram_display
    total_ram_kb=$(grep '^MemTotal:' /proc/meminfo | awk '{print $2}')
    total_ram_mb=$(( total_ram_kb / 1024 ))
    total_ram_gb=$(( total_ram_mb / 1024 ))

    # Exibição legível (evitar "~0 GB" quando < 1 GB)
    if [ "$total_ram_gb" -gt 0 ]; then
        ram_display="~${total_ram_gb} GB"
    else
        ram_display="${total_ram_mb} MB"
    fi

    # -------------------------------------------------------------------------
    # 1. Virtualização
    # -------------------------------------------------------------------------
    if command -v systemd-detect-virt &>/dev/null; then
        local virt
        virt=$(systemd-detect-virt 2>/dev/null)
        if [ -n "$virt" ] && [ "$virt" != "none" ]; then
            avisos+=("Servidor virtualizado ($virt). Recomenda-se servidor físico dedicado.")

            # Detectar memória dinâmica do Hyper-V
            if [ "$virt" = "microsoft" ]; then
                local hyperv_dynamic=0
                if [ -f /sys/devices/system/memory/auto_online_blocks ]; then
                    hyperv_dynamic=1
                elif dmesg 2>/dev/null | grep -qi "Hyper-V Dynamic Memory"; then
                    hyperv_dynamic=1
                fi

                if [ "$hyperv_dynamic" -eq 1 ]; then
                    local max_mem_info=""
                    if command -v dmidecode &>/dev/null; then
                        max_mem_info=$(dmidecode -t 16 2>/dev/null | grep "Maximum Capacity" | awk '{print $3, $4}')
                    fi
                    if [ -n "$max_mem_info" ]; then
                        avisos+=("Hyper-V com memória dinâmica. MemTotal atual: ${ram_display}. Capacidade máxima: ${max_mem_info}.")
                    else
                        avisos+=("Hyper-V com memória dinâmica. MemTotal atual: ${ram_display} (pode não refletir o total disponível).")
                    fi
                    avisos+=("Com memória dinâmica, as verificações de RAM abaixo podem não ser precisas.")
                fi
            fi
        fi
    fi

    # -------------------------------------------------------------------------
    # 2. Número de sockets (limitação SAP: máximo 2)
    # -------------------------------------------------------------------------
    if command -v lscpu &>/dev/null; then
        local sockets
        sockets=$(lscpu 2>/dev/null | grep -i '^Socket(s):' | awk '{print $2}')
        if [ -n "$sockets" ] && [ "$sockets" -gt 2 ] 2>/dev/null; then
            avisos+=("Detectados $sockets sockets de CPU. A licença SAP suporta no máximo 2 sockets.")
        fi
    fi

    # -------------------------------------------------------------------------
    # 3. CPU governor (equivalente ao Plano de Energia do Windows)
    # -------------------------------------------------------------------------
    local gov_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
    if [ -f "$gov_file" ]; then
        local governor
        governor=$(cat "$gov_file" 2>/dev/null)
        if [ -n "$governor" ] && [ "$governor" != "performance" ]; then
            avisos+=("CPU governor: '$governor'. Recomenda-se 'performance' para melhor desempenho.")
        fi
    fi

    # -------------------------------------------------------------------------
    # 4. Frequência do CPU abaixo do máximo
    # -------------------------------------------------------------------------
    local freq_cur_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
    local freq_max_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
    if [ -f "$freq_cur_file" ] && [ -f "$freq_max_file" ]; then
        local freq_cur freq_max pct
        freq_cur=$(cat "$freq_cur_file" 2>/dev/null)
        freq_max=$(cat "$freq_max_file" 2>/dev/null)
        if [ -n "$freq_cur" ] && [ -n "$freq_max" ] && [ "$freq_max" -gt 0 ] 2>/dev/null; then
            pct=$(( freq_cur * 100 / freq_max ))
            if [ "$pct" -lt 70 ]; then
                local cur_mhz=$(( freq_cur / 1000 ))
                local max_mhz=$(( freq_max / 1000 ))
                avisos+=("CPU operando a ${cur_mhz} MHz (${pct}% do máximo ${max_mhz} MHz). Verifique governor e BIOS.")
            fi
        fi
    fi

    # -------------------------------------------------------------------------
    # 5. Uptime > 30 dias
    # -------------------------------------------------------------------------
    local uptime_days
    uptime_days=$(awk '{print int($1/86400)}' /proc/uptime 2>/dev/null)
    if [ -n "$uptime_days" ] && [ "$uptime_days" -gt 30 ]; then
        avisos+=("Servidor ligado há ${uptime_days} dias sem reiniciar. Recomenda-se reiniciar a cada 30 dias.")
    fi

    # -------------------------------------------------------------------------
    # 6. Velocidade da interface de rede
    # -------------------------------------------------------------------------
    local default_iface
    default_iface=$(ip route 2>/dev/null | awk '/default/{print $5; exit}')
    if [ -n "$default_iface" ]; then
        local speed_file="/sys/class/net/$default_iface/speed"
        if [ -f "$speed_file" ]; then
            local net_speed
            net_speed=$(cat "$speed_file" 2>/dev/null)
            if [ -n "$net_speed" ] && [ "$net_speed" -gt 0 ] && [ "$net_speed" -lt 1000 ] 2>/dev/null; then
                avisos+=("Rede ($default_iface): ${net_speed} Mbps. Recomenda-se 1000 Mbps (Gigabit).")
            fi
        fi
        # Detectar Wi-Fi
        if [[ "$default_iface" =~ ^(wl|wlan|wifi) ]]; then
            avisos+=("Interface de rede principal é Wi-Fi ($default_iface). Recomenda-se rede cabeada.")
        fi
    fi

    # -------------------------------------------------------------------------
    # 7. Servidor dedicado — detectar processos concorrentes pesados
    # -------------------------------------------------------------------------
    local proc_concorrentes=""
    local servicos_pesados=("mysqld" "mariadbd" "postgres" "mongod" "docker" "containerd"
                            "apache2" "httpd" "nginx" "smbd" "nmbd" "xrdp" "java")
    for proc in "${servicos_pesados[@]}"; do
        if pgrep -x "$proc" &>/dev/null; then
            proc_concorrentes+="${proc}, "
        fi
    done
    if [ -n "$proc_concorrentes" ]; then
        proc_concorrentes="${proc_concorrentes%, }"
        avisos+=("Processos concorrentes detectados: $proc_concorrentes. Servidor deve ser dedicado ao banco.")
    fi

    # -------------------------------------------------------------------------
    # 8. Verificações POR BANCO ativo
    # -------------------------------------------------------------------------
    local cache_total=0
    for bid in "${bancos[@]}"; do
        systemctl is-active --quiet "sqlanywhere@$bid.service" 2>/dev/null || continue

        local conf="$CONF_DIR/$bid.conf"
        [ -f "$conf" ] || continue

        unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
        source "$conf"
        [ -f "$DB_PATH" ] || continue

        # Tamanho do banco em MB e GB (para exibição)
        local db_bytes db_mb db_display
        db_bytes=$(stat -c%s "$DB_PATH" 2>/dev/null)
        [ -z "$db_bytes" ] && continue
        db_mb=$(( db_bytes / 1024 / 1024 ))
        if [ "$db_mb" -ge 1024 ]; then
            db_display="$(( db_mb / 1024 )) GB"
        else
            db_display="${db_mb} MB"
        fi

        cache_total=$(( cache_total + CACHE_MB ))

        # 8a. RAM recomendada pela tabela Thomson Reuters
        local ram_rec_gb=8
        if   [ "$db_mb" -gt 51200 ]; then ram_rec_gb=128
        elif [ "$db_mb" -gt 25600 ]; then ram_rec_gb=64
        elif [ "$db_mb" -gt 10240 ]; then ram_rec_gb=32
        elif [ "$db_mb" -gt 5120  ]; then ram_rec_gb=16
        fi

        local ram_rec_mb=$(( ram_rec_gb * 1024 ))
        if [ "$total_ram_mb" -lt "$ram_rec_mb" ]; then
            avisos+=("'$bid' (banco: ${db_display}): RAM recomendada ${ram_rec_gb} GB, instalada ${ram_display}.")
        fi

        # 8b. RAM total menor que o próprio banco
        if [ "$total_ram_mb" -le "$db_mb" ]; then
            avisos+=("'$bid': RAM total (${ram_display}) não é maior que o banco (${db_display}). Mínimo absoluto não atendido.")
        fi

        # 8c. Cache < 70% do tamanho do banco
        if [ "$db_mb" -gt 0 ]; then
            local cache_pct=$(( CACHE_MB * 100 / db_mb ))
            if [ "$cache_pct" -lt 70 ]; then
                local cache_ideal=$(( db_mb * 70 / 100 ))
                avisos+=("'$bid' (banco: ${db_display}): Cache ${CACHE_MB} MB = ${cache_pct}% do banco. Mínimo recomendado: 70% (${cache_ideal} MB).")
            fi
        fi

        # 8d. Disco: SSD recomendado
        local db_dir mount_dev dev_base
        db_dir=$(dirname "$DB_PATH")
        mount_dev=$(df "$db_dir" 2>/dev/null | awk 'NR==2{print $1}')
        if [ -n "$mount_dev" ] && [ -b "$mount_dev" ]; then
            dev_base=$(lsblk -no PKNAME "$mount_dev" 2>/dev/null | head -1)
            if [ -n "$dev_base" ] && [ -f "/sys/block/$dev_base/queue/rotational" ]; then
                local rotational
                rotational=$(cat "/sys/block/$dev_base/queue/rotational" 2>/dev/null)
                if [ "$rotational" = "1" ]; then
                    avisos+=("'$bid': Disco HDD detectado. Recomenda-se SSD para o banco de dados.")
                fi
            fi
        fi

        # 8e. Disco: capacidade total >= 3× banco
        local disk_total_kb
        disk_total_kb=$(df -k "$db_dir" 2>/dev/null | awk 'NR==2{print $2}')
        if [ -n "$disk_total_kb" ]; then
            local disk_total_mb=$(( disk_total_kb / 1024 ))
            local needed_mb=$(( db_mb * 3 ))
            if [ "$disk_total_mb" -lt "$needed_mb" ]; then
                avisos+=("'$bid': Disco total ($(( disk_total_mb / 1024 )) GB) menor que 3x o banco ($(( needed_mb / 1024 )) GB).")
            fi
        fi

        # 8f. Disco: espaço livre < tamanho do banco
        local disk_avail_kb
        disk_avail_kb=$(df -k "$db_dir" 2>/dev/null | awk 'NR==2{print $4}')
        if [ -n "$disk_avail_kb" ]; then
            local disk_avail_mb=$(( disk_avail_kb / 1024 ))
            if [ "$disk_avail_mb" -lt "$db_mb" ]; then
                avisos+=("'$bid': Espaço livre ($(( disk_avail_mb / 1024 )) GB) menor que o banco (${db_display}). Risco para cópias de segurança.")
            fi
        fi
    done

    # -------------------------------------------------------------------------
    # 9. Reservar RAM para o SO
    # -------------------------------------------------------------------------
    if [ "$cache_total" -gt 0 ]; then
        local ram_reserva_so=2048
        temAmbienteGrafico && ram_reserva_so=4096

        local sobra_so=$(( total_ram_mb - cache_total ))
        if [ "$sobra_so" -lt "$ram_reserva_so" ]; then
            local tipo_so="server"
            [ "$ram_reserva_so" -eq 4096 ] && tipo_so="desktop"
            avisos+=("Cache total dos bancos: ${cache_total} MB. Sobram ${sobra_so} MB para o SO (mínimo para Linux $tipo_so: ${ram_reserva_so} MB).")
        fi
    fi

    # -------------------------------------------------------------------------
    # EXIBIÇÃO
    # -------------------------------------------------------------------------
    echo
    if [ ${#avisos[@]} -gt 0 ]; then
        tput setaf 3 2>/dev/null
        echo "  ┌─ Verificação de Hardware e Ambiente ────────────────────────────────┐"
        for a in "${avisos[@]}"; do
            echo "  │  ⚠ $a" | fold -s -w 74 | while IFS= read -r line; do
                if [[ "$line" == "  │"* ]]; then
                    echo "$line"
                else
                    echo "  │    $line"
                fi
            done
        done
        echo "  └────────────────────────────────────────────────────────────────────┘"
        tput sgr0 2>/dev/null
    else
        tput setaf 2 2>/dev/null
        echo "  ┌─ Verificação de Hardware e Ambiente ────────────────────────────────┐"
        echo "  │  ✓ Hardware e ambiente adequados para os bancos em execução.        │"
        echo "  └────────────────────────────────────────────────────────────────────┘"
        tput sgr0 2>/dev/null
    fi
}

# ==============================================================================
#  FUNÇÕES PRINCIPAIS
# ==============================================================================

InstalacaoBase() {
    if [ -d "$BASE_DIR" ] && [ -f "$BASE_DIR/bin64/dbsrv17" ]; then
        msg_success "A base do SQL Anywhere já está instalada em $BASE_DIR."
        sleep 2
        return
    fi

    clear
    versaoSistema
    mkdir -p "$BASE_DIR" "$CONF_DIR" "$LOG_DIR" "$LOG_HIST_DIR"

    # --- Dependências ---
    msg_info "Instalando dependências do sistema (wget, tar, libnsl, less, p7zip)..."
    if command -v dnf &>/dev/null; then
        dnf install -y wget tar libnsl p7zip less >/dev/null 2>&1
    elif command -v yum &>/dev/null; then
        yum install -y wget tar libnsl p7zip less >/dev/null 2>&1
    elif command -v apt-get &>/dev/null; then
        apt-get update -qq >/dev/null 2>&1
        apt-get install -y wget tar libnsl2 less p7zip-full >/dev/null 2>&1
    elif command -v zypper &>/dev/null; then
        zypper install -y wget tar libnsl1 less p7zip >/dev/null 2>&1
    else
        msg_warning "Gerenciador de pacotes não reconhecido. Instale manualmente: wget, tar, libnsl, less, p7zip."
    fi

    # --- Obter o pacote ---
    if [ -f "/tmp/$COMPRESSED_FILE" ]; then
        msg_info "Arquivo encontrado em /tmp/$COMPRESSED_FILE. Usando cópia local."
        cp "/tmp/$COMPRESSED_FILE" "$BASE_DIR/$COMPRESSED_FILE"
    else
        msg_info "Baixando SQL Anywhere 17..."
        local wget_opts
        wget_opts=$(detectar_wget_opts)

        # shellcheck disable=SC2086
        if ! wget $wget_opts -O "$BASE_DIR/$COMPRESSED_FILE" "$DOWNLOAD_URL" 2>/dev/null; then
            msg_warning "Download falhou. Tentando novamente sem verificação de certificado SSL..."
            msg_warning "(Isso pode ocorrer em redes com inspeção de tráfego, ex: Zscaler, FortiGate)"
            rm -f "$BASE_DIR/$COMPRESSED_FILE"

            # shellcheck disable=SC2086
            if ! wget --no-check-certificate $wget_opts -O "$BASE_DIR/$COMPRESSED_FILE" "$DOWNLOAD_URL"; then
                msg_error "Falha no download mesmo sem verificação SSL. Verifique sua conexão com a internet."
                rm -rf "$BASE_DIR"
                exit 1
            fi
            msg_warning "Download concluído sem verificação de certificado SSL."
        fi
    fi

    # --- Verificação básica de integridade ---
    local file_size
    file_size=$(stat -c%s "$BASE_DIR/$COMPRESSED_FILE" 2>/dev/null)
    if [ -z "$file_size" ] || [ "$file_size" -lt 1000000 ]; then
        msg_error "Arquivo baixado parece corrompido (tamanho: ${file_size:-0} bytes)."
        rm -f "$BASE_DIR/$COMPRESSED_FILE"
        rm -rf "$BASE_DIR"
        exit 1
    fi
    msg_info "Arquivo verificado (${file_size} bytes)."

    # --- Extração com tratamento de erro ---
    msg_info "Extraindo arquivos para /opt ..."
    local extract_log="/tmp/sqlanywhere_extract.log"
    if ! tar -xf "$BASE_DIR/$COMPRESSED_FILE" -C /opt 2>"$extract_log"; then
        msg_error "Falha na extração! Detalhes:"
        cat "$extract_log"
        msg_error "Log salvo em: $extract_log"
        rm -f "$BASE_DIR/$COMPRESSED_FILE"
        exit 1
    fi
    rm -f "$extract_log" "$BASE_DIR/$COMPRESSED_FILE"
    msg_success "Extração concluída."

    # --- Verificar e recriar symlinks em lib64 ---
    msg_info "Verificando links simbólicos em lib64..."
    if [ -d "$BASE_DIR/lib64" ]; then
        local _pwd_saved
        _pwd_saved=$(pwd)
        cd "$BASE_DIR/lib64" || exit 1
        for real in *.so.1; do
            [ -f "$real" ] || continue
            local link="${real%.1}"
            if [ ! -e "$link" ]; then
                ln -sf "$real" "$link"
                msg_warning "Symlink recriado: $link -> $real"
            fi
        done
        cd "$_pwd_saved" || true
    fi

    # --- Permissões seletivas ---
    msg_info "Ajustando permissões..."
    chmod +x "$BASE_DIR/bin64/"* 2>/dev/null
    chmod +x "$BASE_DIR/bin64s/"* 2>/dev/null
    chmod 644 "$BASE_DIR/lib64/"*.so* 2>/dev/null
    chmod 644 "$BASE_DIR/res/"* 2>/dev/null
    mkdir -p "$CONF_DIR" "$LOG_DIR" "$LOG_HIST_DIR"

    # --- Variáveis de ambiente globais ---
    cat > /etc/profile.d/sqlanywhere17.sh <<'PROFILE_EOF'
# SQL Anywhere 17 — variáveis de ambiente
[ -f /opt/sqlanywhere17/setenv ] && source /opt/sqlanywhere17/setenv >/dev/null 2>&1
PROFILE_EOF
    chmod +x /etc/profile.d/sqlanywhere17.sh

    # --- Script runner (chamado pelo systemd) ---
    cat > "$RUNNER_SCRIPT" <<'RUNNER_EOF'
#!/bin/bash
set -euo pipefail
DB_ID="${1:?Uso: sqlanywhere-runner <id>}"
CONF_FILE="/opt/sqlanywhere17/conf.d/${DB_ID}.conf"
[ -f "$CONF_FILE" ] || { echo "Configuração não encontrada: $CONF_FILE" >&2; exit 1; }

unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
source "$CONF_FILE"
source /opt/sqlanywhere17/setenv >/dev/null 2>&1 || true

: "${SERVER_NAME:?SERVER_NAME não definido em $CONF_FILE}"
: "${DB_PATH:?DB_PATH não definido em $CONF_FILE}"
: "${CACHE_MB:?CACHE_MB não definido em $CONF_FILE}"
: "${PORT:?PORT não definido em $CONF_FILE}"

LOG_FILE="/opt/sqlanywhere17/log/log_${SERVER_NAME}.txt"
EXTRA_OPTS="${EXTRA_OPTS:-}"

exec dbsrv17 -n "$SERVER_NAME" -c "${CACHE_MB}M" -o "$LOG_FILE" \
     -x "tcpip(ServerPort=${PORT})" $EXTRA_OPTS "$DB_PATH"
RUNNER_EOF
    chmod 755 "$RUNNER_SCRIPT"

    # --- Serviço systemd (template) ---
    cat > "$TEMPLATE_SERVICE" <<'SERVICE_EOF'
[Unit]
Description=SQL Anywhere 17 — %i
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/sqlanywhere-runner %i
KillSignal=SIGTERM
KillMode=control-group
TimeoutStopSec=300
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
SERVICE_EOF
    chmod 644 "$TEMPLATE_SERVICE"
    systemctl daemon-reload

    # --- Verificação pós-instalação ---
    msg_info "Verificando se o binário funciona..."
    source "$BASE_DIR/setenv" >/dev/null 2>&1 || true
    if dbsrv17 -v >/dev/null 2>&1; then
        local version
        version=$(dbsrv17 -v 2>&1 | head -1)
        msg_success "dbsrv17 OK: $version"
    else
        msg_warning "dbsrv17 não respondeu. Verificando bibliotecas ausentes..."
        local missing
        missing=$(ldd "$BASE_DIR/bin64/dbsrv17" 2>&1 | grep "not found")
        if [ -n "$missing" ]; then
            msg_error "Bibliotecas faltando:"
            echo "$missing"
            msg_error "Instale as bibliotecas acima antes de iniciar qualquer banco."
        else
            msg_warning "Nenhuma biblioteca ausente detectada, mas o binário não respondeu."
            msg_info "Verifique manualmente: ldd $BASE_DIR/bin64/dbsrv17"
        fi
    fi

    echo
    msg_success "Instalação da base concluída!"
    read -r -p "  Pressione Enter para continuar..."
}

adicionarBanco() {
    clear
    echo
    msg_info "=== Adicionar Novo Banco de Dados ==="
    echo

    # --- ID ---
    read -rp "  Identificador único (ex: matriz, filial01): " ID
    if [[ -z "$ID" || ! "$ID" =~ ^[a-zA-Z0-9_-]+$ ]]; then
        msg_error "Identificador inválido. Use apenas letras, números, _ e -"
        sleep 2
        return
    fi
    if [ -f "$CONF_DIR/$ID.conf" ]; then
        msg_error "Já existe um banco com o ID '$ID'."
        sleep 2
        return
    fi

    # --- Nome do servidor ---
    read -rp "  Nome do servidor (-n): " NAME
    if [[ -z "$NAME" || ! "$NAME" =~ ^[a-zA-Z0-9_.\-]+$ ]]; then
        msg_error "Nome inválido. Use apenas letras, números, _ . e -"
        sleep 2
        return
    fi

    # --- Localizar arquivo .db ---
    echo
    msg_info "Como deseja localizar o arquivo do banco de dados (.db)?"
    echo "  1) Digitar o caminho completo"
    echo "  2) Pesquisar arquivos 'contabil.db' no computador"
    echo
    local DBFILE=""
    read -rp "  Opção [1/2]: " busca_opt

    case "${busca_opt:-1}" in
        2)
            msg_info "Pesquisando nos locais mais comuns..."
            local search_dirs=("/dados" "/home" "/opt" "/srv" "/var" "/mnt" "/backup")
            local found_files=()

            for sdir in "${search_dirs[@]}"; do
                [ -d "$sdir" ] || continue
                while IFS= read -r -d '' f; do
                    found_files+=("$f")
                done < <(find "$sdir" -type f -iname "contabil.db" -print0 2>/dev/null)
            done

            if [ ${#found_files[@]} -eq 0 ]; then
                msg_warning "Nada nos locais comuns. Pesquisando todo o sistema (pode demorar)..."
                while IFS= read -r -d '' f; do
                    found_files+=("$f")
                done < <(find / -type f -iname "contabil.db" \
                    -not -path "/proc/*" -not -path "/sys/*" -not -path "/dev/*" \
                    -not -path "/run/*" -not -path "/snap/*" \
                    -print0 2>/dev/null)
            fi

            if [ ${#found_files[@]} -gt 0 ]; then
                echo
                msg_info "Bancos de dados encontrados:"
                for i in "${!found_files[@]}"; do
                    echo "  $((i + 1))) ${found_files[i]}"
                done
                echo "  0) Digitar caminho manualmente"
                echo
                read -rp "  Escolha: " choice
                if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 0 ] && [ "$choice" -le ${#found_files[@]} ]; then
                    if [ "$choice" -eq 0 ]; then
                        read -rp "  Caminho completo do .db: " DBFILE
                    else
                        DBFILE="${found_files[$((choice - 1))]}"
                    fi
                else
                    msg_error "Opção inválida."
                    sleep 2
                    return
                fi
            else
                msg_warning "Nenhum arquivo 'contabil.db' encontrado."
                read -rp "  Digite o caminho completo do .db: " DBFILE
            fi
            ;;
        *)
            read -rp "  Caminho completo do .db: " DBFILE
            ;;
    esac

    if [ ! -f "$DBFILE" ]; then
        msg_error "Arquivo não encontrado: $DBFILE"
        sleep 2
        return
    fi
    msg_success "Arquivo localizado: $DBFILE"

    # --- Cache (com validação completa) ---
    echo
    verificarMemoriaLivre

    local val_total_mb val_avail_mb val_reserva_so val_cache_existente
    val_total_mb=$(obterRamTotalMB)
    val_avail_mb=$(obterMemDisponivelMB)
    val_reserva_so=2048
    temAmbienteGrafico && val_reserva_so=4096
    val_cache_existente=$(obterCacheTotalBancos)

    local CACHE
    while true; do
        read -rp "  Cache em MB: " CACHE

        if [[ ! "$CACHE" =~ ^[0-9]+$ ]] || [ "$CACHE" -eq 0 ]; then
            msg_error "Informe um número positivo."
            continue
        fi

        local cache_total_novo=$(( val_cache_existente + CACHE ))
        local sobra_so=$(( val_total_mb - cache_total_novo ))

        # BLOQUEIO: cache > RAM total
        if [ "$CACHE" -gt "$val_total_mb" ]; then
            msg_error "O valor informado (${CACHE} MB) é MAIOR que a RAM total do servidor (${val_total_mb} MB)."
            msg_error "Não é possível definir um cache maior que a memória física."
            continue
        fi

        # BLOQUEIO: sobra < 512 MB para o SO
        if [ "$sobra_so" -lt 512 ]; then
            msg_error "Com este valor, sobrariam apenas ${sobra_so} MB para o SO (mínimo absoluto: 512 MB)."
            msg_error "Reduza o valor do cache."
            continue
        fi

        # AVISO: cache > memória disponível
        if [ "$CACHE" -gt "$val_avail_mb" ]; then
            echo
            msg_warning "O valor informado (${CACHE} MB) é maior que a memória disponível agora (${val_avail_mb} MB)."
            msg_warning "Isso pode causar uso excessivo de swap e degradar o desempenho."
            read -rp "  Continuar mesmo assim? [s/N]: " cont_cache
            [[ ! "$cont_cache" =~ ^[Ss]$ ]] && continue
        fi

        # AVISO: sobra para SO abaixo do recomendado
        if [ "$sobra_so" -lt "$val_reserva_so" ]; then
            echo
            local tipo_so="server"
            [ "$val_reserva_so" -eq 4096 ] && tipo_so="desktop"
            msg_warning "Com todos os bancos, sobrarão ${sobra_so} MB para o SO (recomendado para Linux $tipo_so: ${val_reserva_so} MB)."
            read -rp "  Continuar mesmo assim? [s/N]: " cont_so
            [[ ! "$cont_so" =~ ^[Ss]$ ]] && continue
        fi

        # AVISO: cache < 70% do tamanho do banco
        if [ -f "$DBFILE" ]; then
            local val_db_bytes val_db_mb
            val_db_bytes=$(stat -c%s "$DBFILE" 2>/dev/null || echo 0)
            val_db_mb=$(( val_db_bytes / 1024 / 1024 ))
            if [ "$val_db_mb" -gt 0 ]; then
                local val_ideal=$(( val_db_mb * 70 / 100 ))
                if [ "$CACHE" -lt "$val_ideal" ]; then
                    echo
                    msg_warning "O banco tem ${val_db_mb} MB. Recomenda-se cache de pelo menos 70% (${val_ideal} MB)."
                    msg_info "Com cache abaixo disso, o servidor usará mais disco e ficará mais lento."
                    read -rp "  Continuar com ${CACHE} MB? [s/N]: " cont_70
                    [[ ! "$cont_70" =~ ^[Ss]$ ]] && continue
                fi
            fi
        fi

        break
    done

    # --- Porta ---
    local port_sug
    port_sug=$(porta_livre)
    read -rp "  Porta TCP (sugestão: ${port_sug}): " PORT
    PORT="${PORT:-$port_sug}"
    if [[ ! "$PORT" =~ ^[0-9]+$ ]]; then
        msg_error "Porta inválida."
        sleep 2
        return
    fi

    # --- Diretório de backup ---
    local default_bkp
    default_bkp="$(dirname "$DBFILE")/Backups"
    echo
    read -rp "  Diretório para cópias de segurança [$default_bkp]: " BACKUP_PATH
    BACKUP_PATH="${BACKUP_PATH:-$default_bkp}"

    # --- Salvar ---
    cat > "$CONF_DIR/$ID.conf" <<EOF
SERVER_NAME="$NAME"
DB_PATH="$DBFILE"
CACHE_MB="$CACHE"
PORT="$PORT"
BACKUP_DIR="$BACKUP_PATH"
EXTRA_OPTS=""
EOF

    configurar_firewall "$PORT"
    systemctl daemon-reload
    systemctl enable "sqlanywhere@$ID.service" >/dev/null 2>&1
    echo
    msg_success "Banco '$ID' configurado com sucesso!"

    echo
    read -rp "  Iniciar o banco '$ID' agora? [S/n]: " ans
    if [[ "${ans:-S}" =~ ^[Ss]$ ]]; then
        systemctl start "sqlanywhere@$ID.service" && sleep 2
        echo
        systemctl status "sqlanywhere@$ID.service" --no-pager
    fi
    echo
    read -r -p "  Pressione Enter para continuar..."
}

# ==============================================================================
#  REMOVER BANCO
# ==============================================================================

removerBanco() {
    clear
    echo
    msg_info "=== Remover Banco de Dados ==="
    echo

    local bancos
    mapfile -t bancos < <(listarBancos)
    if [ ${#bancos[@]} -eq 0 ]; then
        msg_warning "Nenhum banco de dados configurado."
        sleep 2
        return
    fi

    echo "  Bancos configurados:"
    echo

    local i=1
    for _bid in "${bancos[@]}"; do
        local _conf="$CONF_DIR/$_bid.conf"
        unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
        source "$_conf"

        local _status _cor
        if systemctl is-active --quiet "sqlanywhere@$_bid.service" 2>/dev/null; then
            _status="● Ativo"; _cor=$(tput setaf 2 2>/dev/null)
        else
            _status="● Inativo"; _cor=$(tput setaf 1 2>/dev/null)
        fi
        local _reset
        _reset=$(tput sgr0 2>/dev/null)

        printf "    %d) %-15s  Servidor: %-15s  Porta: %-6s  Cache: %-6s MB  %b%s%b\n" \
            "$i" "$_bid" "${SERVER_NAME:-?}" "${PORT:-?}" "${CACHE_MB:-?}" "$_cor" "$_status" "$_reset"
        i=$((i + 1))
    done

    echo
    echo "    0) Voltar"
    echo
    read -rp "  Selecione o banco para remover [0-${#bancos[@]}]: " escolha

    if [ "$escolha" = "0" ] || [ -z "$escolha" ]; then return; fi

    if [[ ! "$escolha" =~ ^[0-9]+$ ]] || [ "$escolha" -lt 1 ] || [ "$escolha" -gt ${#bancos[@]} ]; then
        msg_error "Opção inválida."
        sleep 2
        return
    fi

    local idx=$((escolha - 1))
    local banco_id="${bancos[$idx]}"
    local conf_file="$CONF_DIR/$banco_id.conf"

    # Ler dados ANTES de qualquer remoção
    unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
    source "$conf_file"

    local saved_name="${SERVER_NAME:-$banco_id}"
    local saved_dbpath="${DB_PATH:-}"
    local saved_port="${PORT:-}"
    local saved_cache="${CACHE_MB:-}"
    local saved_backup="${BACKUP_DIR:-}"

    echo
    echo "  Detalhes do banco:"
    echo "    ID:       $banco_id"
    echo "    Servidor: $saved_name"
    echo "    Banco:    ${saved_dbpath:-?}"
    echo "    Porta:    ${saved_port:-?}"
    echo "    Cache:    ${saved_cache:-?} MB"
    echo "    Backup:   ${saved_backup:-?}"
    echo
    msg_warning "Esta ação irá:"
    echo "    • Parar o serviço (se ativo)"
    echo "    • Remover a configuração e desabilitar o serviço systemd"
    echo "    • Remover a regra de firewall da porta ${saved_port:-?}"
    echo "    • O arquivo do banco (.db) NÃO será apagado automaticamente"
    echo
    read -rp "  Confirma a remoção? Digite 'sim' para confirmar: " confirmacao

    if [ "$confirmacao" != "sim" ]; then
        msg_info "Operação cancelada."
        sleep 2
        return
    fi

    # Parar serviço
    local service_name="sqlanywhere@${banco_id}.service"
    if systemctl is-active --quiet "$service_name" 2>/dev/null; then
        msg_info "Parando o serviço '$banco_id'..."
        systemctl stop "$service_name" 2>/dev/null
        msg_success "Serviço parado."
    fi

    # Desabilitar serviço
    if systemctl is-enabled --quiet "$service_name" 2>/dev/null; then
        systemctl disable "$service_name" 2>/dev/null
        msg_success "Serviço desabilitado."
    fi

    # Remover regra de firewall
    if [ -n "$saved_port" ]; then
        remover_firewall "$saved_port"
        msg_success "Regra de firewall removida (porta $saved_port)."
    fi

    # Remover configuração
    rm -f "$conf_file"
    systemctl daemon-reload
    msg_success "Configuração do banco '$banco_id' removida."

    # Oferecer remoção do arquivo .db
    if [ -n "$saved_dbpath" ] && [ -f "$saved_dbpath" ]; then
        echo
        msg_warning "O arquivo do banco ainda existe em: $saved_dbpath"
        read -rp "  Deseja APAGAR o arquivo do banco? (IRREVERSÍVEL) [s/N]: " apagar_db
        if [[ "$apagar_db" =~ ^[Ss]$ ]]; then
            read -rp "  CERTEZA ABSOLUTA? Digite 'APAGAR' para confirmar: " conf_apagar
            if [ "$conf_apagar" = "APAGAR" ]; then
                rm -f "$saved_dbpath"
                msg_success "Arquivo $saved_dbpath removido."
            else
                msg_info "Arquivo do banco mantido."
            fi
        else
            msg_info "Arquivo do banco mantido em: $saved_dbpath"
        fi
    fi

    echo
    msg_success "Banco '$banco_id' removido com sucesso."
    echo
    read -r -p "  Pressione Enter para continuar..."
}

# ==============================================================================
#  BACKUP
# ==============================================================================

realizarBackup() {
    clear
    echo
    msg_info "=== Cópia de Segurança do Banco de Dados ==="
    echo

    local bancos
    mapfile -t bancos < <(listarBancos)
    if [ ${#bancos[@]} -eq 0 ]; then
        msg_warning "Nenhum banco de dados configurado."
        sleep 2
        return
    fi

    msg_info "Selecione o banco para copiar:"
    select id in "${bancos[@]}" "Todos" "Cancelar"; do
        [ "$id" = "Cancelar" ] && return
        [ -n "$id" ] && break
    done

    echo
    msg_warning "O banco será PARADO durante a cópia para garantir a integridade dos dados."
    read -rp "  Continuar? [S/n]: " c
    [[ ! "${c:-S}" =~ ^[Ss]$ ]] && return

    if ! command -v 7z &>/dev/null; then
        msg_error "O utilitário '7z' não está instalado. Execute a opção 1 (Instalar Base) primeiro."
        read -r -p "  Pressione Enter para continuar..."
        return
    fi

    local alvos=()
    if [ "$id" = "Todos" ]; then
        alvos=("${bancos[@]}")
    else
        alvos=("$id")
    fi

    local falhas=0
    for i in "${alvos[@]}"; do
        unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
        source "$CONF_DIR/$i.conf"

        local bdir="${BACKUP_DIR:-$(dirname "$DB_PATH")/Backups}"
        mkdir -p "$bdir"

        msg_info "Parando banco '$i'..."
        systemctl stop "sqlanywhere@$i.service" 2>/dev/null

        local bn
        bn=$(date +"%Y%m%d")
        local seq=1
        local bkp="${bn}_${seq}C.dom"
        while [ -f "$bdir/$bkp" ]; do
            seq=$((seq + 1))
            bkp="${bn}_${seq}C.dom"
        done

        msg_info "Compactando '$i' -> $bdir/$bkp ..."
        if 7z a -mx=4 "$bdir/$bkp" "$(dirname "$DB_PATH")"/* >/dev/null 2>&1; then
            msg_success "Cópia de '$i' concluída: $bdir/$bkp"
        else
            msg_error "Falha ao copiar o banco '$i'!"
            falhas=$((falhas + 1))
        fi

        msg_info "Iniciando banco '$i'..."
        systemctl start "sqlanywhere@$i.service" 2>/dev/null
        echo
    done

    if [ $falhas -eq 0 ]; then
        msg_success "Todas as cópias de segurança foram concluídas."
    else
        msg_warning "$falhas cópia(s) apresentaram falha. Verifique os erros acima."
    fi
    read -r -p "  Pressione Enter para continuar..."
}

gerenciarParametros() {
    clear
    echo
    msg_info "=== Parâmetros Avançados ==="
    echo

    local bancos
    mapfile -t bancos < <(listarBancos)
    if [ ${#bancos[@]} -eq 0 ]; then
        msg_warning "Nenhum banco de dados configurado."
        sleep 2
        return
    fi

    msg_info "Selecione o banco:"
    select id in "${bancos[@]}" "Cancelar"; do
        [ "$id" = "Cancelar" ] && return
        [ -n "$id" ] && break
    done

    local conf="$CONF_DIR/$id.conf"
    unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
    source "$conf"

    local ti=0 sb=0 zt=0
    [[ "${EXTRA_OPTS:-}" == *"-ti 0"* ]] && ti=1
    [[ "${EXTRA_OPTS:-}" == *"-sb 0"* ]] && sb=1
    [[ "${EXTRA_OPTS:-}" == *"-zt"* ]] && zt=1

    while true; do
        clear
        echo
        msg_info "Parâmetros do banco: '$id' (servidor: $SERVER_NAME)"
        echo
        echo "  1) $([ $ti -eq 1 ] && echo '[ATIVO]' || echo '[-----]') Desabilitar timeout por ociosidade (-ti 0)"
        echo "  2) $([ $sb -eq 1 ] && echo '[ATIVO]' || echo '[-----]') Ocultar servidor na rede (-sb 0)"
        tput setaf 5 2>/dev/null
        echo "  3) $([ $zt -eq 1 ] && echo '[ATIVO]' || echo '[-----]') Log de diagnóstico detalhado (pode impactar desempenho)"
        tput sgr0 2>/dev/null
        echo
        echo "  4) Salvar e reiniciar o banco"
        echo "  5) Cancelar sem salvar"
        echo
        read -rp "  Opção: " opt

        case $opt in
            1) ti=$((1 - ti)) ;;
            2) sb=$((1 - sb)) ;;
            3)
                if [ $zt -eq 0 ]; then
                    echo
                    msg_warning "O log de diagnóstico gera grande volume de dados e pode afetar o desempenho."
                    read -rp "  Ativar mesmo assim? [s/N]: " zc
                    [[ "$zc" =~ ^[Ss]$ ]] && zt=1
                else
                    zt=0
                fi
                ;;
            4)
                local opts=""
                [ $ti -eq 1 ] && opts+="-ti 0"
                [ $sb -eq 1 ] && { [ -n "$opts" ] && opts+=" "; opts+="-sb 0"; }
                [ $zt -eq 1 ] && { [ -n "$opts" ] && opts+=" "; opts+="-zt -zl -zr all -zn 20 -zs 500m -zo $LOG_DIR/req_${SERVER_NAME}.txt"; }

                cat > "$conf" <<EOF
SERVER_NAME="$SERVER_NAME"
DB_PATH="$DB_PATH"
CACHE_MB="$CACHE_MB"
PORT="$PORT"
BACKUP_DIR="${BACKUP_DIR:-}"
EXTRA_OPTS="$opts"
EOF
                if grep -q "EXTRA_OPTS=\"$opts\"" "$conf"; then
                    msg_success "Configuração salva. EXTRA_OPTS='$opts'"
                else
                    msg_error "Erro ao salvar configuração!"
                    read -r -p "  Pressione Enter..."
                    return
                fi

                msg_info "Reiniciando banco '$id'..."
                systemctl restart "sqlanywhere@$id.service" && sleep 2
                echo
                systemctl status "sqlanywhere@$id.service" --no-pager
                echo
                read -r -p "  Pressione Enter para continuar..."
                return
                ;;
            5) return ;;
            *) ;;
        esac
    done
}

Desinstalacao() {
    clear
    echo
    msg_warning "=== Remover Instalação do SQL Anywhere 17 ==="
    echo
    msg_info "Esta ação irá:"
    echo "  • Parar e remover todos os serviços configurados"
    echo "  • Remover as regras de firewall das portas configuradas"
    echo "  • Remover a instalação em $BASE_DIR"
    echo "  • Os arquivos de banco de dados (.db) NÃO serão apagados"
    echo
    read -rp "  Digite 'SIM' para confirmar a remoção: " confirmacao
    [ "$confirmacao" != "SIM" ] && { msg_info "Operação cancelada."; sleep 2; return; }

    local bancos
    mapfile -t bancos < <(listarBancos 2>/dev/null)

    for bid in "${bancos[@]}"; do
        if [ -f "$CONF_DIR/$bid.conf" ]; then
            unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
            source "$CONF_DIR/$bid.conf"
            msg_info "Parando e desabilitando '$bid'..."
            systemctl stop "sqlanywhere@$bid.service" >/dev/null 2>&1
            systemctl disable "sqlanywhere@$bid.service" >/dev/null 2>&1
            [ -n "${PORT:-}" ] && remover_firewall "$PORT"
        fi
    done

    rm -f "$TEMPLATE_SERVICE" "$RUNNER_SCRIPT"
    rm -f /etc/profile.d/sqlanywhere17.sh /etc/profile.d/domsis.sh
    systemctl daemon-reload
    rm -rf "$BASE_DIR"

    echo
    msg_success "SQL Anywhere 17 removido completamente."
    sleep 3
}

gerenciarBancos() {
    clear
    echo
    local titulo="$1"
    local acao="$2"

    msg_info "=== $titulo ==="
    echo

    local bancos
    mapfile -t bancos < <(listarBancos)
    if [ ${#bancos[@]} -eq 0 ]; then
        msg_warning "Nenhum banco de dados configurado."
        sleep 2
        return
    fi

    msg_info "Selecione o banco:"
    select id in "${bancos[@]}" "Todos" "Cancelar"; do
        case "$id" in
            "Cancelar") return ;;
            "Todos")
                for b in "${bancos[@]}"; do
                    echo
                    msg_info "--- $b ---"
                    if [ "$acao" = "status" ]; then
                        systemctl "$acao" "sqlanywhere@$b.service" --no-pager 2>&1
                    else
                        systemctl "$acao" "sqlanywhere@$b.service" 2>&1
                    fi
                done
                break
                ;;
            *)
                if [ -n "$id" ]; then
                    echo
                    if [ "$acao" = "status" ]; then
                        systemctl "$acao" "sqlanywhere@$id.service" --no-pager
                    else
                        systemctl "$acao" "sqlanywhere@$id.service"
                    fi
                fi
                break
                ;;
        esac
    done

    echo
    read -r -p "  Pressione Enter para continuar..."
}

exibirLog() {
    clear
    echo
    msg_info "=== Visualizar Log do Banco ==="
    echo

    local bancos
    mapfile -t bancos < <(listarBancos)
    if [ ${#bancos[@]} -eq 0 ]; then
        msg_warning "Nenhum banco de dados configurado."
        sleep 2
        return
    fi

    msg_info "Selecione o banco:"
    select id in "${bancos[@]}" "Cancelar"; do
        [ "$id" = "Cancelar" ] && return
        if [ -n "$id" ]; then
            unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
            source "$CONF_DIR/$id.conf"
            local log="$LOG_DIR/log_${SERVER_NAME}.txt"
            if [ -f "$log" ]; then
                echo
                msg_info "Abrindo: $log"
                msg_info "Pressione 'q' para sair  |  Shift+F para acompanhar em tempo real"
                sleep 1
                less +F "$log"
            else
                echo
                msg_warning "Log não encontrado: $log"
                msg_info "Isso acontece quando o serviço nunca foi iniciado com sucesso."
                echo
                read -r -p "  Pressione Enter para continuar..."
            fi
            break
        fi
    done
}

arquivarLogs() {
    clear
    echo
    msg_info "=== Arquivar Logs ==="
    echo
    msg_info "Logs ativos com mais de 30 dias serão compactados em .7z"
    msg_info "e movidos para a pasta de histórico: $LOG_HIST_DIR"
    echo

    if ! command -v 7z &>/dev/null; then
        msg_error "O utilitário '7z' não está instalado."
        read -r -p "  Pressione Enter para continuar..."
        return
    fi

    mkdir -p "$LOG_HIST_DIR"

    local arquivados=0
    local agora
    agora=$(date +%s)

    for logfile in "$LOG_DIR"/log_*.txt "$LOG_DIR"/req_*.txt; do
        [ -f "$logfile" ] || continue
        [ ! -s "$logfile" ] && continue

        local mod_time
        mod_time=$(stat -c%Y "$logfile" 2>/dev/null)
        [ -z "$mod_time" ] && continue

        local idade_dias=$(( (agora - mod_time) / 86400 ))

        if [ "$idade_dias" -lt 30 ]; then
            continue
        fi

        local base_nome
        base_nome=$(basename "$logfile" .txt)
        local mes_ref
        mes_ref=$(date -d "@$mod_time" +"%Y-%m" 2>/dev/null)
        [ -z "$mes_ref" ] && mes_ref=$(date +"%Y-%m")

        local destino="$LOG_HIST_DIR/${base_nome}_${mes_ref}.7z"

        if [ -f "$destino" ]; then
            local s=2
            while [ -f "$LOG_HIST_DIR/${base_nome}_${mes_ref}_${s}.7z" ]; do
                s=$((s + 1))
            done
            destino="$LOG_HIST_DIR/${base_nome}_${mes_ref}_${s}.7z"
        fi

        msg_info "Arquivando: $(basename "$logfile") ($idade_dias dias)"

        local tmp_copy="/tmp/$(basename "$logfile").archiving"
        cp "$logfile" "$tmp_copy"
        : > "$logfile"

        if 7z a -mx=7 "$destino" "$tmp_copy" >/dev/null 2>&1; then
            rm -f "$tmp_copy"
            msg_success "  -> $(basename "$destino")"
            arquivados=$((arquivados + 1))
        else
            msg_error "Falha ao compactar. Restaurando log original..."
            cat "$tmp_copy" >> "$logfile"
            rm -f "$tmp_copy"
        fi
    done

    echo
    if [ $arquivados -gt 0 ]; then
        msg_success "$arquivados log(s) arquivado(s)."
    else
        msg_info "Nenhum log com mais de 30 dias encontrado."
        echo
        read -rp "  Deseja arquivar TODOS os logs ativos agora mesmo? [s/N]: " forcar
        if [[ "$forcar" =~ ^[Ss]$ ]]; then
            for logfile in "$LOG_DIR"/log_*.txt "$LOG_DIR"/req_*.txt; do
                [ -f "$logfile" ] || continue
                [ ! -s "$logfile" ] && continue

                local base_nome
                base_nome=$(basename "$logfile" .txt)
                local mes_ref
                mes_ref=$(date +"%Y-%m")
                local destino="$LOG_HIST_DIR/${base_nome}_${mes_ref}.7z"
                if [ -f "$destino" ]; then
                    local s=2
                    while [ -f "$LOG_HIST_DIR/${base_nome}_${mes_ref}_${s}.7z" ]; do s=$((s + 1)); done
                    destino="$LOG_HIST_DIR/${base_nome}_${mes_ref}_${s}.7z"
                fi

                local tmp_copy="/tmp/$(basename "$logfile").archiving"
                cp "$logfile" "$tmp_copy"
                : > "$logfile"
                if 7z a -mx=7 "$destino" "$tmp_copy" >/dev/null 2>&1; then
                    rm -f "$tmp_copy"
                    msg_success "Arquivado: $(basename "$destino")"
                    arquivados=$((arquivados + 1))
                else
                    cat "$tmp_copy" >> "$logfile"
                    rm -f "$tmp_copy"
                    msg_error "Falha: $(basename "$logfile")"
                fi
            done
            [ $arquivados -gt 0 ] && msg_success "$arquivados log(s) arquivado(s)."
        fi
    fi

    if ls "$LOG_HIST_DIR"/*.7z >/dev/null 2>&1; then
        echo
        msg_info "Histórico de logs arquivados:"
        ls -lh "$LOG_HIST_DIR"/*.7z 2>/dev/null | awk '{printf "  %-50s %s\n", $NF, $5}'
    fi

    echo
    read -r -p "  Pressione Enter para continuar..."
}

coletarDiagnosticos() {
    clear
    echo
    msg_info "=== Coletar Informações de Diagnóstico ==="
    echo
    msg_info "Será gerado um arquivo .tar.gz com logs, configs e dados do sistema."
    msg_info "Envie este arquivo ao suporte quando solicitado."
    echo

    local d="/tmp/diag_$(date +%s)"
    local f="/tmp/diag_$(hostname)_$(date +%Y%m%d_%H%M%S).tar.gz"
    mkdir -p "$d"

    msg_info "Coletando logs..."
    cp -r "$LOG_DIR" "$d/logs" 2>/dev/null

    msg_info "Coletando configurações..."
    cp -r "$CONF_DIR" "$d/conf" 2>/dev/null

    msg_info "Coletando informações do sistema..."
    {
        echo "=== Data/Hora ==="
        date
        echo
        echo "=== Hostname ==="
        hostname
        echo
        echo "=== Kernel e Arquitetura ==="
        uname -a
        echo
        echo "=== Distribuição ==="
        cat /etc/os-release 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== glibc ==="
        ldd --version 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Uptime ==="
        uptime 2>/dev/null
        echo
        echo "=== Virtualização ==="
        systemd-detect-virt 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== CPU ==="
        lscpu 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== CPU Governor ==="
        cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== CPU Frequência Atual (kHz) ==="
        cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== CPU Frequência Máxima (kHz) ==="
        cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Memória ==="
        free -h 2>/dev/null || free -m 2>/dev/null
        echo
        grep -E 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree' /proc/meminfo 2>/dev/null
        echo
        echo "=== Disco ==="
        df -h 2>/dev/null
        echo
        echo "=== Tipo de disco (rotational: 1=HDD 0=SSD) ==="
        for dev in /sys/block/sd* /sys/block/nvme* /sys/block/vd*; do
            [ -f "$dev/queue/rotational" ] || continue
            echo "  $(basename "$dev"): $(cat "$dev/queue/rotational" 2>/dev/null)"
        done 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Interfaces de Rede ==="
        ip addr 2>/dev/null || ifconfig 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Velocidade da Interface Principal ==="
        local diag_iface
        diag_iface=$(ip route 2>/dev/null | awk '/default/{print $5; exit}')
        if [ -n "$diag_iface" ] && [ -f "/sys/class/net/$diag_iface/speed" ]; then
            echo "  $diag_iface: $(cat "/sys/class/net/$diag_iface/speed" 2>/dev/null) Mbps"
        else
            echo "(indisponível)"
        fi
        echo
        echo "=== Processos SQL Anywhere ==="
        ps -aux 2>/dev/null | grep "[d]bsrv17" || echo "Nenhum processo dbsrv17"
        echo
        echo "=== Serviços SQL Anywhere ==="
        systemctl list-units 'sqlanywhere@*' --no-pager 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Firewall (ufw) ==="
        ufw status verbose 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Firewall (firewalld) ==="
        firewall-cmd --list-all 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Dependências do dbsrv17 ==="
        ldd "$BASE_DIR/bin64/dbsrv17" 2>/dev/null || echo "(indisponível)"
        echo
        echo "=== Processos com maior consumo de memória (top 15) ==="
        ps -eo pid,ppid,user,%mem,%cpu,rss,comm --sort=-%mem 2>/dev/null | head -16 || echo "(indisponível)"
        echo
        echo "=== Processos com maior consumo de CPU (top 15) ==="
        ps -eo pid,ppid,user,%cpu,%mem,rss,comm --sort=-%cpu 2>/dev/null | head -16 || echo "(indisponível)"
    } > "$d/sistema.txt" 2>&1

    msg_info "Compactando..."
    if tar -czf "$f" -C "$(dirname "$d")" "$(basename "$d")" 2>/dev/null; then
        rm -rf "$d"
        echo
        msg_success "Diagnóstico gerado com sucesso!"
        msg_info "Arquivo: $f"
        msg_info "Tamanho: $(du -h "$f" 2>/dev/null | awk '{print $1}')"
    else
        msg_error "Erro ao gerar o arquivo de diagnóstico."
    fi

    echo
    read -r -p "  Pressione Enter para continuar..."
}

# ==============================================================================
#  MENU PRINCIPAL
# ==============================================================================

Menu() {
    clear
    echo
    tput setaf 1 2>/dev/null
    echo "  ╔═══════════════════════════════════════════════════╗"
    echo "  ║       Gerenciador SQL Anywhere 17  (v8.0)         ║"
    echo "  ╚═══════════════════════════════════════════════════╝"
    tput sgr0 2>/dev/null

    verificarHardware

    echo
    echo "  Instalação e Cópias de Segurança"
    tput setaf 2 2>/dev/null
    echo "   1) Instalar Base do SQL Anywhere"
    tput sgr0 2>/dev/null
    echo "   2) Adicionar Banco de Dados"
    tput setaf 1 2>/dev/null
    echo "   3) Remover Banco de Dados"
    tput sgr0 2>/dev/null
    tput setaf 2 2>/dev/null
    echo "   4) Fazer Cópia de Segurança"
    tput sgr0 2>/dev/null
    echo "   5) Remover Instalação Completa"
    echo
    echo "  Controle dos Serviços"
    tput setaf 2 2>/dev/null
    echo "   6) Iniciar Banco"
    tput sgr0 2>/dev/null
    echo "   7) Parar Banco"
    tput setaf 2 2>/dev/null
    echo "   8) Reiniciar Banco"
    tput sgr0 2>/dev/null
    echo "   9) Ver Status do Banco"
    echo
    echo "  Ferramentas e Diagnóstico"
    tput setaf 2 2>/dev/null
    echo "  10) Visualizar Log"
    tput setaf 5 2>/dev/null
    echo "  11) Parâmetros Avançados"
    tput sgr0 2>/dev/null
    echo "  12) Liberar Porta no Firewall"
    tput setaf 5 2>/dev/null
    echo "  13) Coletar Diagnósticos (para suporte)"
    tput sgr0 2>/dev/null
    echo "  14) Arquivar Logs (mover antigos para histórico)"
    echo
    tput setaf 2 2>/dev/null
    echo "   0) Sair"
    tput sgr0 2>/dev/null
    echo

    read -rp "  Opção: " var
    case $var in
        1)  InstalacaoBase ;;
        2)  adicionarBanco ;;
        3)  removerBanco ;;
        4)  realizarBackup ;;
        5)  Desinstalacao ;;
        6)  gerenciarBancos "Iniciar Banco de Dados" "start" ;;
        7)  gerenciarBancos "Parar Banco de Dados" "stop" ;;
        8)  gerenciarBancos "Reiniciar Banco de Dados" "restart" ;;
        9)  gerenciarBancos "Ver Status do Banco" "status" ;;
        10) exibirLog ;;
        11) gerenciarParametros ;;
        12)
            clear
            echo
            msg_info "=== Liberar Porta no Firewall ==="
            echo
            local bancos_fw
            mapfile -t bancos_fw < <(listarBancos)
            if [ ${#bancos_fw[@]} -eq 0 ]; then
                msg_warning "Nenhum banco configurado."
                sleep 2
            else
                msg_info "Selecione o banco:"
                select fw_id in "${bancos_fw[@]}" "Cancelar"; do
                    [ "$fw_id" = "Cancelar" ] && break
                    if [ -n "$fw_id" ]; then
                        unset SERVER_NAME DB_PATH CACHE_MB PORT EXTRA_OPTS BACKUP_DIR
                        source "$CONF_DIR/$fw_id.conf"
                        configurar_firewall "$PORT"
                        echo
                        msg_success "Porta $PORT liberada para o banco '$fw_id'."
                        break
                    fi
                done
            fi
            echo
            read -r -p "  Pressione Enter para continuar..."
            ;;
        13) coletarDiagnosticos ;;
        14) arquivarLogs ;;
        0)  echo; msg_info "Saindo."; exit 0 ;;
        *)  ;;
    esac
}

# ==============================================================================
#  LOOP PRINCIPAL
# ==============================================================================

while true; do
    Menu
done