#!/bin/bash
# --------------------------------------------------------
# Script para subir OpenSearch e OpenSearch Dashboards com SSL
# --------------------------------------------------------
# Author: Samuel Goncalves
# 4Linux OpenSoftware Specialists

set -eu

# Função para buscar a última versão estável do OpenSearch
get_latest_stable_version() {
  versions=$(curl -s "https://api.github.com/repos/opensearch-project/OpenSearch/releases")
  stable_versions=$(echo "$versions" | jq -r '.[] | select(.tag_name | test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) | .tag_name')
  latest_version=$(echo "$stable_versions" | sort -V | tail -n 1)
  echo "$latest_version"
}

# Função para gerar uma senha forte
generate_random_password() {
  LENGTH="${1:-12}"

  while : ; do
    password=$(LC_ALL=C tr -dc 'A-Za-z0-9_@!#&' < /dev/urandom | head -c "${LENGTH}")

    # Verificar se a senha gerada atende aos requisitos de força
    if [[ "$password" =~ [A-Z] && "$password" =~ [a-z] && "$password" =~ [0-9] && "$password" =~ [_@!#] ]]; then
      break
    fi
  done

  echo "$password"
}

# Função para verificar se o Docker e o Docker Compose estão instalados
check_requirements() {
  if ! command -v docker >/dev/null 2>&1; then
    echo "Erro: Docker não encontrado. Instale o Docker."
    exit 1
  fi

  if ! command -v docker-compose >/dev/null 2>&1; then
    echo "Erro: Docker Compose não encontrado. Instale o Docker Compose."
    exit 1
  fi
}

# Função para verificar se o volume já existe
check_volume() {
  volume_name="$1"
  if docker volume ls | grep -q "$volume_name"; then
    echo "Volume '$volume_name' já existe."
    read -p "Deseja aproveitar o volume existente? (s/n): " choice
    if [[ "$choice" == "n" || "$choice" == "N" ]]; then
      echo "Excluindo e recriando o volume..."
      docker volume rm "$volume_name"
      docker volume create "$volume_name"
    else
      echo "Volume existente será usado."
    fi
  else
    echo "Volume '$volume_name' não encontrado. Criando volume..."
    docker volume create "$volume_name"
  fi
}

# Função para gerar o arquivo .env com variáveis de configuração
create_env_file() {
  echo "Criando o arquivo .env com as variáveis..."
  
  cat > .env <<- EOM
OPENSEARCH_VERSION=$opensearch_version
OPENSEARCH_CONTAINER_NAME=$opensearch_container_name
OPENSEARCH_PASSWORD=$opensearch_password
OPENSEARCH_PORT=$opensearch_port
OPENSEARCH_HEAP_INIT=$opensearch_heap_init
OPENSEARCH_HEAP_MAX=$opensearch_heap_max
OPENSEARCH_INITIAL_ADMIN_PASSWORD=$opensearch_admin_password
EOM

  if [ -z "${opensearch_only:-}" ]; then
    cat >> .env <<- EOM
OPENSEARCH_DASHBOARDS_CONTAINER_NAME=$opensearch_dashboards_container_name
OPENSEARCH_DASHBOARDS_PORT=$opensearch_dashboards_port
OPENSEARCH_DASHBOARDS_PASSWORD=$opensearch_dashboards_password
EOM
  fi
}

# Função para criar o arquivo docker-compose.yml
create_docker_compose_file() {
  echo "Criando o arquivo docker-compose.yml com as configurações..."
  
  cat > docker-compose.yml <<- EOM
version: '3'
services:
  opensearch:
    image: opensearchproject/opensearch:${opensearch_version}
    container_name: ${opensearch_container_name}
    environment:
      - discovery.type=single-node
      - OPENSEARCH_PASSWORD=${opensearch_password}
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${opensearch_admin_password}
      - "ES_JAVA_OPTS=-Xms${opensearch_heap_init} -Xmx${opensearch_heap_max}"   # Configuração de memória
      - OPENSEARCH_SSL_ENABLED=true        # Habilitar SSL
      - OPENSEARCH_HTTP_ENABLED=false     # Desabilitar HTTP sem SSL
      - OPENSEARCH_NETWORK_HOST=0.0.0.0
      - OPENSEARCH_SSL_KEYSTORE_PATH=/usr/share/opensearch/config/certs/opensearch.keystore.p12
      - OPENSEARCH_SSL_KEYSTORE_PASSWORD=${opensearch_ssl_keystore_password}
      - OPENSEARCH_SSL_TRUSTSTORE_PATH=/usr/share/opensearch/config/certs/opensearch.truststore.p12
      - OPENSEARCH_SSL_TRUSTSTORE_PASSWORD=${opensearch_ssl_truststore_password}
    ports:
      - 0.0.0.0:9200:9200
      - 0.0.0.0:9300:9300
      - 0.0.0.0:9600:9600 # Usar HTTPS
      - 0.0.0.0:9650:9650
    volumes:
      - opensearch-data:/usr/share/opensearch/data
      - ./certs:/usr/share/opensearch/config/certs  # Volume para os certificados SSL
    healthcheck:
      test: "curl --silent --fail --show-error -u elastic:${opensearch_password} https://localhost:443"
      interval: 10s
      retries: 30
EOM

  if [ -z "${opensearch_only:-}" ]; then
    cat >> docker-compose.yml <<- EOM
  opensearch-dashboards:
    image: opensearchproject/opensearch-dashboards:${opensearch_version}
    container_name: ${opensearch_dashboards_container_name}
    environment:
      - OPENSEARCH_URL=https://opensearch:443
      - OPENSEARCH_USERNAME=elastic
      - OPENSEARCH_PASSWORD=${opensearch_dashboards_password}
    ports:
      - 0.0.0.0:${opensearch_dashboards_port}:5601
    depends_on:
      - opensearch
    healthcheck:
      test: "curl --silent --fail --show-error https://localhost:5601"
      interval: 10s
      retries: 30
EOM
  fi

  cat >> docker-compose.yml <<- EOM
volumes:
  opensearch-data:
EOM
}

# Função para executar o Docker Compose
run_docker_compose() {
  echo "Iniciando o OpenSearch e o OpenSearch Dashboards com SSL..."
  docker-compose up -d
}

# Função para mostrar a senha gerada
show_password() {
  echo "A senha para o OpenSearch é: ${opensearch_password}"
  echo "A senha de administrador do OpenSearch é: ${opensearch_admin_password}"
  echo "A senha do keystore SSL é: ${opensearch_ssl_keystore_password}"
  echo "A senha do truststore SSL é: ${opensearch_ssl_truststore_password}"
  if [ -z "${opensearch_only:-}" ]; then
    echo "A senha para o OpenSearch Dashboards é: ${opensearch_dashboards_password}"
  fi
}

# Função principal para rodar o script
main() {
  # Inicializa as variáveis padrão
  opensearch_container_name="opensearch-local"
  opensearch_dashboards_container_name="opensearch-dashboards-local"
  opensearch_port="443"                  # Usar a porta HTTPS
  opensearch_dashboards_port="5601"        # Definir a porta padrão do Dashboards
  opensearch_heap_init="512m"             # Configuração de memória mínima para o heap
  opensearch_heap_max="2g"               # Configuração de memória máxima para o heap
  opensearch_admin_password=$(generate_random_password)  # Gerar senha para admin
  opensearch_ssl_keystore_password=$(generate_random_password)  # Senha para keystore
  opensearch_ssl_truststore_password=$(generate_random_password) # Senha para truststore
  
  # Verifica os argumentos passados para a versão do OpenSearch
  while [ "$#" -gt 0 ]; do
    case "$1" in
      -v)
        if [ $# -lt 2 ]; then
          echo "Erro: -v requer um valor de versão (exemplo: -v 2.9.0)"
          exit 1
        fi
        opensearch_version="$2"
        shift 2
        ;;
      -opensearchonly)
        opensearch_only=true
        shift
        ;;
      *)
        echo "Opção desconhecida: $1"
        exit 1
        ;;
    esac
  done

  # Se a versão não for passada, busca a versão mais recente
  if [ -z "${opensearch_version:-}" ]; then
    echo "Versão não fornecida, buscando a última versão estável..."
    opensearch_version=$(get_latest_stable_version)
  fi
  
  # Exibir a versão para debug
  echo "Usando a versão do OpenSearch: ${opensearch_version}"

  # Gerar senha para o OpenSearch
  opensearch_password=$(generate_random_password)

  if [ -z "${opensearch_only:-}" ]; then
    # Gerar senha para o OpenSearch Dashboards
    opensearch_dashboards_password=$(generate_random_password)
  fi

  # Verificar requisitos
  check_requirements

  # Verificar volume
  check_volume "opensearch-data"

  # Criar arquivos .env e docker-compose.yml
  create_env_file
  create_docker_compose_file

  # Executar o Docker Compose
  run_docker_compose

  # Mostrar senhas geradas
  show_password
}

# Executar o script
main "$@"
