#!/bin/bash
#===================================================================================
#======================== Desenvolvido por Matheus Salvador ========================
#=============================== www.redesbrasil.com ===============================
#===================================================================================
# ==============================================================================
# Instalacao Zabbix 7.0 + PostgreSQL + Apache2 + Zabbix Agent2 + Grafana
# Sistema: Debian 12 (Bookworm)
# Data: 2026-03-12
#
# Problemas conhecidos tratados neste script:
#  1. Autenticacao peer do PostgreSQL impede import direto como usuario 'zabbix'
#     -> Schema importado como 'postgres', depois GRANT explicito ao 'zabbix'
#  2. php8.2-pgsql e php8.2-intl nao vem na instalacao padrao do Zabbix
#     -> Instalados explicitamente
#  3. Apache2 /etc/apache2/envvars define LANG=C, quebrando PT-BR no frontend
#     -> Substituido por LANG=pt_BR.UTF-8
#  4. zabbix.conf.php inexistente apos instalacao
#     -> Criado via Python3 para evitar problemas de escape com heredoc
#  5. Apache zabbix.conf sem bloco <IfModule php_module> para PHP 8.x
#     -> Adicionado bloco explicito para cobrir PHP 8.2
#  6. PHP ini com valores insuficientes (memory 128M, execution 30s, etc.)
#     -> Ajustados para atender requisitos do Zabbix 7.0
#  7. Integracao Grafana->Zabbix via API Token (nao usuario/senha)
#     -> Token criado via Zabbix JSON-RPC API e configurado no datasource Grafana
#  8. Locales pt_BR.UTF-8 e en_US.UTF-8 nao gerados no container
#     -> Gerados e configurados como padrao do sistema
#  9. zabbix_server.conf tem DBPassword comentado por padrao
#     -> Descomentado e definido via sed
# ==============================================================================

set -euo pipefail

# ------------------------------------------------------------------------------
# CONFIGURACOES -- ajuste antes de executar
# ------------------------------------------------------------------------------
ZABBIX_VERSION="7.0"
DEBIAN_CODENAME="bookworm"
DB_NAME="zabbix"
DB_USER="zabbix"
DB_PASS="Redes@2026#certificacao"   # senha do usuario PostgreSQL do Zabbix
ZBX_SERVER_NAME="ZABBIX-POSTGRESQL" # nome exibido no frontend
TIMEZONE="America/Sao_Paulo"
LANG_LOCALE="pt_BR.UTF-8"

# Grafana
GRAFANA_ADMIN_PASS="admin"          # senha inicial do admin Grafana
ZBX_ADMIN_USER="Admin"              # usuario Admin do Zabbix (para gerar o API Token)
ZBX_ADMIN_PASS="zabbix"            # senha Admin do Zabbix (usada apenas para gerar o token)

# ------------------------------------------------------------------------------
# SISTEMA DE LOGS -- pasta logs/ no mesmo diretorio do script
# ------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="${SCRIPT_DIR}/logs"
STEP_LOG="/dev/stderr"  # redefinido por log_step()
MASTER_LOG=""           # definido em main()

# ------------------------------------------------------------------------------
# FUNCOES AUXILIARES
# ------------------------------------------------------------------------------
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'

_log_write() {
    local level="$1"; shift
    local msg="$*"
    local ts; ts="$(date '+%Y-%m-%d %H:%M:%S')"
    echo "[${ts}] [${level}]  ${msg}" >> "$STEP_LOG"
    [ -n "$MASTER_LOG" ] && echo "[${ts}] [${level}]  ${msg}" >> "$MASTER_LOG"
}

log_info()  { echo -e "${BLUE}[INFO]${NC}  $*";     _log_write "INFO " "$*"; }
log_ok()    { echo -e "${GREEN}[OK]${NC}    $*";     _log_write "OK   " "$*"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC}  $*";    _log_write "WARN " "$*"; }
log_error() { echo -e "${RED}[ERRO]${NC}  $*" >&2;  _log_write "ERRO " "$*"; }

log_step() {
    local title="$*"
    local safe_name
    safe_name=$(echo "$title" | sed 's/[^a-zA-Z0-9 _-]//g' | tr ' ' '_' \
        | tr '[:upper:]' '[:lower:]' | sed 's/__*/_/g' | cut -c1-60)
    STEP_LOG="${LOG_DIR}/${safe_name}.log"
    {
        echo "========================================"
        echo "  $(date '+%Y-%m-%d %H:%M:%S')"
        echo "  ${title}"
        echo "========================================"
    } >> "$STEP_LOG"
    echo -e "\n${BLUE}==================================================${NC}"
    echo -e "${BLUE}  ${title}${NC}"
    echo -e "${BLUE}==================================================${NC}"
}

die() { log_error "$*"; exit 1; }

check_root() {
    [ "$(id -u)" -eq 0 ] || die "Este script deve ser executado como root."
}

check_debian12() {
    if ! grep -q "bookworm\|12" /etc/os-release 2>/dev/null; then
        log_warn "Sistema nao e Debian 12 (bookworm). Continuando mesmo assim..."
    fi
}

