#!/usr/bin/env bash
set -Eeuo pipefail

IMAGE='labpasid/jupiter-analytics'
CONTAINER='jupiter-mono'
KEEP_ALIVE=0
SELECT_VERSION=0
TARGET_VERSION=''
while [[ $# -gt 0 ]]; do
    case "$1" in
        --keep-alive|--no-kill) KEEP_ALIVE=1; shift ;;
        --select-version) SELECT_VERSION=1; shift ;;
        --version) TARGET_VERSION="$2"; shift 2 ;;
        --version=*) TARGET_VERSION="${1#*=}"; shift ;;
        *) printf '[ERRO] Argumento desconhecido: %s\n' "$1" >&2; exit 2 ;;
    esac
done

step() {
    local number=$1 label=$2 filled i
    filled=$(( (number-1)*6/5 ))
    printf '\n['
    for ((i=0; i<6; i++)); do
        if ((i<filled)); then printf '#'; else printf '.'; fi
    done
    printf '] %d/5 %s\n' "$number" "$label"
}
fail() { printf '\n[ERRO] %s\n' "$*" >&2; exit 1; }
cleanup() { [[ -z ${TMP_INSTALLER:-} ]] || rm -f "$TMP_INSTALLER"; }
trap cleanup EXIT

docker_cmd() { "${DOCKER_CMD[@]}" "$@"; }
port_free() {
    if command -v python3 >/dev/null 2>&1; then
        python3 - "$1" <<'PY'
import socket, sys
s = socket.socket()
try:
    s.bind(('127.0.0.1', int(sys.argv[1])))
except OSError:
    sys.exit(1)
finally:
    s.close()
PY
    elif command -v lsof >/dev/null 2>&1; then
        ! lsof -nP -iTCP:"$1" -sTCP:LISTEN -t | grep -q .
    elif command -v ss >/dev/null 2>&1; then
        ! ss -ltn "sport = :$1" | tail -n +2 | grep -q .
    else
        fail 'Instale python3, lsof ou ss para verificar as portas.'
    fi
}
wait_tick() {
    local elapsed=$1 limit=$2 label=$3 marker j
    marker=$(( (elapsed / 3) % 6 ))
    printf '\r[ '
    for ((j=0; j<6; j++)); do
        if ((j==marker)); then printf '#'; else printf '.'; fi
    done
    printf ' ] %s - %ds decorridos (limite %ds)   ' "$label" "$elapsed" "$limit"
}
wait_docker() {
    local i successes=0
    for ((i=0; i<120; i++)); do
        if docker_cmd info >/dev/null 2>&1 && docker_cmd ps >/dev/null 2>&1; then
            successes=$((successes+1))
            if ((successes >= 3)); then printf '\n'; return 0; fi
        else
            successes=0
        fi
        wait_tick "$((i*3))" 360 'Aguardando Docker'
        sleep 3
    done
    printf '\n'
    return 1
}
pull_image() {
    local reference=$1 attempt
    for ((attempt=1; attempt<=5; attempt++)); do
        wait_docker || fail 'Docker nao ficou pronto em 6 minutos. Verifique o aplicativo, permissao e virtualizacao.'
        printf 'Baixando %s (tentativa %d de 5)...\n' "$reference" "$attempt"
        if docker_cmd pull "$reference"; then return 0; fi
        if ((attempt < 5)); then
            printf 'Download falhou. Aguardando 15 segundos para tentar novamente.\n' >&2
            sleep 15
        fi
    done
    fail "Nao foi possivel baixar $reference apos 5 tentativas. Confira a conexao e as mensagens do Docker acima."
}
wait_http() {
    local url=$1 limit=$2 label=$3 i
    for ((i=0; i<limit; i+=3)); do
        if curl -fsS --max-time 3 "$url" >/dev/null 2>&1; then printf '\n'; return 0; fi
        wait_tick "$i" "$limit" "$label"
        sleep 3
    done
    printf '\n'
    return 1
}
open_jupiter() {
    local url=$1
    printf 'Abrindo Jupiter Analytics no navegador...\n'
    if [[ $OS_NAME == Darwin ]]; then
        if open "$url" >/dev/null 2>&1; then return 0; fi
    else
        if command -v xdg-open >/dev/null 2>&1 && xdg-open "$url" >/dev/null 2>&1; then return 0; fi
        if command -v gio >/dev/null 2>&1 && gio open "$url" >/dev/null 2>&1; then return 0; fi
        if command -v sensible-browser >/dev/null 2>&1 && sensible-browser "$url" >/dev/null 2>&1; then return 0; fi
    fi
    printf '[AVISO] Nao foi possivel abrir o navegador automaticamente. Abra: %s\n' "$url" >&2
}

