#!/bin/bash

# Por Tales A. Mendonça (talesam@gmail.com)
# Faz a instalação da aplicação IHX Access Pro em Docker

# Versão do script
Ver="v1.8.6"

# Definição de Cores
CIN='\e[30;1m'			# Cinza
RED='\e[31;1m'			# Vermelho
GRE='\e[32;1m'			# Verde
YEL='\e[33;1m'			# Amarelo
BLU='\e[34;1m'			# Azul
ROS='\e[35;1m'			# Rosa
CYA='\e[36;1m'			# Ciano
NEG='\e[37;1m'			# Negrito
CUI='\e[40;31;5m'		# Vermelho pisacando, aviso!
STD='\e[m'				# Fechamento de cor

### INÍCIO FUNÇÕES ###

# Função pause
pause(){
   read -p "$*"
}

### Função Alerta Vermelho
alertaVermelho() {
    echo -e "${RED}*${STD} ${NEG} $1 ${STD}"
    echo ""
}

### Função Alerta Amarelo
alertaAmarelo() {
    echo -e "${YEL}*${STD} ${NEG} $1 ${STD}"
    echo ""
    sleep 1
}

### Função Alerta Verde
alertaVerde() {
    echo -e "${GRE}*${STD} ${NEG} $1 ${STD}"
    echo ""
    sleep 1
}

### Função Alerta Verde
alertaAzul() {
    echo -e "${BLU}*${STD} ${NEG} $1 ${STD}"
    echo ""
    sleep 1
}

### Função coleta de dados
coletaDados() {
	
	# Este é um loop infinito que continuará até que seja quebrado com 'break'
	while : ; do

		# Solicitar entrada do usuário
		alertaAzul "Insira o nome do cliente:"
		read cliente

		# Definir o caminho para o diretório do cliente
		caminho="$HOME/Docker/IHX_Access_Pro/$cliente"
		
		# Converte a entrada do usuário para minúsculas
		cliente=${cliente,,}

		# Verificar se a entrada não está vazia
		if [ -z "$cliente" ]; then
			alertaAmarelo "O nome do cliente não pode ser vazio."
		else
			break  # Se a entrada não estiver vazia, quebra o loop e continua o script
		fi
	done

	# Verificar se o diretório já existe
	if [ -d "$caminho" ]; then
		alertaAmarelo "Diretório $caminho já existe."
	else
		# Criar o diretório se ele não existir
		mkdir -p "$caminho"
		alertaVerde "Diretório $caminho criado com sucesso."
	fi

	# caminho para o arquivo .env
	envPath="$caminho/.env"
	
	# Encontra e configura a porta disponível
	if ! encontrarPortaDisponivel "$envPath"; then
		alertaVermelho "Erro ao encontrar uma porta disponível."
		exit 1
	fi

	# Escrever as variáveis no arquivo .env para o docker-compose.yml
	{
		echo "cliente=$cliente"
		echo "db_Container_Name=db_ihx_$cliente"
		echo "db_Root_Password=1hx@p4550rd"
		echo "db_Name=ihx_access_pro"
		echo "db_User=db_user"
		echo "db_Password=1hx@p4550rd"
		echo "app_Container_Name=ihx_$cliente"
		echo "network_Name=ihx-net_$cliente"
	} >> "$envPath"

	# Carregar arquivo .env
	source "$envPath"

}

# Função para encontrar uma porta disponível
encontrarPortaDisponivel() {
	local envPath="$1" # Adiciona um argumento para o caminho do arquivo .env
    local basePort=33601 # Porta base para começar a busca
    local maxPort=34000 # Porta máxima que queremos verificar
    local port=$basePort

    # Loop para encontrar uma porta disponível
    while : ; do
        # Checa se a porta está em uso (necessário ter o net-tools instalado)
        if ! netstat -tuln | grep -q ":$port"; then
            # Porta está disponível
            alertaVerde "Porta disponível encontrada: $port"

            # Escreve a porta disponível no arquivo .env e verifica se foi bem-sucedido
            echo "porta_db=$port" >> "$envPath" && \
				alertaVerde "Porta escrita no arquivo .env com sucesso." || \
				{ alertaVermelho "Falha ao escrever a porta no arquivo .env."; return 1; }


            # Adiciona a regra no firewall para permitir a porta encontrada (necessário ter permissões de superusuário)
            if ! sudo ufw allow $port/tcp; then
                alertaVermelho "Falha ao adicionar regra no firewall para a porta $port."
                return 1
            else
                alertaVerde "Regra no firewall adicionada com sucesso para a porta $port."
                return 0
            fi
        else
            # Porta está em uso, incrementa e tenta a próxima
            ((port++))
            if [ $port -ge $maxPort ]; then
                alertaVermelho "Não foi possível encontrar uma porta disponível."
                return 1
            fi
        fi
    done
}

