#!/bin/bash

# ============================================================================
# DNSBlock Agent - Script de Instalação e Desinstalação
# ============================================================================
# Este script realiza a instalação ou desinstalação completa do DNSBlock Agent
# Requer privilégios de root para execução
#
# Uso:
#   ./install.sh              Instalação interativa
#   ./install.sh uninstall    Desinstalação
# ============================================================================

set -e

# Diretórios e arquivos
INSTALL_DIR="/opt/dnsblock"
RPZ_DIR="$INSTALL_DIR/rpz"
SERVICE_FILE="/etc/systemd/system/dnsblock-agent.service"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Configurações do Unbound
UNBOUND_CONF_DIR="/etc/unbound/unbound.conf.d"
UNBOUND_MAIN_CONF="/etc/unbound/unbound.conf"
DNSBLOCK_UNBOUND_CONF="$INSTALL_DIR/unbound.conf"
DNSBLOCK_INCLUDE='include: "/opt/dnsblock/unbound.conf"'

# Variáveis globais
SERIAL=""
NO_SERIAL=false
UNBOUND_ERROR=""

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

# Verificar se está executando como root
verificar_root() {
    if [ "$(id -u)" -ne 0 ]; then
        whiptail --title "Erro" --msgbox "Este script deve ser executado como root.\n\nExecute novamente com sudo." 10 50
        exit 1
    fi
}

# Verificar dependências
verificar_dependencias() {
    local deps_faltando=""
    
    if ! command -v whiptail &> /dev/null; then
        echo "Erro: whiptail não encontrado. Instale-o antes de continuar."
        exit 1
    fi
    
    if ! command -v unbound-checkconf &> /dev/null; then
        deps_faltando="$deps_faltando unbound-checkconf"
    fi
    
    if [ -n "$deps_faltando" ]; then
        whiptail --title "Dependências Faltando" \
            --msgbox "As seguintes dependências não foram encontradas:\n$deps_faltando\n\nPor favor, instale-as antes de continuar." 12 55
        exit 1
    fi
}

# ============================================================================
# FUNÇÕES DE SERIAL
# ============================================================================

# Solicitar o serial via whiptail
solicitar_serial() {
    local input=""
    
    input=$(whiptail --title "DNSBlock Agent - Ativação" \
        --inputbox "Digite o serial de ativação do agente:\n\n(Deixe em branco para instalar sem serial)" \
        12 60 "" 3>&1 1>&2 2>&3)
    
    local status=$?
    
    if [ $status -ne 0 ]; then
        whiptail --title "Instalação Cancelada" \
            --msgbox "A instalação foi cancelada pelo usuário." 8 45
        exit 0
    fi
    
    if [ -z "$input" ]; then
        if whiptail --title "Instalação sem Serial" \
            --yesno "Você não informou um serial.\n\nDeseja instalar sem serial?\n\n(O serviço será ativado mas não inicializado)\n\nVocê pode obter seu serial em:\nhttp://painel.dnsblock.com.br" \
            14 55; then
            NO_SERIAL=true
            SERIAL=""
        else
            whiptail --title "Instalação Cancelada" \
                --msgbox "A instalação foi cancelada pelo usuário." 8 45
            exit 0
        fi
    else
        SERIAL="$input"
    fi
}

# Atualizar o serial no config.json
atualizar_config_serial() {
    local serial="$1"
    local config_file="$INSTALL_DIR/config.json"
    
    if [ -f "$config_file" ] && [ -n "$serial" ]; then
        sed -i "s/\"serial_key\":[[:space:]]*\"[^\"]*\"/\"serial_key\": \"$serial\"/" "$config_file"
    fi
}

# ============================================================================
# FUNÇÕES DE CONFIGURAÇÃO DO UNBOUND
# ============================================================================