# ------------------------------------------------------------------------------
# ETAPA 0 -- DEPENDENCIAS GERAIS
# Instala todos os utilitarios externos usados pelo script em um unico bloco.
# Qualquer comando nao padrao (curl, wget, gpg, etc.) deve ser listado aqui.
# ------------------------------------------------------------------------------
step_dependencies() {
    log_step "ETAPA 0 -- Dependencias Gerais"

    local deps=(
        curl                       # chamadas HTTP/API (Grafana, Zabbix JSON-RPC)
        wget                       # download de pacotes .deb e chaves GPG
        gnupg2                     # gpg --dearmor para chaves de repositorio
        apt-transport-https        # suporte a repositorios HTTPS no APT
        software-properties-common # add-apt-repository
        ca-certificates            # certificados TLS para HTTPS
        python3                    # escrita do zabbix.conf.php sem escape de heredoc
        locales                    # geracao de pt_BR.UTF-8 e en_US.UTF-8
    )

    # Atualiza as listas de pacotes ANTES de qualquer install. Em imagens Debian 12
    # legadas as listas podem estar velhas/expiradas, quebrando o apt-get install.
    log_info "Atualizando listas de pacotes (apt-get update)..."
    DEBIAN_FRONTEND=noninteractive apt-get update >> "$STEP_LOG" 2>&1 \
        || log_warn "apt-get update falhou (repositorios EOL / sem rede?); seguindo com as listas existentes."

    log_info "Verificando e instalando dependencias gerais..."
    DEBIAN_FRONTEND=noninteractive apt-get install -y "${deps[@]}" >> "$STEP_LOG" 2>&1

    # Confirmar presenca dos comandos criticos
    local missing=0
    for cmd in curl wget gpg python3; do
        if command -v "$cmd" > /dev/null 2>&1; then
            log_ok "Comando disponivel: ${cmd}"
        else
            log_error "Comando NAO encontrado apos instalacao: ${cmd}"
            missing=$((missing + 1))
        fi
    done

    [ "$missing" -gt 0 ] && die "Dependencias criticas ausentes. Verifique conectividade e repositorios APT."
    log_ok "Todas as dependencias gerais instaladas."
}

# ------------------------------------------------------------------------------
# ETAPA 1 -- LOCALE
# ------------------------------------------------------------------------------
step_locale() {
    log_step "ETAPA 1 -- Configuracao de Locale (${LANG_LOCALE})"

    # Habilitar locales no /etc/locale.gen
    for loc in "pt_BR.UTF-8 UTF-8" "en_US.UTF-8 UTF-8"; do
        local name="${loc%% *}"
        if ! grep -q "^${name}" /etc/locale.gen 2>/dev/null; then
            echo "$loc" >> /etc/locale.gen
            log_info "Adicionado ao locale.gen: $name"
        else
            log_info "Locale ja presente: $name"
        fi
    done

    locale-gen >> "$STEP_LOG" 2>&1
    log_ok "Locales gerados."

    update-locale LANG="${LANG_LOCALE}" LANGUAGE="${LANG_LOCALE%.*}:${LANG_LOCALE%%_*}" LC_ALL="${LANG_LOCALE}"

    cat > /etc/environment <<EOF
LANG=${LANG_LOCALE}
LANGUAGE=${LANG_LOCALE%.*}:${LANG_LOCALE%%_*}
LC_ALL=${LANG_LOCALE}
EOF

    cat > /etc/profile.d/locale.sh <<EOF
export LANG=${LANG_LOCALE}
export LANGUAGE=${LANG_LOCALE%.*}:${LANG_LOCALE%%_*}
export LC_ALL=${LANG_LOCALE}
EOF

    log_ok "Locale configurado: ${LANG_LOCALE}"
}

# ------------------------------------------------------------------------------
# ETAPA 2 -- REPOSITORIO ZABBIX
# ------------------------------------------------------------------------------
step_zabbix_repo() {
    log_step "ETAPA 2 -- Repositorio Zabbix ${ZABBIX_VERSION}"

    # O repositorio Zabbix usa numero da versao Debian (debian12),
    # NAO o codename (bookworm). Detectar via /etc/os-release.
    local debian_version_id
    debian_version_id=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"')
    [ -n "$debian_version_id" ] || die "Nao foi possivel detectar a versao do Debian (/etc/os-release)."

    local repo_pkg="zabbix-release_latest_${ZABBIX_VERSION}+debian${debian_version_id}_all.deb"
    local repo_url="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/debian/pool/main/z/zabbix-release/${repo_pkg}"

    if dpkg -l zabbix-release 2>/dev/null | grep -q "^ii"; then
        log_info "Repositorio Zabbix ja instalado."
    else
        log_info "Debian detectado: ${debian_version_id} (${DEBIAN_CODENAME})"
        log_info "Pacote: ${repo_pkg}"
        log_info "URL:    ${repo_url}"
        log_info "Baixando repositorio Zabbix ${ZABBIX_VERSION}..."
        wget -q "${repo_url}" -O /tmp/zabbix-release.deb >> "$STEP_LOG" 2>&1 \
            || die "Falha ao baixar o repositorio Zabbix. Verifique a conectividade."
        dpkg -i /tmp/zabbix-release.deb >> "$STEP_LOG" 2>&1
        rm -f /tmp/zabbix-release.deb
        log_ok "Repositorio Zabbix ${ZABBIX_VERSION} instalado."
    fi

    log_info "Atualizando lista de pacotes..."
    apt-get update -qq >> "$STEP_LOG" 2>&1
    log_ok "APT atualizado."
}

# ------------------------------------------------------------------------------
# ETAPA 3 -- POSTGRESQL
# ------------------------------------------------------------------------------
step_postgresql() {
    log_step "ETAPA 3 -- PostgreSQL"

    if dpkg -l postgresql 2>/dev/null | grep -q "^ii"; then
        log_info "PostgreSQL ja instalado."
    else
        log_info "Instalando PostgreSQL..."
        DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql >> "$STEP_LOG" 2>&1
        log_ok "PostgreSQL instalado."
    fi

    systemctl start postgresql >> "$STEP_LOG" 2>&1
    systemctl enable postgresql >> "$STEP_LOG" 2>&1
    systemctl is-active --quiet postgresql && log_ok "PostgreSQL: ativo." \
        || die "PostgreSQL nao iniciou corretamente."
}