printf '\n==============================================\n'
printf '       Jupiter Analytics | Instalacao\n'
printf '==============================================\n'
OS_NAME=$(uname -s)
[[ $OS_NAME == Linux || $OS_NAME == Darwin ]] || fail 'Sistema operacional nao suportado.'
step 1 'Verificando Docker'
if ! command -v docker >/dev/null 2>&1; then
    if [[ $OS_NAME == Linux ]]; then
        command -v curl >/dev/null 2>&1 || fail 'curl e necessario para instalar o Docker.'
        command -v sudo >/dev/null 2>&1 || fail 'sudo e necessario para instalar o Docker.'
        printf 'Instalando Docker Engine pelo instalador oficial. Sera solicitada a senha de administrador.\n'
        TMP_INSTALLER=$(mktemp)
        curl -fL --retry 3 --progress-bar https://get.docker.com -o "$TMP_INSTALLER" || fail 'Falha no download do Docker.'
        sudo sh "$TMP_INSTALLER" || fail 'Falha na instalacao do Docker.'
    else
        if [[ -d /Applications/Docker.app ]]; then
            printf "Docker Desktop ja esta instalado.\n"
        elif command -v brew >/dev/null 2>&1; then
            brew install --cask docker || fail 'Falha na instalacao do Docker Desktop.'
        else
            command -v curl >/dev/null 2>&1 || fail 'curl e necessario para instalar o Docker Desktop.'
            tmpbase=$(mktemp -t jupiter-docker)
            rm -f "$tmpbase"
            TMP_INSTALLER="$tmpbase.dmg"
            arch=$(uname -m)
            case "$arch" in
                arm64) docker_arch=arm64 ;;
                x86_64) docker_arch=amd64 ;;
                *) fail "Arquitetura macOS nao suportada: $arch" ;;
            esac
            curl -fL --retry 3 --progress-bar "https://desktop.docker.com/mac/main/$docker_arch/Docker.dmg" -o "$TMP_INSTALLER" || fail 'Falha no download do Docker Desktop.'
            hdiutil attach "$TMP_INSTALLER" -quiet || fail 'Nao foi possivel montar o instalador.'
            sudo /Volumes/Docker/Docker.app/Contents/MacOS/install || { hdiutil detach /Volumes/Docker -quiet || true; fail 'Falha na instalacao do Docker Desktop.'; }
            hdiutil detach /Volumes/Docker -quiet || true
        fi
        export PATH="$HOME/.docker/bin:/usr/local/bin:$PATH"
    fi
fi
if [[ $OS_NAME == Darwin ]]; then
    export PATH="$HOME/.docker/bin:/usr/local/bin:$PATH"
    open -a Docker || fail 'Nao foi possivel abrir o Docker Desktop.'
    for ((i=0; i<40; i++)); do
        command -v docker >/dev/null 2>&1 && break
        wait_tick "$((i*3))" 120 'Aguardando comando Docker'
        sleep 3
    done
    printf '\n'
fi
command -v docker >/dev/null 2>&1 || fail 'Docker foi instalado, mas o comando docker nao foi encontrado.'
DOCKER_CMD=(docker)
command -v curl >/dev/null 2>&1 || fail 'curl e necessario para verificar a aplicacao.'
step 2 'Iniciando Docker'
if [[ $OS_NAME == Linux ]]; then
    if ! docker info >/dev/null 2>&1; then
        if command -v systemctl >/dev/null 2>&1; then
            sudo systemctl enable --now docker || fail 'Nao foi possivel iniciar o servico Docker.'
        else
            sudo service docker start || fail 'Nao foi possivel iniciar o servico Docker.'
        fi
        if ! docker info >/dev/null 2>&1; then
            sudo -v || fail 'Sem permissao para acessar o Docker.'
            DOCKER_CMD=(sudo docker)
            printf 'Acesso temporario via sudo nesta execucao.\n'
        fi
    fi