### Função para baixa os arquivos necessários
arquivosNecessarios() {
	# Diretório temporário para extração dos arquivos baixados
	dirTmp="/tmp/IHX"
	rm -rf $dirTmp

	# Cria o diretório temporário
	mkdir -p $dirTmp

	# Nome do arquivo
	arquivo="docker-compose_v1.8.tar.bz2"

	# Baixa o arquivo
	wget -q https://cloud.talesam.org/s/zekHJye2ncwM4pk/download/$arquivo -P $dirTmp

	# Verifica se o arquivo foi baixado corretamente
	if [ ! -f "$dirTmp/$arquivo" ]; then
		alertaVermelho "Erro: O arquivo não foi baixado corretamente."
		exit 1
	fi

	# Extrai o arquivo
	tar -xvJf $dirTmp/$arquivo -C $caminho

	# Verifica se o arquivo foi extraído corretamente
	if [ $? -ne 0 ]; then
		alertaVermelho "Erro: Falha ao extrair o arquivo."
		exit 1
	else
		alertaVerde "Arquivos extraídos com sucesso para $caminho."
	fi
}

### Função para instalar aplicação IHX
instalarIHX() {
	# Limpa a tela
    clear
    
	# Chama a função para coleta de dados
	coletaDados
	
	# Chama a funçao para baixar os arquivos
	arquivosNecessarios
	
    # Inicia os Conteiners
    alertaAzul "Ativando todos os containers, aguarde..."

    # Deixa os conteiners rodando em segundo plano
    cd $caminho
    docker-compose up -d && sleep 1

    # Nomes dos contêineres a serem verificados
    containers=("$db_Container_Name")

    # Verifica a saúde dos contêineres
    for container in "${containers[@]}"; do
        statusSaude=""
        tentativasMax=100
        tentativas=0
        pontos=""

        while [[ "$statusSaude" != "healthy" && $tentativas -lt $tentativasMax ]]; do
            statusSaude=$(docker inspect --format='{{json .State.Health.Status}}' "$container" | tr -d '"')
            
            if [[ "$statusSaude" == "healthy" ]]; then
                alertaVerde "Contêiner $container saudável!"
            else
                pontos+="."
                printf " ${YEL}*${STD} ${NEG}Contêiner{STD} ${YEL}%-22s${STD} ${NEG}ainda está iniciando, aguardando$pontos${STD}\r" "$container"
            fi
            
            ((tentativas++))
            sleep 1
        done

        echo ""

        if [[ "$statusSaude" != "healthy" ]]; then
            alertaVermelho "Contêiner $container não ficou saudável após $tentativasMax tentativas."
            exit 1
        fi

        # O contêiner está saudável, executar criação de tabelas
        alertaAzul "Iniciando a criação das tabelas e importação do banco de dados no contêiner $app_Container_Name..."
                
        docker exec $app_Container_Name bash -c "source /ihx_access_pro/env/bin/activate && python2 /ihx_access_pro/manage.py syncdb --noinput"

        if [[ $? -eq 0 ]]; then
            alertaVerde "Tabelas criadas e sincronizadas com sucesso!"
            
            sqlFileS=("funcoes.sql" "inserts.sql" "updates.sql")
            sucessoImportacao=true

            for sqlFile in "${sqlFileS[@]}"; do
                alertaAzul "Copiando e importando $sqlFile"
                
                docker cp $caminho/Config/db/$sqlFile $db_Container_Name:/tmp/$sqlFile
                docker exec $db_Container_Name mysql -u$db_User -p$db_Password ihx_access_pro -e "source /tmp/$sqlFile"
                
                if [[ $? -ne 0 ]]; then
                    alertaVermelho "Falha ao importar $sqlFile"
                    sucessoImportacao=false
                    break
                else
                    alertaVerde "$sqlFile importado com sucesso!"
                fi
            done
        else
            alertaVermelho "Falha ao criar tabelas ou importar banco de dados!"
            exit 1
        fi
    done
    
    # Chama a função pause
    pause " Tecle [Enter] para voltar ao menu..." && sleep 1 ; clear
}