# ------------------------------------------------------------------------------
# ETAPA 4 -- ZABBIX SERVER, FRONTEND, AGENT2
# ------------------------------------------------------------------------------
step_zabbix_packages() {
    log_step "ETAPA 4 -- Pacotes Zabbix (Server + Frontend + Agent2)"

    local pkgs=(
        zabbix-server-pgsql
        zabbix-frontend-php
        zabbix-apache-conf
        zabbix-sql-scripts
        zabbix-agent2
        zabbix-agent2-plugin-mongodb
        zabbix-agent2-plugin-ember-plus
        php8.2-pgsql   # CRITICO: sem este, o frontend nao conecta ao PostgreSQL
        php8.2-intl    # CRITICO: sem este, opcoes de idioma ficam inativas no frontend
    )

    log_info "Instalando pacotes Zabbix e extensoes PHP necessarias..."
    DEBIAN_FRONTEND=noninteractive apt-get install -y "${pkgs[@]}" >> "$STEP_LOG" 2>&1
    log_ok "Todos os pacotes instalados."
}

# ------------------------------------------------------------------------------
# ETAPA 5 -- BANCO DE DADOS
# ------------------------------------------------------------------------------
step_database() {
    log_step "ETAPA 5 -- Banco de Dados PostgreSQL"

    # Verificar se usuario ja existe
    if su - postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'\"" 2>/dev/null | grep -q 1; then
        log_info "Usuario '${DB_USER}' ja existe. Atualizando senha..."
        su - postgres -c "psql -c \"ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';\"" >> "$STEP_LOG" 2>&1
    else
        log_info "Criando usuario '${DB_USER}'..."
        su - postgres -c "psql -c \"CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';\"" >> "$STEP_LOG" 2>&1
        log_ok "Usuario '${DB_USER}' criado."
    fi

    # Verificar se banco ja existe
    if su - postgres -c "psql -lqt 2>/dev/null | cut -d'|' -f1 | grep -qw '${DB_NAME}'"; then
        log_info "Banco '${DB_NAME}' ja existe. Pulando criacao."
    else
        log_info "Criando banco '${DB_NAME}'..."
        su - postgres -c "psql -c \"CREATE DATABASE ${DB_NAME} OWNER ${DB_USER} ENCODING UTF8 LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;\"" >> "$STEP_LOG" 2>&1
        log_ok "Banco '${DB_NAME}' criado."

        # ATENCAO: import feito como 'postgres' (nao 'zabbix') pois a
        # autenticacao peer do PostgreSQL nega acesso local ao usuario 'zabbix'.
        # Apos o import, GRANT explicito concedido ao usuario 'zabbix'.
        log_info "Importando schema Zabbix (pode demorar 1-2 min)..."
        zcat /usr/share/zabbix-sql-scripts/postgresql/server.sql.gz \
            | su - postgres -c "psql ${DB_NAME}" >> "$STEP_LOG" 2>&1
        log_ok "Schema importado com sucesso."

        log_info "Concedendo permissoes ao usuario '${DB_USER}'..."
        su - postgres -c "psql ${DB_NAME} -c \"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${DB_USER};\"" >> "$STEP_LOG" 2>&1
        su - postgres -c "psql ${DB_NAME} -c \"GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${DB_USER};\"" >> "$STEP_LOG" 2>&1
        su - postgres -c "psql ${DB_NAME} -c \"GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ${DB_USER};\"" >> "$STEP_LOG" 2>&1
        su - postgres -c "psql ${DB_NAME} -c \"ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${DB_USER};\"" >> "$STEP_LOG" 2>&1
        log_ok "Permissoes concedidas ao usuario '${DB_USER}'."
    fi
}

# ------------------------------------------------------------------------------
# ETAPA 6 -- CONFIGURAR ZABBIX SERVER
# ------------------------------------------------------------------------------
step_zabbix_server_conf() {
    log_step "ETAPA 6 -- Configuracao do Zabbix Server"

    local conf="/etc/zabbix/zabbix_server.conf"
    [ -f "$conf" ] || die "Arquivo $conf nao encontrado."

    # DBPassword pode estar comentada como '# DBPassword=' ou ausente
    if grep -q "^DBPassword=" "$conf"; then
        sed -i "s|^DBPassword=.*|DBPassword=${DB_PASS}|" "$conf"
        log_info "DBPassword atualizado."
    elif grep -q "^# DBPassword=" "$conf"; then
        sed -i "s|^# DBPassword=.*|DBPassword=${DB_PASS}|" "$conf"
        log_ok "DBPassword definido (era comentado)."
    else
        echo "DBPassword=${DB_PASS}" >> "$conf"
        log_ok "DBPassword adicionado ao conf."
    fi

    local db_name db_user
    db_name=$(grep "^DBName=" "$conf" | cut -d= -f2)
    db_user=$(grep "^DBUser=" "$conf" | cut -d= -f2)
    log_ok "zabbix_server.conf: DBName=${db_name}, DBUser=${db_user}, DBPassword=***"
}