fi
wait_docker || fail 'Docker nao ficou pronto em 6 minutos. Verifique o aplicativo, permissao e virtualizacao.'
[[ $(docker_cmd info --format '{{.OSType}}') == linux ]] || fail 'Configure Docker para conteineres Linux.'
step 3 'Selecionando e baixando a imagem'
VERSION=latest
if [[ -n "$TARGET_VERSION" ]]; then
    VERSION="$TARGET_VERSION"
    printf 'Usando versao especificada: %s\n' "$VERSION"
elif command -v python3 >/dev/null 2>&1; then
    tags=$(curl -fsS --max-time 30 "https://hub.docker.com/v2/repositories/$IMAGE/tags/?page_size=100" 2>/dev/null) || tags=''
    if [[ -n $tags ]]; then
        latest_numbered=$(python3 -c 'import sys,json,re; names=[x["name"] for x in json.load(sys.stdin).get("results",[])]; nums=[x for x in names if re.fullmatch(r"v?[0-9]+\.[0-9]+\.[0-9]+",x)]; print(max(nums,key=lambda x: tuple(map(int,x.lstrip("v").split("."))),default="latest"))' <<< "$tags") || latest_numbered=latest
        VERSION=$latest_numbered
    fi
fi
if ((SELECT_VERSION)); then
    printf 'Versoes disponiveis:\n'
    if [[ -n ${tags:-} ]] && command -v python3 >/dev/null 2>&1; then
        python3 -c 'import sys,json; [print("  " + x["name"]) for x in json.load(sys.stdin).get("results",[])[:20]]' <<< "$tags" || true
    fi
    read -r -p "Versao desejada (Enter para $VERSION): " selected
    VERSION=${selected:-$VERSION}
fi
[[ $VERSION =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || fail 'Versao Docker invalida.'
printf 'O Docker mostrara o progresso de cada camada da imagem abaixo.\n'
pull_image "$IMAGE:$VERSION"
step 4 'Configurando dados e portas'
if ! docker_cmd container inspect "$CONTAINER" >/dev/null 2>&1; then
    port_free 3001 || fail 'A porta 3001 esta ocupada. Libere-a para o backend.'
    port_free 8080 || fail 'A porta 8080 esta ocupada. Libere-a para o motor.'
fi
FRONT_PORT=80
while ! port_free "$FRONT_PORT"; do
    FRONT_PORT=$((FRONT_PORT+1))
    ((FRONT_PORT <= 65535)) || fail 'Nao ha porta livre para o frontend.'
done
DATA_DIR="$HOME/Documents/JupiterModels"
mkdir -p "$DATA_DIR" || fail 'Nao foi possivel criar o diretorio de modelos.'
if docker_cmd container inspect "$CONTAINER" >/dev/null 2>&1; then
    docker_cmd rm -f "$CONTAINER" >/dev/null || fail 'Nao foi possivel substituir o conteiner anterior.'
    port_free 3001 || fail 'A porta 3001 esta ocupada apos parar o conteiner antigo.'
    port_free 8080 || fail 'A porta 8080 esta ocupada apos parar o conteiner antigo.'
fi
step 5 'Iniciando e verificando a aplicacao'
docker_cmd run -d --restart unless-stopped --name "$CONTAINER" \
    -p "127.0.0.1:$FRONT_PORT:80" \
    -p '127.0.0.1:3001:3001' \
    -p '127.0.0.1:8080:8080' \
    -v "$DATA_DIR:/app/data" "$IMAGE:$VERSION" >/dev/null || fail 'Falha ao iniciar o conteiner.'
URL="http://localhost:$FRONT_PORT"
if ! wait_http "$URL/" 180 'Verificando frontend' || ! wait_http 'http://localhost:3001/health' 30 'Verificando API'; then
    docker_cmd logs --tail 60 "$CONTAINER" >&2 || true
    fail 'O conteiner iniciou, mas a aplicacao nao respondeu a tempo.'
fi
printf '\n[######] 5/5 Pronto! Aplicacao: %s\n' "$URL"
printf 'Modelos salvos em: %s\n' "$DATA_DIR"
open_jupiter "$URL"
if (( ! KEEP_ALIVE )); then printf 'O conteiner continuara em segundo plano. Para parar: docker stop %s\n' "$CONTAINER"; fi