### Função para instalar certificado SSL
instalarCertificado() {
    # Limpa a tela
    clear

    # Listar os Diretórios
    baseDir="$HOME/Docker/IHX_Access_Pro/"
    IFS=$'\n' read -d '' -r -a dirArray < <(ls -d "$baseDir"*/ && printf '\0')

    # Exibir o Menu
    alertaAzul "Escolha onde deseja instalar o certificado SSL:"
    for i in "${!dirArray[@]}"; do
        dirName=$(basename "${dirArray[$i]}")
        echo -e "${BLU}$((i + 1)))${STD} $dirName"
    done

    # Adicionada a opção de voltar
    echo -e "${ROS}$(( ${#dirArray[@]} + 1 )))${STD} Voltar ao menu principal"

    # Seleção do Usuário
    echo ""
    read -p "Escolha um número: " choice

    # Se o usuário escolher 'Voltar ao menu principal'
    if [ "$choice" -eq "$(( ${#dirArray[@]} + 1 ))" ]; then
        return
    fi

    cliente=$(basename "${dirArray[$((choice - 1))]}")

    # Define o caminho para o diretório do cliente
    caminho="$HOME/Docker/IHX_Access_Pro/$cliente"

    # Desativando contêineres para gerar e importar das chaves e certificados SSL
    alertaAzul "Desativando os contêineres para gerar e importar as chaves e certificados SSL, aguarde..."
    if ! cd "$caminho" || ! docker-compose down; then
        alertaVermelho "Falha ao desativar os contêineres."
        exit 1
    fi

    # Define o diretório para armazenar as chaves SSL
    sslKeyDir="$caminho/mysql_keys"

    # Cria o diretório para armazenar as chaves SSL, se não existir
    if ! mkdir -p "$sslKeyDir"; then
        alertaVermelho "Erro ao criar o diretório $sslKeyDir"
        exit 1
    fi
    
	# Modifica a propriedade do diretório para o usuário ihx e o grupo ihx
    if ! sudo chown ihx:ihx "$sslKeyDir"; then
        alertaVermelho "Erro ao mudar a propriedade do diretório $sslKeyDir para ihx:ihx"
        exit 1
    fi

    # Gera uma senha randômica
    senhaChave=$(openssl rand -base64 48)
    echo "$senhaChave" > "$sslKeyDir/senha.txt"
    chmod 600 "$sslKeyDir/senha.txt"

    # Gera a chave e o certificado do servidor
    if ! openssl req -x509 -newkey rsa:2048 -keyout "$sslKeyDir/server-key-enc.pem" \
        -out "$sslKeyDir/server-cert.pem" \
        -subj "/C=BR/ST=MG/L=Juiz de Fora/O=IHX Sistema/OU=Tecnologia/CN=server/emailAddress=contato@ihxsistema.com.br" \
        -passout pass:"$senhaChave"; then
        alertaVermelho "Erro ao gerar a chave e certificado do servidor."
        exit 1
    else
        alertaVerde "Chave e o certificado do servidor gerados com sucesso!"
    fi

    # Decodifica a chave do servidor
    if ! openssl rsa -in "$sslKeyDir/server-key-enc.pem" -out "$sslKeyDir/server-key.pem" \
        -passin pass:"$senhaChave" -passout pass:"$senhaChave"; then
        alertaVermelho "Erro ao decodificar chave servidor."
        exit 1
    else
        alertaVerde "Chave do servidor decodificada com sucesso!"
    fi

    # Gera a chave e o certificado do cliente
    if ! openssl req -x509 -newkey rsa:2048 -keyout "$sslKeyDir/client-key-enc.pem" \
        -out "$sslKeyDir/client-cert.pem" \
        -subj "/C=BR/ST=MG/L=Juiz de Fora/O=IHX Sistema/OU=Tecnologia/CN=client/emailAddress=contato@ihxsistema.com.br" \
        -passout pass:"$senhaChave"; then
        alertaVermelho "Erro ao gerar a chave e certificado do cliente."
        exit 1
    else
        alertaVerde "Chave e o certificado do cliente gerados com sucesso!"
    fi

    # Decodifica a chave do cliente
    if ! openssl rsa -in "$sslKeyDir/client-key-enc.pem" -out "$sslKeyDir/client-key.pem" \
        -passin pass:"$senhaChave" -passout pass:"$senhaChave"; then
        alertaVermelho "Erro ao decodificar chave cliente."
        exit 1
    else
        alertaVerde "Chave do cliente decodificada com sucesso!"
    fi

    # Combina os certificados de cliente e servidor no arquivo de certificados CA
    if ! cat "$sslKeyDir/server-cert.pem" "$sslKeyDir/client-cert.pem" > "$sslKeyDir/ca-cert.pem"; then
        alertaVermelho "Erro ao combinar certificado do cliente e servidor no arquivo de certificados CA."
        exit 1
    else
        alertaVerde "Certificado do cliente e servidor combinados no arquivo de certificados CA com sucesso!"
    fi

    # Verifica o Certificado do Servidor
    if ! openssl verify -CAfile "$sslKeyDir/ca-cert.pem" "$sslKeyDir/server-cert.pem"; then
        alertaVermelho "Erro ao verificar certificado do servidor."
        exit 1
    else
        alertaVerde "Certificado do servidor verificado com sucesso!"
    fi

    # Verifica o Certificado do Cliente
    if ! openssl verify -CAfile "$sslKeyDir/ca-cert.pem" "$sslKeyDir/client-cert.pem"; then
        alertaVermelho "Erro ao verificar certificado do cliente."
        exit 1
    else
        alertaVerde "Certificado do cliente verificado com sucesso!"
    fi

    # Substituição do arquivo custom_ssl.cnf pelo arquivo custom.cnf
    alertaAzul "Substituindo o arquivo custom_ssl.cnf pelo arquivo custom.cnf..."
    if ! cp -f "$caminho/Config/custom_ssl.cnf" "$caminho/Config/custom.cnf"; then
        alertaVermelho "Erro ao realizar a substituição do arquivo custom.cnf."
        exit 1
    else
        alertaVerde "Substituição do arquivo custom.cnf realizada com sucesso!"
    fi

    # Remove as chaves codificadas para garantir a segurança
    alertaAzul "Removendo as chaves codificadas..."
    if ! rm -f "$sslKeyDir/client-key-enc.pem" "$sslKeyDir/server-key-enc.pem"; then
        alertaVermelho "Erro ao remover as chaves codificadas."
        exit 1
    else
        alertaVerde "Chaves codificadas removidas com sucesso!"
    fi
    
    # Obtém a data atual no formato AAAA-MM-DD
    dataAtual=$(date +%F)

    # Nome do arquivo zip seguindo o padrão keys_data_cliente.zip
    nomeArquivoZip="keys_${dataAtual}_${cliente}.zip"

    # Compacta os arquivos especificados em um arquivo .zip
    alertaAzul "Compactando as chaves..."
    if zip -j "$sslKeyDir/$nomeArquivoZip" \
    "$sslKeyDir/ca-cert.pem" \
    "$sslKeyDir/client-cert.pem" \
    "$sslKeyDir/client-key.pem"; then
        alertaVerde "Chaves compactadas com sucesso no arquivo $nomeArquivoZip."

        # Altera a propriedade do arquivo zip para o usuário ihx e o grupo ihx
        if sudo chown ihx:ihx "$sslKeyDir/$nomeArquivoZip"; then
            alertaVerde "Propriedade do arquivo $nomeArquivoZip alterada para ihx:ihx com sucesso."
        else
            alertaVermelho "Erro ao alterar a propriedade do arquivo $nomeArquivoZip."
            exit 1
        fi
    else
        alertaVermelho "Erro ao compactar as chaves."
        exit 1
    fi

    # Define os detalhes de login do Nextcloud
    nextcloudUser='talesam@gmail.com'
    nextcloudPass='nmYiiahisDA5653'  # Substitua MINHA_SENHA pela sua senha real
    nextcloudURL="https://cloud.talesam.org/remote.php/dav/files/$nextcloudUser/"

    # O caminho completo da pasta no Nextcloud onde o arquivo será salvo
    # Os espaços e caracteres especiais são codificados para URL
    nextcloudDirPath="Servidor/Docker/Fran%C3%A7ois/IHX%20Access%20Pro/Keys"

    # Caminho local do arquivo que deseja enviar
    localFilePath="$sslKeyDir/$nomeArquivoZip"

    # Nome do arquivo no Nextcloud, incluindo o caminho da pasta onde deve ser salvo
    nextcloudFilePath="$nextcloudDirPath/$nomeArquivoZip"

    # Envia o arquivo para o Nextcloud e captura a resposta HTTP
    alertaAzul "Enviando o arquivo com as keys para o Nextcloud..."
    response=$(curl -w "%{http_code}" -u "$nextcloudUser:$nextcloudPass" -T "$localFilePath" "$nextcloudURL$nextcloudFilePath" -o /dev/null -s)

    # Verifica se o upload foi bem-sucedido analisando o código de resposta HTTP
    if [ "$response" -eq 201 ]; then
        alertaVerde "Arquivo enviado para o Nextcloud com sucesso!!"
    elif [ "$response" -eq 204 ]; then
        alertaVerde "Arquivo substituído com sucesso no Nextcloud."
    else
        alertaVermelho "Erro ao enviar arquivo para o Nextcloud: HTTP status $response."
        exit 1
    fi
    
    # Iniciar os contêineres
    alertaAzul "Iniciando os contêineres com SSL ativo no banco de dados..."
    if ! cd "$caminho" || ! docker-compose up -d; then
        alertaVermelho "Erro ao iniciar contêineres."
        exit 1
    else
        alertaVerde "Contêineres iniciados com sucesso!"
    fi
    
    # Chama a função pause
    pause " Tecle [Enter] para voltar ao menu..." && sleep 1 && clear
}