# ------------------------------------------------------------------------------
# ETAPA 7 -- PHP: INI E EXTENSOES
# ------------------------------------------------------------------------------
step_php_config() {
    log_step "ETAPA 7 -- Configuracao PHP para Zabbix"

    local php_ini="/etc/php/8.2/apache2/php.ini"
    [ -f "$php_ini" ] || die "php.ini do Apache nao encontrado em $php_ini"

    # Parametros minimos exigidos pelo Zabbix 7.0
    declare -A PHP_SETTINGS=(
        ["max_execution_time"]="300"
        ["max_input_time"]="300"
        ["memory_limit"]="256M"
        ["post_max_size"]="32M"
        ["upload_max_filesize"]="32M"
    )

    for key in "${!PHP_SETTINGS[@]}"; do
        val="${PHP_SETTINGS[$key]}"
        if grep -q "^${key} = " "$php_ini"; then
            sed -i "s|^${key} = .*|${key} = ${val}|" "$php_ini"
        else
            echo "${key} = ${val}" >> "$php_ini"
        fi
        log_info "PHP: ${key} = ${val}"
    done

    # Timezone -- pode estar comentada com ; ou ausente
    if grep -q "^date.timezone = " "$php_ini"; then
        sed -i "s|^date.timezone = .*|date.timezone = ${TIMEZONE}|" "$php_ini"
    elif grep -q "^;date.timezone" "$php_ini"; then
        sed -i "s|^;date.timezone.*|date.timezone = ${TIMEZONE}|" "$php_ini"
    else
        echo "date.timezone = ${TIMEZONE}" >> "$php_ini"
    fi
    log_ok "PHP: date.timezone = ${TIMEZONE}"

    # Verificar extensoes obrigatorias no SAPI do Apache (mod_php), que e o que o
    # frontend do Zabbix realmente usa. O script NAO instala o php CLI, entao usar
    # `php -m` daria falso "AUSENTE" para todas. Aqui checamos os modulos habilitados
    # em conf.d e os built-in do core (json/openssl/session/zlib/ctype ja compilados).
    local apache_confd="/etc/php/8.2/apache2/conf.d"
    local php_builtin="ctype json openssl session zlib"
    local missing=0
    for ext in bcmath ctype gd gettext json ldap mbstring pgsql openssl session sockets xmlreader xmlwriter zlib curl intl; do
        if ls "${apache_confd}"/*"${ext}".ini >/dev/null 2>&1 \
           || printf '%s\n' $php_builtin | grep -qx "$ext"; then
            log_ok "PHP ext: ${ext}"
        else
            log_warn "PHP ext AUSENTE: ${ext}"
            missing=$((missing + 1))
        fi
    done

    [ "$missing" -gt 0 ] && log_warn "${missing} extensao(oes) PHP ausente(s). Verifique acima." \
        || log_ok "Todas as extensoes PHP presentes."
}

# ------------------------------------------------------------------------------
# ETAPA 8 -- APACHE: LOCALE E CONF DO ZABBIX
# ------------------------------------------------------------------------------
step_apache_config() {
    log_step "ETAPA 8 -- Configuracao Apache2"

    # 8a. Corrigir LANG no envvars
    # CRITICO: /etc/apache2/envvars define LANG=C por padrao, o que faz o PHP
    # nao reconhecer pt_BR.UTF-8 e deixar a opcao de idioma inativa no frontend.
    local envvars="/etc/apache2/envvars"
    if grep -q "^export LANG=C$" "$envvars"; then
        sed -i "s|^export LANG=C$|export LANG=${LANG_LOCALE}|" "$envvars"
        log_ok "Apache envvars: LANG corrigido de C para ${LANG_LOCALE}"
    elif grep -q "^export LANG=" "$envvars"; then
        sed -i "s|^export LANG=.*|export LANG=${LANG_LOCALE}|" "$envvars"
        log_info "Apache envvars: LANG atualizado para ${LANG_LOCALE}"
    else
        echo "export LANG=${LANG_LOCALE}" >> "$envvars"
        log_info "Apache envvars: LANG adicionado."
    fi

    # 8b. Reescrever conf do Zabbix para cobrir PHP 8.x
    # O conf padrao usa <IfModule mod_php.c> e <IfModule mod_php7.c>.
    # PHP 8.2 com libapache2-mod-php registra-se como 'php_module'.
    local zabbix_apache="/etc/apache2/conf-available/zabbix.conf"

    cat > "$zabbix_apache" <<APACHECONF
# Define /zabbix alias
<IfModule mod_alias.c>
    Alias /zabbix /usr/share/zabbix
</IfModule>

<Directory "/usr/share/zabbix">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all

    <IfModule mod_php.c>
        php_value max_execution_time 300
        php_value memory_limit 256M
        php_value post_max_size 32M
        php_value upload_max_filesize 32M
        php_value max_input_time 300
        php_value max_input_vars 10000
        php_value always_populate_raw_post_data -1
        php_value date.timezone ${TIMEZONE}
    </IfModule>

    <IfModule mod_php7.c>
        php_value max_execution_time 300
        php_value memory_limit 256M
        php_value post_max_size 32M
        php_value upload_max_filesize 32M
        php_value max_input_time 300
        php_value max_input_vars 10000
        php_value always_populate_raw_post_data -1
        php_value date.timezone ${TIMEZONE}
    </IfModule>

    <IfModule php_module>
        php_value max_execution_time 300
        php_value memory_limit 256M
        php_value post_max_size 32M
        php_value upload_max_filesize 32M
        php_value max_input_time 300
        php_value max_input_vars 10000
        php_value always_populate_raw_post_data -1
        php_value date.timezone ${TIMEZONE}
    </IfModule>
</Directory>

<Directory "/usr/share/zabbix/conf">
    Order deny,allow
    Deny from all
    <files *.php>
        Order deny,allow
        Deny from all
    </files>
</Directory>

<Directory "/usr/share/zabbix/app">
    Order deny,allow
    Deny from all
    <files *.php>
        Order deny,allow
        Deny from all
    </files>
</Directory>

<Directory "/usr/share/zabbix/include">
    Order deny,allow
    Deny from all
    <files *.php>
        Order deny,allow
        Deny from all
    </files>
</Directory>

<Directory "/usr/share/zabbix/local">
    Order deny,allow
    Deny from all
    <files *.php>
        Order deny,allow
        Deny from all
    </files>
</Directory>

<Directory "/usr/share/zabbix/vendor">
    Order deny,allow
    Deny from all
    <files *.php>
        Order deny,allow
        Deny from all
    </files>
</Directory>
APACHECONF

    log_ok "Apache conf do Zabbix reescrito com suporte a PHP 8.x e timezone."

    if [ ! -L "/etc/apache2/conf-enabled/zabbix.conf" ]; then
        a2enconf zabbix >> "$STEP_LOG" 2>&1
        log_info "a2enconf zabbix executado."
    fi

    local apache_test
    apache_test=$(apache2ctl configtest 2>&1 | grep -v "ServerName" | grep -v "^$" || true)
    log_info "Apache configtest: ${apache_test}"
    echo "$apache_test" >> "$STEP_LOG"
}

# ------------------------------------------------------------------------------
# ETAPA 9 -- ZABBIX.CONF.PHP (interface web)
# ------------------------------------------------------------------------------
step_zabbix_web_conf() {
    log_step "ETAPA 9 -- Arquivo zabbix.conf.php (frontend)"

    local web_conf_dir="/etc/zabbix/web"
    local web_conf="${web_conf_dir}/zabbix.conf.php"
    local share_conf="/usr/share/zabbix/conf/zabbix.conf.php"

    mkdir -p "$web_conf_dir"
    chown www-data:www-data "$web_conf_dir"

    log_info "Escrevendo zabbix.conf.php via Python3..."

    # CRITICO: usar Python3 para escrever o arquivo PHP.
    # Heredoc bash com \\\$ produz \$DB no arquivo PHP causando
    # "unexpected token \". Com Python3 e string concatenation
    # nao ha ambiguidade de escape bash/Python/PHP.
    python3 << PYEOF
import sys

db_name  = "${DB_NAME}"
db_user  = "${DB_USER}"
db_pass  = "${DB_PASS}"
zbx_name = "${ZBX_SERVER_NAME}"

content = (
    "<?php\n"
    "// Zabbix GUI configuration file.\n"
    "\n"
    "\$DB['TYPE']                     = 'POSTGRESQL';\n"
    "\$DB['SERVER']                   = 'localhost';\n"
    "\$DB['PORT']                     = '5432';\n"
    "\$DB['DATABASE']                 = '" + db_name + "';\n"
    "\$DB['USER']                     = '" + db_user + "';\n"
    "\$DB['PASSWORD']                 = '" + db_pass + "';\n"
    "\$DB['SCHEMA']                   = '';\n"
    "\$DB['ENCRYPTION']               = false;\n"
    "\$DB['KEY_FILE']                 = '';\n"
    "\$DB['CERT_FILE']                = '';\n"
    "\$DB['CA_FILE']                  = '';\n"
    "\$DB['VERIFY_HOST']              = false;\n"
    "\$DB['CIPHER_LIST']              = '';\n"
    "\$DB['VAULT_URL']                = '';\n"
    "\$DB['VAULT_DB_PATH']            = '';\n"
    "\$DB['VAULT_TOKEN']              = '';\n"
    "\$DB['VAULT_CERT_FILE']          = '';\n"
    "\$DB['VAULT_KEY_FILE']           = '';\n"
    "\$DB['VAULT_CACHE']              = false;\n"
    "\$DB['DOUBLE_IEEE754']           = true;\n"
    "\n"
    "\$ZBX_SERVER                     = 'localhost';\n"
    "\$ZBX_SERVER_PORT                = '10051';\n"
    "\$ZBX_SERVER_NAME                = '" + zbx_name + "';\n"
    "\n"
    "\$IMAGE_FORMAT_DEFAULT           = IMAGE_FORMAT_PNG;\n"
    "\n"
    "\$HISTORY_STORAGE_URL            = '';\n"
    "\$HISTORY_STORAGE_TYPES          = [];\n"
    "\n"
    "\$ALLOW_HTTP_AUTH                = false;\n"
)

with open("${web_conf}", "w") as f:
    f.write(content)

print("zabbix.conf.php escrito com sucesso.")
sys.exit(0)
PYEOF

    local py_exit=$?
    [ $py_exit -ne 0 ] && die "Python3 falhou ao escrever zabbix.conf.php (exit ${py_exit})"

    chown www-data:www-data "$web_conf"
    chmod 640 "$web_conf"

    log_info "Conteudo do zabbix.conf.php gerado:"
    cat "$web_conf" >> "$STEP_LOG"

    # Validar sintaxe PHP -- usa exit code (nao grep no output).
    # ATENCAO: "No syntax errors detected" contem "syntax error"
    # como substring -- grep causaria falso positivo.
    log_info "Validando sintaxe PHP..."
    local php_lint
    php_lint=$(php -l "$web_conf" 2>&1)
    local php_exit=$?
    echo "$php_lint" >> "$STEP_LOG"
    if [ $php_exit -ne 0 ]; then
        log_error "Erro de sintaxe PHP detectado:"
        log_error "$php_lint"
        die "Erro de sintaxe no zabbix.conf.php"
    fi
    log_ok "Sintaxe PHP: OK -- ${php_lint}"

    # Garantir symlink no diretorio do share (onde o frontend busca)
    if [ ! -L "$share_conf" ] && [ ! -f "$share_conf" ]; then
        ln -s "$web_conf" "$share_conf"
        log_info "Symlink criado: ${share_conf} -> ${web_conf}"
    elif [ -L "$share_conf" ]; then
        log_info "Symlink ja existe: ${share_conf}"
    fi

    log_ok "zabbix.conf.php criado e validado."
}

# ------------------------------------------------------------------------------
# ETAPA 10 -- INICIAR E HABILITAR SERVICOS
# ------------------------------------------------------------------------------
step_services() {
    log_step "ETAPA 10 -- Servicos"

    local services=(postgresql zabbix-server zabbix-agent2 apache2)

    for svc in "${services[@]}"; do
        log_info "Reiniciando ${svc}..."
        systemctl restart "$svc" >> "$STEP_LOG" 2>&1
        systemctl enable "$svc" >> "$STEP_LOG" 2>&1
        if systemctl is-active --quiet "$svc"; then
            log_ok "${svc}: ativo"
        else
            log_error "${svc}: FALHOU ao iniciar"
            journalctl -u "$svc" --no-pager -n 20 >> "$STEP_LOG" 2>&1
            journalctl -u "$svc" --no-pager -n 10 >&2
        fi
    done
}

# ------------------------------------------------------------------------------
# ETAPA 11 -- GRAFANA + INTEGRACAO COM ZABBIX VIA API TOKEN
# ------------------------------------------------------------------------------
step_grafana() {
    log_step "ETAPA 11 -- Grafana + Integracao Zabbix via API Token"

    # 11a. Repositorio Grafana stable
    if dpkg -l grafana 2>/dev/null | grep -q "^ii"; then
        log_info "Grafana ja instalado: $(grafana-server --version 2>/dev/null | head -1)"
    else
        log_info "Adicionando repositorio Grafana stable..."

        mkdir -p /etc/apt/keyrings
        wget -q -O - https://apt.grafana.com/gpg.key \
            | gpg --dearmor > /etc/apt/keyrings/grafana.gpg \
            || die "Falha ao baixar chave GPG do Grafana."

        echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" \
            > /etc/apt/sources.list.d/grafana.list

        apt-get update -qq >> "$STEP_LOG" 2>&1
        DEBIAN_FRONTEND=noninteractive apt-get install -y grafana >> "$STEP_LOG" 2>&1
        log_ok "Grafana instalado: $(grafana-server --version 2>/dev/null | head -1)"
    fi

    # 11b. Plugin Zabbix para Grafana
    if [ -d "/var/lib/grafana/plugins/alexanderzobnin-zabbix-app" ]; then
        log_info "Plugin alexanderzobnin-zabbix-app ja instalado."
    else
        log_info "Instalando plugin Zabbix para Grafana..."
        systemctl stop grafana-server 2>/dev/null || true
        grafana-cli --homepath /usr/share/grafana plugins install alexanderzobnin-zabbix-app >> "$STEP_LOG" 2>&1
        # grafana-cli instala como root; Grafana precisa de grafana:grafana
        chown -R grafana:grafana /var/lib/grafana/plugins/ >> "$STEP_LOG" 2>&1
        log_ok "Plugin alexanderzobnin-zabbix-app instalado."
    fi

    # 11c. Configurar grafana.ini
    local grafana_ini="/etc/grafana/grafana.ini"

    if grep -q "^;*\s*timezone\s*=" "$grafana_ini"; then
        sed -i "s|^;*\s*\(timezone\s*=\).*|\1 ${TIMEZONE}|" "$grafana_ini"
    fi

    if grep -q "^\[plugins\]" "$grafana_ini"; then
        if grep -q "allow_loading_unsigned_plugins" "$grafana_ini"; then
            sed -i "s|^;*allow_loading_unsigned_plugins.*|allow_loading_unsigned_plugins = alexanderzobnin-zabbix-datasource|" "$grafana_ini"
        else
            sed -i "/^\[plugins\]/a allow_loading_unsigned_plugins = alexanderzobnin-zabbix-datasource" "$grafana_ini"
        fi
    fi

    log_ok "grafana.ini configurado."

    # 11d. Iniciar Grafana
    systemctl enable grafana-server >> "$STEP_LOG" 2>&1
    systemctl restart grafana-server >> "$STEP_LOG" 2>&1

    log_info "Aguardando Grafana iniciar (max 60s)..."
    local ready=0
    for i in $(seq 1 60); do
        if curl -sf http://localhost:3000/api/health > /dev/null 2>&1; then
            log_ok "Grafana pronto em ${i}s."
            ready=1
            break
        fi
        sleep 1
    done
    [ "$ready" -eq 1 ] || die "Grafana nao respondeu apos 60s. Verifique: journalctl -u grafana-server"

    # 11e. Habilitar plugin Zabbix no Grafana via API
    local plugin_resp
    plugin_resp=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
        -X POST http://localhost:3000/api/plugins/alexanderzobnin-zabbix-app/settings \
        -H "Content-Type: application/json" \
        -d '{"enabled":true,"pinned":true,"jsonData":{}}' 2>/dev/null || echo "error")

    echo "Plugin resp: $plugin_resp" >> "$STEP_LOG"
    echo "$plugin_resp" | grep -q "Plugin settings updated" \
        && log_ok "Plugin Zabbix habilitado no Grafana." \
        || log_warn "Resposta ao habilitar plugin: $plugin_resp"

    # 11f. Criar API Token no Zabbix via JSON-RPC
    # A integracao usa API Token, NAO usuario/senha diretamente.
    local zbx_url="http://localhost/zabbix/api_jsonrpc.php"

    log_info "Autenticando na API Zabbix como ${ZBX_ADMIN_USER}..."
    local auth_token
    auth_token=$(curl -sf -X POST "$zbx_url" \
        -H "Content-Type: application/json-rpc" \
        -d "{\"jsonrpc\":\"2.0\",\"method\":\"user.login\",\"params\":{\"username\":\"${ZBX_ADMIN_USER}\",\"password\":\"${ZBX_ADMIN_PASS}\"},\"id\":1}" \
        | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',''))" 2>/dev/null) \
        || die "Falha ao autenticar na API Zabbix."

    [ -n "$auth_token" ] || die "API Zabbix retornou token vazio. Verifique credenciais ZBX_ADMIN_USER/ZBX_ADMIN_PASS."
    log_ok "Autenticado na API Zabbix."

    # Verificar se ja existe token 'grafana-integration'
    local existing_token_id
    existing_token_id=$(curl -sf -X POST "$zbx_url" \
        -H "Content-Type: application/json-rpc" \
        -d "{\"jsonrpc\":\"2.0\",\"method\":\"token.get\",\"params\":{\"output\":[\"tokenid\",\"name\"],\"filter\":{\"name\":\"grafana-integration\"}},\"auth\":\"$auth_token\",\"id\":2}" \
        | python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',[]); print(r[0]['tokenid'] if r else '')" 2>/dev/null)

    local api_token
    if [ -n "$existing_token_id" ]; then
        log_info "Token 'grafana-integration' ja existe (ID=${existing_token_id}). Regenerando..."
        api_token=$(curl -sf -X POST "$zbx_url" \
            -H "Content-Type: application/json-rpc" \
            -d "{\"jsonrpc\":\"2.0\",\"method\":\"token.generate\",\"params\":[\"${existing_token_id}\"],\"auth\":\"$auth_token\",\"id\":3}" \
            | python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['token'])" 2>/dev/null) \
            || die "Falha ao regenerar token Zabbix."
    else
        log_info "Criando API Token 'grafana-integration' no Zabbix..."
        local token_id
        token_id=$(curl -sf -X POST "$zbx_url" \
            -H "Content-Type: application/json-rpc" \
            -d "{\"jsonrpc\":\"2.0\",\"method\":\"token.create\",\"params\":{\"name\":\"grafana-integration\",\"userid\":\"1\",\"expires_at\":0},\"auth\":\"$auth_token\",\"id\":3}" \
            | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['tokenids'][0])" 2>/dev/null) \
            || die "Falha ao criar token Zabbix."

        api_token=$(curl -sf -X POST "$zbx_url" \
            -H "Content-Type: application/json-rpc" \
            -d "{\"jsonrpc\":\"2.0\",\"method\":\"token.generate\",\"params\":[\"${token_id}\"],\"auth\":\"$auth_token\",\"id\":4}" \
            | python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['token'])" 2>/dev/null) \
            || die "Falha ao gerar valor do token Zabbix."
    fi

    [ -n "$api_token" ] || die "Token Zabbix vazio -- verifique a API Zabbix."
    log_ok "API Token Zabbix gerado: ${api_token:0:12}..."

    echo "$api_token" > /root/.zabbix_grafana_token
    chmod 600 /root/.zabbix_grafana_token

    # 11g. Configurar datasource Zabbix no Grafana via API
    local ds_exists
    ds_exists=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
        "http://localhost:3000/api/datasources/name/Zabbix" 2>/dev/null | python3 -c \
        "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || echo "")

    local ds_payload
    ds_payload="{
        \"name\": \"Zabbix\",
        \"type\": \"alexanderzobnin-zabbix-datasource\",
        \"access\": \"proxy\",
        \"url\": \"http://localhost/zabbix/api_jsonrpc.php\",
        \"basicAuth\": false,
        \"isDefault\": true,
        \"jsonData\": {
            \"authType\": \"token\",
            \"trends\": true,
            \"trendsFrom\": \"7d\",
            \"trendsRange\": \"4d\",
            \"cachingTTL\": \"600\",
            \"timeout\": 30000,
            \"dbConnectionEnable\": false
        },
        \"secureJsonData\": {
            \"apiToken\": \"${api_token}\"
        }
    }"

    local ds_resp
    if [ -n "$ds_exists" ]; then
        log_info "Datasource 'Zabbix' ja existe (ID=${ds_exists}). Atualizando..."
        ds_resp=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
            -X PUT "http://localhost:3000/api/datasources/${ds_exists}" \
            -H "Content-Type: application/json" \
            -d "$ds_payload" 2>/dev/null)
    else
        log_info "Criando datasource 'Zabbix' no Grafana..."
        ds_resp=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
            -X POST "http://localhost:3000/api/datasources" \
            -H "Content-Type: application/json" \
            -d "$ds_payload" 2>/dev/null)
    fi

    echo "DS resp: $ds_resp" >> "$STEP_LOG"
    echo "$ds_resp" | grep -q "Datasource\|datasource" \
        && log_ok "Datasource Zabbix configurado no Grafana com API Token." \
        || log_warn "Resposta inesperada ao configurar datasource: $ds_resp"

    # 11h. Testar health do datasource
    local ds_id
    ds_id=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
        "http://localhost:3000/api/datasources/name/Zabbix" 2>/dev/null \
        | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")

    if [ -n "$ds_id" ]; then
        local health
        health=$(curl -sf -u "admin:${GRAFANA_ADMIN_PASS}" \
            "http://localhost:3000/api/datasources/${ds_id}/health" 2>/dev/null \
            | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','?'), '-', d.get('message',''))" 2>/dev/null \
            || echo "health check falhou")
        log_ok "Grafana datasource health: ${health}"
    fi

    log_ok "Grafana integrado ao Zabbix via API Token."
    log_info "Acesso Grafana: http://$(hostname -I | awk '{print $1}'):3000  (admin / ${GRAFANA_ADMIN_PASS})"
}

# ------------------------------------------------------------------------------
# ETAPA 12 -- VERIFICACAO FINAL
# ------------------------------------------------------------------------------
step_verify() {
    log_step "ETAPA 12 -- Verificacao Final"

    local errors=0

    # Servicos
    echo ""
    log_info "-- Servicos --"
    for svc in postgresql zabbix-server zabbix-agent2 apache2 grafana-server; do
        if systemctl is-active --quiet "$svc"; then
            log_ok "${svc}: ativo"
        else
            log_error "${svc}: INATIVO"
            journalctl -u "$svc" --no-pager -n 20 >> "$STEP_LOG" 2>&1
            errors=$((errors + 1))
        fi
    done

    # Portas
    echo ""
    log_info "-- Portas --"
    declare -A PORTS=(
        ["10051"]="Zabbix Server"
        ["10050"]="Zabbix Agent2"
        ["80"]="Apache2/Frontend"
        ["5432"]="PostgreSQL"
        ["3000"]="Grafana"
    )
    for port in "${!PORTS[@]}"; do
        # systemctl restart retorna quando a unit fica "active", mas o socket
        # pode levar alguns segundos para dar bind. Espera ate ~30s por porta.
        local tries=0 aberta=0
        while [ "$tries" -lt 15 ]; do
            if ss -tln | grep -Eq ":${port}([[:space:]]|$)"; then
                aberta=1
                break
            fi
            tries=$((tries + 1))
            sleep 2
        done
        if [ "$aberta" -eq 1 ]; then
            log_ok "Porta ${port}/tcp (${PORTS[$port]}): ABERTA"
        else
            log_error "Porta ${port}/tcp (${PORTS[$port]}): FECHADA"
            errors=$((errors + 1))
        fi
    done

    # Extensoes PHP
    echo ""
    log_info "-- Extensoes PHP --"
    local ext_errors=0
    local apache_confd="/etc/php/8.2/apache2/conf.d"
    local php_cli_mods=""
    if command -v php >/dev/null 2>&1; then
        php_cli_mods="$(php -m 2>>"$STEP_LOG")"
    else
        log_warn "php CLI ausente; validando extensoes apenas pelos modulos do Apache."
    fi
    for ext in bcmath ctype gd gettext json ldap mbstring pgsql openssl session sockets xmlreader xmlwriter zlib curl intl; do
        # Presente se listado pelo CLI OU habilitado no SAPI do Apache (conf.d),
        # que e o que o frontend do Zabbix realmente usa.
        if printf '%s\n' "$php_cli_mods" | grep -qi "^${ext}$" \
           || ls "${apache_confd}"/*"${ext}".ini >/dev/null 2>&1; then
            :
        else
            log_error "PHP ext AUSENTE (Apache SAPI): ${ext}"
            ext_errors=$((ext_errors+1))
        fi
    done
    [ "$ext_errors" -eq 0 ] && log_ok "Todas as extensoes PHP presentes (16/16)."
    errors=$((errors + ext_errors))

    # PHP INI
    echo ""
    log_info "-- PHP INI (Apache) --"
    local php_ini="/etc/php/8.2/apache2/php.ini"
    for key in max_execution_time max_input_time memory_limit post_max_size upload_max_filesize date.timezone; do
        local val
        val=$(grep "^${key} = " "$php_ini" 2>/dev/null | awk '{print $3}')
        log_info "  ${key} = ${val}"
    done

    # Banco de dados
    echo ""
    log_info "-- Banco de Dados --"
    local tabelas
    tabelas=$(su - postgres -c "psql ${DB_NAME} -tAc \"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';\"" 2>/dev/null || echo "0")
    if [ "$tabelas" -gt 100 ] 2>/dev/null; then
        log_ok "Banco '${DB_NAME}': ${tabelas} tabelas (schema OK)"
    else
        log_error "Banco com poucas ou nenhuma tabela: ${tabelas}"
        errors=$((errors + 1))
    fi

    # Frontend HTTP
    echo ""
    log_info "-- Frontend Web --"
    local http_code
    http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/zabbix/ 2>/dev/null || echo "000")
    if [ "$http_code" = "200" ] || [ "$http_code" = "302" ]; then
        log_ok "Frontend HTTP: ${http_code} OK"
    else
        log_error "Frontend HTTP: ${http_code} -- verifique /var/log/apache2/error.log"
        errors=$((errors + 1))
    fi

    # Resultado
    echo ""
    echo -e "${BLUE}==================================================${NC}"
    if [ "$errors" -eq 0 ]; then
        echo -e "${GREEN}  INSTALACAO CONCLUIDA COM SUCESSO!${NC}"
    else
        echo -e "${RED}  INSTALACAO CONCLUIDA COM ${errors} ERRO(S). Revise acima.${NC}"
    fi
    echo -e "${BLUE}==================================================${NC}"
    echo ""
    local server_ip
    server_ip=$(hostname -I | awk '{print $1}')
    echo -e "  Zabbix frontend: ${GREEN}http://${server_ip}/zabbix${NC}"
    echo -e "    Usuario:       ${YELLOW}Admin${NC} / Senha: ${YELLOW}zabbix${NC}  <-- ${RED}ALTERE!${NC}"
    echo -e "  Grafana:         ${GREEN}http://${server_ip}:3000${NC}"
    echo -e "    Usuario:       ${YELLOW}admin${NC} / Senha: ${YELLOW}${GRAFANA_ADMIN_PASS}${NC}  <-- ${RED}ALTERE!${NC}"
    echo -e "  Token Zabbix:    ${YELLOW}/root/.zabbix_grafana_token${NC}"
    echo -e "  Logs:            ${YELLOW}${LOG_DIR}/${NC}"
    echo ""

    return "$errors"
}

# ------------------------------------------------------------------------------
# MAIN
# ------------------------------------------------------------------------------
main() {
    echo -e "${BLUE}"
    echo "  +==================================================+"
    echo "  |  Zabbix ${ZABBIX_VERSION} + PostgreSQL + Apache2 + Grafana    |"
    echo "  |  Debian 12 (Bookworm)                          |"
    echo "  +==================================================+"
    echo -e "${NC}"

    check_root
    check_debian12

    # Inicializar sistema de logs
    mkdir -p "$LOG_DIR"
    MASTER_LOG="${LOG_DIR}/install-$(date '+%Y%m%d-%H%M%S').log"
    STEP_LOG="$MASTER_LOG"
    {
        echo "========================================"
        echo "  Inicio: $(date '+%Y-%m-%d %H:%M:%S')"
        echo "  Script: ${BASH_SOURCE[0]}"
        echo "  Logs:   ${LOG_DIR}/"
        echo "========================================"
    } >> "$MASTER_LOG"
    log_info "Logs sendo gravados em: ${LOG_DIR}/"

    step_dependencies
    step_locale
    step_zabbix_repo
    step_postgresql
    step_zabbix_packages
    step_database
    step_zabbix_server_conf
    step_php_config
    step_apache_config
    step_zabbix_web_conf
    step_services
    step_grafana
    step_verify

    {
        echo "========================================"
        echo "  Fim: $(date '+%Y-%m-%d %H:%M:%S')"
        echo "========================================"
    } >> "$MASTER_LOG"
}

main "$@"
