#!/bin/bash

# Gerenciador SQL Anywhere 17 para Linux
# Último ajuste por Maicon Antunes Luiz (maicon.luiz@thomsonreuters.com)
# Versão 7.0 — Revisão Completa (Segurança, Compatibilidade e UX)
#
# 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+

# ==============================================================================
#  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
}

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 ))
    echo
    free -mh 2>/dev/null | grep -E 'Mem|total' || free -m 2>/dev/null | head -2
    echo
    msg_info "Memória utilizável para cache: ~${mem_rec_mb} MB (80% de MemAvailable)"
    msg_info "Defina o cache do banco dentro desse limite."
    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
}

# ==============================================================================
#  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 ---
    echo
    verificarMemoriaLivre
    read -rp "  Cache em MB: " CACHE
    if [[ ! "$CACHE" =~ ^[0-9]+$ ]] || [ "$CACHE" -eq 0 ]; then
        msg_error "Valor de cache inválido."
        sleep 2
        return
    fi

    # --- 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..."
}

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 ))

        # Arquivar somente logs com mais de 30 dias, OU oferecer forçar
        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"

        # Se já existe arquivo deste mês, adicionar sequência
        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)"

        # Abordagem copy-truncate: copiar, limpar original, compactar
        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

    # Listar histórico
    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 "=== 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 "=== Processos SQL Anywhere ==="
        ps -aux 2>/dev/null | grep "[d]bsrv17" || echo "Nenhum processo dbsrv17"
        echo
        echo "=== Memória ==="
        free -h 2>/dev/null || free -m 2>/dev/null
        echo
        echo "=== Disco ==="
        df -h 2>/dev/null
        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)"
    } > "$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  (v7.0)         ║"
    echo "  ╚═══════════════════════════════════════════════════╝"
    tput sgr0 2>/dev/null
    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 2 2>/dev/null
    echo "   3) Fazer Cópia de Segurança"
    tput sgr0 2>/dev/null
    echo "   4) Remover Instalação Completa"
    echo
    echo "  Controle dos Serviços"
    tput setaf 2 2>/dev/null
    echo "   5) Iniciar Banco"
    tput sgr0 2>/dev/null
    echo "   6) Parar Banco"
    tput setaf 2 2>/dev/null
    echo "   7) Reiniciar Banco"
    tput sgr0 2>/dev/null
    echo "   8) Ver Status do Banco"
    echo
    echo "  Ferramentas e Diagnóstico"
    tput setaf 2 2>/dev/null
    echo "   9) Visualizar Log"
    tput setaf 5 2>/dev/null
    echo "  10) Parâmetros Avançados"
    tput sgr0 2>/dev/null
    echo "  11) Liberar Porta no Firewall"
    tput setaf 5 2>/dev/null
    echo "  12) Coletar Diagnósticos (para suporte)"
    tput sgr0 2>/dev/null
    echo "  13) 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)  realizarBackup ;;
        4)  Desinstalacao ;;
        5)  gerenciarBancos "Iniciar Banco de Dados" "start" ;;
        6)  gerenciarBancos "Parar Banco de Dados" "stop" ;;
        7)  gerenciarBancos "Reiniciar Banco de Dados" "restart" ;;
        8)  gerenciarBancos "Ver Status do Banco" "status" ;;
        9)  exibirLog ;;
        10) gerenciarParametros ;;
        11)
            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
                return
            fi
            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
            echo
            read -r -p "  Pressione Enter para continuar..."
            ;;
        12) coletarDiagnosticos ;;
        13) arquivarLogs ;;
        0)  echo; msg_info "Saindo."; exit 0 ;;
        *)  ;;
    esac
}

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

while true; do
    Menu
done