### Função para remover uma aplicação
removerAplicacao() {
    # Limpa a tela
    clear

    # Listar os Diretórios
    baseDir="$HOME/Docker/IHX_Access_Pro/"
    IFS=$'\n' read -d '' -r -a dirArray < <(ls -d "$baseDir"*/ && printf '\0')
    
    # Exibir o Menu
    alertaAzul "Escolha a aplicação que deseja remover:"
    for i in "${!dirArray[@]}"; do
        dirName=$(basename "${dirArray[$i]}")
        echo -e "${BLU}$((i + 1)))${STD} $dirName"
    done
    
    # Adicionada a opção de voltar
    echo -e "${ROS}$(( ${#dirArray[@]} + 1 )))${STD} Voltar ao menu principal"
    
    # Seleção do Usuário
    echo ""
    read -p "Escolha um número: " choice
    
    # Se o usuário escolher 'Voltar ao menu principal'
    if [ "$choice" -eq "$(( ${#dirArray[@]} + 1 ))" ]; then
        return
    fi

    cliente=$(basename "${dirArray[$((choice - 1))]}")
    
    # Define o caminho do diretório do cliente
    caminho="$HOME/Docker/IHX_Access_Pro/$cliente"
    
    # Entra no diretório do cliente
    if ! cd "$caminho"; then
        alertaVermelho "Erro ao entrar no diretório $caminho."
        exit 1
    fi
    
    # Desativa a aplicação
    alertaAzul "Desativando a aplicação para ser removida..."
    if ! docker-compose down; then
        alertaVermelho "Erro ao desativar a aplicação com docker-compose down."
        exit 1
    else
        alertaVerde "Aplicação desativada com sucesso."
    fi

    # Lê a porta que estava em uso a partir do arquivo .env
    if [ -f ".env" ]; then
        source .env
        porta=${porta_db:-}
        if [ -z "$porta" ]; then
            alertaVermelho "Porta não definida no arquivo .env."
            exit 1
        fi
    else
        alertaVermelho "Arquivo .env não encontrado. Não é possível determinar a porta para bloquear no firewall."
        exit 1
    fi

    # Retorna ao diretório anterior
    cd - > /dev/null 2>&1 || { alertaVermelho "Erro ao retornar ao diretório anterior."; return 1; }

    # Remove o diretório da aplicação
    if ! sudo rm -rf "$caminho"; then
        alertaVermelho "Erro ao remover o diretório $caminho."
        exit 1
    else
        alertaVerde "Diretório $caminho removido com sucesso."
    fi

    # Bloqueia a porta no firewall
    if ! sudo ufw delete allow "$porta/tcp"; then
        alertaVermelho "Erro ao bloquear a porta $porta no firewall."
        exit 1
    else
        alertaVerde "Porta $porta bloqueada no firewall com sucesso."
    fi

    alertaVerde "Aplicação removida e porta bloqueada com sucesso."
    
    # Chama a função pause
    pause " Tecle [Enter] para voltar ao menu..."
}

### FIM FUNÇÕES ###

# ------- MENU -------

# Mostra o menu
while true; do
    clear
	echo -e " ${BLU}SCRIPT DE INSTALAÇÃO DO IHX ACCESS PRO -${STD} ${YEL}${Ver}${STD}"
	echo ""
    echo ""
    echo -e " ${BLU}1) Instalar aplicação IHX${STD}"
    echo -e " ${GRE}2) Instalar certificado${STD}"
    echo -e " ${RED}3) Remover aplicação IHX${STD}"
    echo -e " ${YEL}0) Sair${STD}"
    echo ""
    read -p " Escolha uma opção: " escolha

    case $escolha in
        1) instalarIHX ;;
        2) instalarCertificado ;;
        3) removerAplicacao ;;
        0) exit ;;
        *) alertaAmarelo "Opção inválida" ;;
    esac
done