# Encontrar arquivo de configuração do Unbound para adicionar o include
encontrar_arquivo_config_unbound() {
    # Prioridade: arquivo com RPZ existente > local.conf > unbound.conf principal
    
    # Primeiro, verificar se existe algum arquivo com bloco RPZ
    if [ -d "$UNBOUND_CONF_DIR" ]; then
        for conf in "$UNBOUND_CONF_DIR"/*.conf; do
            if [ -f "$conf" ] && grep -q "^[[:space:]]*rpz:" "$conf" 2>/dev/null; then
                echo "$conf"
                return 0
            fi
        done
        
        # Se existe local.conf, usar ele
        if [ -f "$UNBOUND_CONF_DIR/local.conf" ]; then
            echo "$UNBOUND_CONF_DIR/local.conf"
            return 0
        fi
    fi
    
    # Verificar arquivo principal
    if [ -f "$UNBOUND_MAIN_CONF" ]; then
        # Verificar se tem bloco RPZ
        if grep -q "^[[:space:]]*rpz:" "$UNBOUND_MAIN_CONF" 2>/dev/null; then
            echo "$UNBOUND_MAIN_CONF"
            return 0
        fi
        # Mesmo sem RPZ, usar o principal
        echo "$UNBOUND_MAIN_CONF"
        return 0
    fi
    
    # Nenhum arquivo encontrado
    return 1
}

# Verificar se o include do DNSBlock já existe
verificar_include_existente() {
    local include_pattern='include:[[:space:]]*"/opt/dnsblock/unbound.conf"'
    
    # Verificar no arquivo principal
    if [ -f "$UNBOUND_MAIN_CONF" ] && grep -qE "$include_pattern" "$UNBOUND_MAIN_CONF" 2>/dev/null; then
        return 0
    fi
    
    # Verificar nos arquivos do diretório conf.d
    if [ -d "$UNBOUND_CONF_DIR" ]; then
        for conf in "$UNBOUND_CONF_DIR"/*.conf; do
            if [ -f "$conf" ] && grep -qE "$include_pattern" "$conf" 2>/dev/null; then
                return 0
            fi
        done
    fi
    
    return 1
}

# Configurar o Unbound para usar o DNSBlock
configurar_unbound() {
    UNBOUND_ERROR=""
    
    # Criar o diretório RPZ
    mkdir -p "$RPZ_DIR"
    chown unbound:unbound "$RPZ_DIR"
    
    # Copiar arquivo de configuração do DNSBlock para o Unbound (já vem no pacote)
    if [ -f "$SCRIPT_DIR/unbound.conf" ]; then
        cp "$SCRIPT_DIR/unbound.conf" "$DNSBLOCK_UNBOUND_CONF"
        chown unbound:unbound "$DNSBLOCK_UNBOUND_CONF"
        chmod 644 "$DNSBLOCK_UNBOUND_CONF"
    fi
    
    # Criar arquivo RPZ vazio se não existir
    local rpz_file="$RPZ_DIR/rpz.dnsblock.zone"
    if [ ! -f "$rpz_file" ]; then
        cat > "$rpz_file" << 'EOF'
$TTL 1H
@   SOA localhost. hostmaster.localhost. (
    1       ; Serial
    1h      ; Refresh
    15m     ; Retry
    30d     ; Expire
    2h      ; Negative Cache TTL
)
    NS localhost.
; RPZ block hosts
EOF
        chown unbound:unbound "$rpz_file"
        chmod 644 "$rpz_file"
    fi
    
    # Verificar se o include já existe
    if verificar_include_existente; then
        # Já configurado, apenas verificar e reiniciar
        if unbound-checkconf 2>&1; then
            systemctl restart unbound 2>/dev/null || true
            return 0
        else
            UNBOUND_ERROR="Configuração do Unbound com erros. Verifique manualmente."
            return 1
        fi
    fi
    
    # Encontrar arquivo de configuração do Unbound
    local config_file
    config_file=$(encontrar_arquivo_config_unbound)
    
    if [ -z "$config_file" ]; then
        UNBOUND_ERROR="Não foi possível encontrar arquivo de configuração do Unbound."
        return 1
    fi
    
    # Fazer backup do arquivo
    cp "$config_file" "${config_file}.bak.dnsblock"
    
    # Verificar se existe bloco RPZ no arquivo
    if grep -q "^[[:space:]]*rpz:" "$config_file" 2>/dev/null; then
        # Existe bloco RPZ - adicionar include ANTES do primeiro bloco rpz
        # Usando awk para inserir antes da primeira ocorrência de rpz:
        awk -v include="# DNSBlock\n$DNSBLOCK_INCLUDE\n" '
            /^[[:space:]]*rpz:/ && !inserted {
                print "# DNSBlock"
                print "include: \"/opt/dnsblock/unbound.conf\""
                print ""
                inserted=1
            }
            {print}
        ' "$config_file" > "${config_file}.tmp" && mv "${config_file}.tmp" "$config_file"
    else
        # Não existe bloco RPZ - adicionar include no final do arquivo
        echo "" >> "$config_file"
        echo "# DNSBlock" >> "$config_file"
        echo "$DNSBLOCK_INCLUDE" >> "$config_file"
    fi
    
    # Verificar configuração do Unbound
    local check_output
    check_output=$(unbound-checkconf 2>&1)
    
    if [ $? -ne 0 ]; then
        # Erro na configuração - restaurar backup
        mv "${config_file}.bak.dnsblock" "$config_file"
        UNBOUND_ERROR="Erro na configuração do Unbound:\n$check_output"
        return 1
    fi
    
    # Remover backup
    rm -f "${config_file}.bak.dnsblock"
    
    # Reiniciar o Unbound
    if ! systemctl restart unbound 2>&1; then
        UNBOUND_ERROR="Falha ao reiniciar o Unbound. Verifique o serviço manualmente."
        return 1
    fi
    
    return 0
}

# Remover o include do DNSBlock dos arquivos de configuração
remover_include_unbound() {
    # Remover do arquivo principal
    if [ -f "$UNBOUND_MAIN_CONF" ]; then
        # Criar arquivo temporário sem as linhas do DNSBlock
        grep -v '/opt/dnsblock' "$UNBOUND_MAIN_CONF" | grep -v '^# DNSBlock' > "${UNBOUND_MAIN_CONF}.tmp" 2>/dev/null
        if [ -s "${UNBOUND_MAIN_CONF}.tmp" ]; then
            mv "${UNBOUND_MAIN_CONF}.tmp" "$UNBOUND_MAIN_CONF"
        else
            rm -f "${UNBOUND_MAIN_CONF}.tmp"
        fi
    fi
    
    # Remover dos arquivos do diretório conf.d
    if [ -d "$UNBOUND_CONF_DIR" ]; then
        for conf in "$UNBOUND_CONF_DIR"/*.conf; do
            if [ -f "$conf" ]; then
                # Criar arquivo temporário sem as linhas do DNSBlock
                grep -v '/opt/dnsblock' "$conf" | grep -v '^# DNSBlock' > "${conf}.tmp" 2>/dev/null
                if [ -s "${conf}.tmp" ]; then
                    mv "${conf}.tmp" "$conf"
                else
                    rm -f "${conf}.tmp"
                fi
            fi
        done
    fi
}

# Remover configuração do Unbound
remover_configuracao_unbound() {
    # Remover include dos arquivos de configuração
    remover_include_unbound
    
    # Verificar se a configuração está OK e reiniciar
    if unbound-checkconf &>/dev/null; then
        systemctl restart unbound 2>/dev/null || true
    fi
}

# ============================================================================
# FUNÇÃO DE INSTALAÇÃO
# ============================================================================

instalar() {
    # Verificar dependências
    verificar_dependencias
    
    # Verificar se já está instalado
    if [ -d "$INSTALL_DIR" ]; then
        if ! whiptail --title "DNSBlock já instalado" \
            --yesno "O DNSBlock Agent já está instalado.\n\nDeseja reinstalar?" 10 50; then
            exit 0
        fi
        
        # Parar serviço se estiver rodando
        systemctl stop dnsblock-agent 2>/dev/null || true
        systemctl disable dnsblock-agent 2>/dev/null || true
        rm -rf "$INSTALL_DIR"
        rm -f "$SERVICE_FILE"
    fi
    
    # Exibir progresso - Fase 1: Criar diretórios e copiar arquivos
    {
        echo "5"; echo "# Criando diretórios..."
        mkdir -p "$INSTALL_DIR"
        mkdir -p "$INSTALL_DIR/logs"
        mkdir -p "$INSTALL_DIR/rpz"
        sleep 0.2
        
        echo "15"; echo "# Copiando binário..."
        if [ -f "$SCRIPT_DIR/dnsblock-agent" ]; then
            cp "$SCRIPT_DIR/dnsblock-agent" "$INSTALL_DIR/"
            chmod +x "$INSTALL_DIR/dnsblock-agent"
        else
            echo "ERRO: dnsblock-agent não encontrado!" >&2
            exit 1
        fi
        sleep 0.2
        
        echo "25"; echo "# Criando config.json..."
        if [ -f "$SCRIPT_DIR/config.json.example" ]; then
            cp "$SCRIPT_DIR/config.json.example" "$INSTALL_DIR/config.json"
        else
            echo "ERRO: config.json.example não encontrado!" >&2
            exit 1
        fi
        sleep 0.2
        
        echo "30"; echo "# Copiando configuração do Unbound..."
        if [ -f "$SCRIPT_DIR/unbound.conf" ]; then
            cp "$SCRIPT_DIR/unbound.conf" "$INSTALL_DIR/"
        fi
        sleep 0.2
        
        echo "35"; echo "# Configurando permissões..."
        touch "$INSTALL_DIR/logs/agent.log"
        chown -R unbound:unbound "$INSTALL_DIR"
        chmod 755 "$INSTALL_DIR"
        sleep 0.2
        
        echo "40"
    } | whiptail --title "DNSBlock Agent - Instalação" --gauge "Preparando arquivos..." 8 60 0
    
    # Solicitar serial (fora do gauge para funcionar corretamente)
    solicitar_serial
    
    # Exibir progresso - Fase 2: Configurar serial e serviço
    {
        echo "45"; echo "# Configurando serial..."
        if [ -n "$SERIAL" ]; then
            atualizar_config_serial "$SERIAL"
        fi
        sleep 0.2
        
        echo "55"; echo "# Instalando serviço systemd..."
        if [ -f "$SCRIPT_DIR/dnsblock-agent.service" ]; then
            cp "$SCRIPT_DIR/dnsblock-agent.service" "$SERVICE_FILE"
        else
            echo "ERRO: dnsblock-agent.service não encontrado!" >&2
            exit 1
        fi
        sleep 0.2
        
        echo "65"; echo "# Habilitando serviço..."
        systemctl daemon-reload
        systemctl enable dnsblock-agent 2>/dev/null || true
        sleep 0.2
        
        echo "75"; echo "# Configurando Unbound..."
        configurar_unbound || true
        sleep 0.2
        
        echo "90"; echo "# Finalizando..."
        if [ -n "$SERIAL" ]; then
            systemctl start dnsblock-agent 2>/dev/null || true
        fi
        sleep 0.2
        
        echo "100"
    } | whiptail --title "DNSBlock Agent - Instalação" --gauge "Configurando sistema..." 8 60 45
    
    # Verificar se houve erro no Unbound
    if [ -n "$UNBOUND_ERROR" ]; then
        whiptail --title "Aviso - Configuração do Unbound" \
            --msgbox "Houve um problema ao configurar o Unbound:\n\n$UNBOUND_ERROR\n\nO agente foi instalado, mas você precisará configurar o Unbound manualmente." 14 65
    fi
    
    # Exibir mensagem final
    if [ -n "$SERIAL" ]; then
        whiptail --title "Instalação Concluída" \
            --msgbox "O DNSBlock Agent foi instalado e iniciado com sucesso!\n\nComandos úteis:\n  • Status: systemctl status dnsblock-agent\n  • Logs: journalctl -u dnsblock-agent -f\n  • Reiniciar: systemctl restart dnsblock-agent" 14 60
    else
        whiptail --title "Instalação Concluída (sem serial)" \
            --msgbox "O DNSBlock Agent foi instalado mas NÃO foi iniciado.\n\nPara ativar o agente:\n\n1. Obtenha seu serial em:\n   http://painel.dnsblock.com.br\n\n2. Edite o arquivo:\n   $INSTALL_DIR/config.json\n\n3. Inicie o serviço:\n   systemctl start dnsblock-agent" 18 60
    fi
}

# ============================================================================
# FUNÇÃO DE DESINSTALAÇÃO
# ============================================================================

desinstalar() {
    if [ ! -d "$INSTALL_DIR" ] && [ ! -f "$SERVICE_FILE" ]; then
        whiptail --title "Não Instalado" \
            --msgbox "O DNSBlock Agent não parece estar instalado." 8 50
        exit 0
    fi
    
    if ! whiptail --title "Confirmar Desinstalação" \
        --yesno "Você está prestes a desinstalar o DNSBlock Agent.\n\nTodos os arquivos e configurações serão removidos.\n\nDeseja continuar?" 12 55; then
        exit 0
    fi
    
    # Exibir progresso
    {
        echo "10"; echo "# Parando serviço..."
        systemctl stop dnsblock-agent 2>/dev/null || true
        sleep 0.3
        
        echo "30"; echo "# Desabilitando serviço..."
        systemctl disable dnsblock-agent 2>/dev/null || true
        sleep 0.3
        
        echo "50"; echo "# Removendo arquivo de serviço..."
        rm -f "$SERVICE_FILE"
        systemctl daemon-reload
        sleep 0.3
        
        echo "70"; echo "# Removendo configuração do Unbound..."
        remover_configuracao_unbound
        sleep 0.3
        
        echo "90"; echo "# Removendo arquivos..."
        rm -rf "$INSTALL_DIR"
        sleep 0.3
        
        echo "100"
    } | whiptail --title "DNSBlock Agent - Desinstalação" --gauge "Removendo DNSBlock Agent..." 8 60 0
    
    whiptail --title "Desinstalação Concluída" \
        --msgbox "O DNSBlock Agent foi completamente removido do sistema." 8 55
}

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

menu_principal() {
    local opcao
    
    opcao=$(whiptail --title "DNSBlock Agent" \
        --menu "Selecione uma opção:" 15 60 4 \
        "1" "Instalar DNSBlock Agent" \
        "2" "Desinstalar DNSBlock Agent" \
        "3" "Sair" \
        3>&1 1>&2 2>&3)
    
    local status=$?
    
    if [ $status -ne 0 ] || [ "$opcao" = "3" ]; then
        exit 0
    fi
    
    case "$opcao" in
        1) instalar ;;
        2) desinstalar ;;
    esac
}

# ============================================================================
# INÍCIO DO SCRIPT
# ============================================================================

# Verificar root
verificar_root

# Processar argumentos
case "${1:-}" in
    install)
        instalar
        ;;
    uninstall|remove)
        desinstalar
        ;;
    --help|-h)
        echo ""
        echo "DNSBlock Agent - Script de Instalação"
        echo ""
        echo "Uso: $0 [install|uninstall]"
        echo ""
        echo "Opções:"
        echo "  install     Instalar o DNSBlock Agent"
        echo "  uninstall   Desinstalar o DNSBlock Agent"
        echo "  --help      Exibir esta mensagem"
        echo ""
        echo "Se executado sem argumentos, exibe o menu interativo."
        echo ""
        ;;
    "")
        menu_principal
        ;;
    *)
        echo "Opção inválida: $1"
        echo "Use --help para ver as opções disponíveis."
        exit 1
        ;;
esac
