#!/bin/bash
# Info: Script to install tools
# Author: JesusPerezLorenzo
# Release: 1.0
# Date: 12-11-2023

[ "$DEBUG" == "-x" ] && set -x

USAGE="install-tools [ tool-name: providers tera k9s, etc | all] [--update]
As alternative use environment var TOOL_TO_INSTALL with a list-of-tools (separeted with spaces)
Versions are set in ./versions file

This can be called by directly with an argumet or from an other srcipt
"

ORG=$(pwd)

function _install_cmds {
  OS="$(uname | tr '[:upper:]' '[:lower:]')"
  local has_cmd
  for cmd in $CMDS_PROVISIONING
  do
    has_cmd=$(type -P $cmd)
    if [ -z "$has_cmd" ] ; then
      case "$OS" in
        darwin) brew install $cmd ;;
        linux) sudo apt install $cmd ;;
        *) echo "Install $cmd in your PATH" ;;
      esac
    fi
  done
}
function _install_providers {
  local match=$1
  shift
  local options
  local info_keys
  options="$*"
  info_keys="info version site"
  if [ -z "$match" ] || [ "$match" == "all" ] || [ "$match" == "-" ]; then
    match="all"
  fi

  for prov in $(ls $PROVIDERS_PATH | grep -v "^_" )
  do
    prov_name=$(basename "$prov")
    [ ! -d "$PROVIDERS_PATH/$prov_name/templates" ] && continue
    if [ "$match" == "all" ] || [ "$prov_name" == "$match" ] ; then
      [ -x "$PROVIDERS_PATH/$prov_name/bin/install.sh" ] && $PROVIDERS_PATH/$prov_name/bin/install.sh $options
    elif [ "$match" == "?" ] ; then
      [ -n "$options" ] && [ -z "$(echo "$options" | grep ^$prov_name)" ] && continue
      if [ -r "$PROVIDERS_PATH/$prov_name/provisioning.yaml" ] ; then
        echo "-------------------------------------------------------"
        for key in $info_keys
        do
          echo -n "$key:"
          [ "$key" != "version" ] && echo -ne "\t"
          echo " $(grep "^$key:" "$PROVIDERS_PATH/$prov_name/provisioning.yaml" | sed "s/$key: //g")"
        done
        [ -n "$options" ] && echo "________________________________________________________"
      else
        echo "$prov_name"
      fi
    fi
  done
  [ "$match" == "?" ] && [ -z "$options" ] && echo "________________________________________________________"
}
function _install_tools {
  local match=$1
  shift
  local options
  options="$*"
  # local has_jq
  # local jq_version
  # local has_yq
  # local yq_version
  local has_nu
  local nu_version
  local has_nickel
  local nickel_version
  local has_tera
  local tera_version
  local has_k9s
  local k9s_version
  local has_age
  local age_version
  local has_sops
  local sops_version

  OS="$(uname | tr '[:upper:]' '[:lower:]')"
  ORG_OS=$(uname)
  ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')"
  ORG_ARCH="$(uname -m)"

  if [ -z "$CHECK_ONLY" ] && [ "$match" == "all" ] ; then
    _install_cmds
  fi

  # if [ -n "$JQ_VERSION" ] && [ "$match" == "all" ] ||  [ "$match" == "jq" ]  ; then
  #   has_jq=$(type -P jq)
  #   num_version="0"
  #   [ -n "$has_jq" ] && jq_version=$(jq -V | sed 's/jq-//g') && num_version=${jq_version//\./}
  #   expected_version_num=${JQ_VERSION//\./}
  #   if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
  #     curl -fsSLO "https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-${OS}-${ARCH}" &&
  #       chmod +x "jq-${OS}-${ARCH}" &&
  #       sudo mv "jq-${OS}-${ARCH}" /usr/local/bin/jq &&
  #       printf "%s\t%s\n" "jq" "installed $JQ_VERSION"
  #   elif [ -n "$CHECK_ONLY" ] ; then
  #     printf "%s\t%s\t%s\n" "jq" "$jq_version" "expected $JQ_VERSION"
  #   else
  #     printf "%s\t%s\n" "jq" "already $JQ_VERSION"
  #   fi
  # fi
  # if [ -n "$YQ_VERSION" ] && [ "$match" == "all" ] ||  [ "$match" == "yq" ]  ; then
  #   has_yq=$(type -P yq)
  #   num_version="0"
  #   [ -n "$has_yq" ] && yq_version=$(yq -V | cut -f4 -d" " | sed 's/v//g') && num_version=${yq_version//\./}
  #   expected_version_num=${YQ_VERSION//\./}
  #   if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
  #     curl -fsSLO "https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_${OS}_${ARCH}.tar.gz" &&
  #       tar -xzf "yq_${OS}_${ARCH}.tar.gz" &&
  #       sudo mv "yq_${OS}_${ARCH}" /usr/local/bin/yq &&
  #       sudo ./install-man-page.sh &&
  #       rm -f install-man-page.sh yq.1 "yq_${OS}_${ARCH}.tar.gz" &&
  #       printf "%s\t%s\n" "yq" "installed $YQ_VERSION"
  #   elif [ -n "$CHECK_ONLY" ] ; then
  #     printf "%s\t%s\t%s\n" "yq" "$yq_version" "expected $YQ_VERSION"
  #   else
  #     printf "%s\t%s\n" "yq" "already $YQ_VERSION"
  #   fi
  # fi

  if [ -n "$NU_VERSION" ] && [ "$match" == "all" ] ||  [ "$match" == "nu" ]  ; then
    has_nu=$(type -P nu)
    num_version="0"
    [ -n "$has_nu" ] && nu_version=$(nu -v) && num_version=${nu_version//\./} && num_version=${num_version//0/}
    expected_version_num=${NU_VERSION//\./}
    expected_version_num=${expected_version_num//0/}
    [ -z "$num_version" ] && num_version=0
    if [ -z "$num_version" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION require installation"
    elif [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION require installation"
    elif [ -n "$CHECK_ONLY" ] ; then
      printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION"
    else
      printf "%s\t%s\n" "nu" "already $NU_VERSION"
    fi
  fi
  if [ -n "$NICKEL_VERSION" ] && [ "$match" == "all" ] ||  [ "$match" == "nickel" ]  ; then
    has_nickel=$(type -P nickel)
    num_version=0
    [ -n "$has_nickel" ] && nickel_version=$((nickel -V | cut -f3 -d" ") 2>/dev/null) && num_version=${nickel_version//\./}
    expected_version_num=${NICKEL_VERSION//\./}
    [ -z "$num_version" ] && num_version=0
    if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      # macOS: try Cargo first, then Homebrew
      if [ "$OS" == "darwin" ] ; then
        printf "%s\t%s\n" "nickel" "installing $NICKEL_VERSION on macOS"

        # Try Cargo first (if available)
        if command -v cargo >/dev/null 2>&1 ; then
          printf "%s\t%s\n" "nickel" "using Cargo (Rust compiler)"
          if cargo install nickel-lang-cli --version "${NICKEL_VERSION}" ; then
            printf "%s\t%s\n" "nickel" "✅ installed $NICKEL_VERSION via Cargo"
          else
            printf "%s\t%s\n" "nickel" "❌ Failed to build with Cargo"
            exit 1
          fi
        # Try Homebrew if Cargo not available
        elif command -v brew >/dev/null 2>&1 ; then
          printf "%s\t%s\n" "nickel" "using Homebrew"
          if brew install nickel ; then
            printf "%s\t%s\n" "nickel" "✅ installed $NICKEL_VERSION via Homebrew"
          else
            printf "%s\t%s\n" "nickel" "❌ Failed to install with Homebrew"
            exit 1
          fi
        else
          # Neither Cargo nor Homebrew available
          printf "%s\t%s\n" "nickel" "⚠️  Neither Cargo nor Homebrew found"
          printf "%s\t%s\n" "nickel" "Install one of:"
          printf "%s\t%s\n" "nickel" "  1. Cargo: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
          printf "%s\t%s\n" "nickel" "  2. Homebrew: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
          exit 1
        fi
      else
        # Non-macOS: download binary from GitHub
        printf "%s\t%s\n" "nickel" "installing $NICKEL_VERSION on $OS"

        # Map architecture names (GitHub uses different naming)
        local nickel_arch="$ARCH"
        [ "$nickel_arch" == "amd64" ] && nickel_arch="x86_64"

        # Build download URL
        local download_url="https://github.com/tweag/nickel/releases/download/${NICKEL_VERSION}/nickel-${nickel_arch}-${OS}"

        # Download and install
        if curl -fsSLO "$download_url" && chmod +x "nickel-${nickel_arch}-${OS}" && sudo mv "nickel-${nickel_arch}-${OS}" /usr/local/bin/nickel ; then
          printf "%s\t%s\n" "nickel" "installed $NICKEL_VERSION"
        else
          printf "%s\t%s\n" "nickel" "❌ Failed to download/install Nickel binary"
          exit 1
        fi
      fi
    elif [ -n "$CHECK_ONLY" ] ; then
      printf "%s\t%s\t%s\n" "nickel" "$nickel_version" "expected $NICKEL_VERSION"
    else
      printf "%s\t%s\n" "nickel" "already $NICKEL_VERSION"
    fi
  fi
  #if [ -n "$TERA_VERSION" ] && [ "$match" == "all" ] ||  [ "$match" == "tera" ]  ; then
  #  has_tera=$(type -P tera)
  #  num_version="0"
  #  [ -n "$has_tera" ] && tera_version=$(tera -V | cut -f2 -d" " | sed 's/teracli//g') && num_version=${tera_version//\./}
  #  expected_version_num=${TERA_VERSION//\./}
  #  [ -z "$num_version" ] && num_version=0
  #  if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
  #    if [ -x "$(dirname "$0")/../tools/tera_${OS}_${ARCH}" ] ; then
  #	      sudo cp "$(dirname "$0")/../tools/tera_${OS}_${ARCH}" /usr/local/bin/tera && printf "%s\t%s\n" "tera" "installed $TERA_VERSION"
  #    else
  #      echo "Error: $(dirname "$0")/../tools/tera_${OS}_${ARCH} not found !!"
  #      exit 2
  #    fi
  #  elif [ -n "$CHECK_ONLY" ] ; then
  #    printf "%s\t%s\t%s\n" "tera" "$tera_version" "expected $TERA_VERSION"
  #  else
  #    printf "%s\t%s\n" "tera" "already $TERA_VERSION"
  #  fi
  #fi
  if [ -n "$K9S_VERSION" ]  && [ "$match" == "all" ] ||  [ "$match" == "k9s" ]  ; then
    has_k9s=$(type -P k9s)
    num_version="0"
    [ -n "$has_k9s" ] && k9s_version="$( k9s version | grep Version | cut -f2 -d"v" | sed 's/ //g')" && num_version=${k9s_version//\./}
    expected_version_num=${K9S_VERSION//\./}
    [ -z "$num_version" ] && num_version=0
    if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      mkdir -p k9s && cd k9s &&
      curl -fsSLO https://github.com/derailed/k9s/releases/download/v${K9S_VERSION}/k9s_${ORG_OS}_${ARCH}.tar.gz &&
        tar -xzf "k9s_${ORG_OS}_${ARCH}.tar.gz" &&
        sudo mv k9s /usr/local/bin &&
        cd "$ORG" && rm -rf /tmp/k9s "/k9s_${ORG_OS}_${ARCH}.tar.gz" &&
        printf "%s\t%s\n" "k9s" "installed $K9S_VERSION"
    elif [ -n "$CHECK_ONLY" ] ; then
      printf "%s\t%s\t%s\n" "k9s" "$k9s_version" "expected $K9S_VERSION"
    else
      printf "%s\t%s\n" "k9s" "already $K9S_VERSION"
    fi
  fi
  if [ -n "$AGE_VERSION" ]  && [ "$match" == "all" ] ||  [ "$match" == "age" ]  ; then
    has_age=$(type -P age)
    num_version="0"
    [ -n "$has_age" ] && age_version="${AGE_VERSION}" && num_version=${age_version//\./}
    expected_version_num=${AGE_VERSION//\./}
    if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      curl -fsSLO https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz &&
        tar -xzf age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz &&
        sudo mv age/age /usr/local/bin &&
        sudo mv age/age-keygen /usr/local/bin &&
        rm -rf age "age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz" &&
        printf "%s\t%s\n" "age" "installed $AGE_VERSION"
    elif [ -n "$CHECK_ONLY" ] ; then
      printf "%s\t%s\t%s\n" "age" "$age_version" "expected $AGE_VERSION"
    else
      printf "%s\t%s\n" "age" "already $AGE_VERSION"
    fi
  fi
  if [ -n "$SOPS_VERSION" ]  && [ "$match" == "all" ] ||  [ "$match" == "sops" ]  ; then
    has_sops=$(type -P sops)
    num_version="0"
    [ -n "$has_sops" ] && sops_version="$(sops -v | grep ^sops |  cut -f2 -d" " | sed 's/ //g')" && num_version=${sops_version//\./}
    expected_version_num=${SOPS_VERSION//\./}
    [ -z "$num_version" ] && num_version=0
    if [ -z "$expected_version_num" ] ; then
      printf "%s\t%s\t%s\n" "sops" "$sops_version" "expected $SOPS_VERSION"
    elif [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then
      mkdir -p sops && cd sops &&
      curl -fsSLO https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.${OS}.${ARCH} &&
     	mv sops-v${SOPS_VERSION}.${OS}.${ARCH} sops &&
        chmod +x sops  &&
      sudo mv sops /usr/local/bin  &&
    	rm -f sops-v${SOPS_VERSION}.${OS}.${ARCH} sops &&
      printf "%s\t%s\n" "sops" "installed $SOPS_VERSION"
    elif [ -n "$CHECK_ONLY" ] ; then
      printf "%s\t%s\t%s\n" "sops" "$sops_version" "expected $SOPS_VERSION"
    else
      printf "%s\t%s\n" "sops" "already $SOPS_VERSION"
    fi
  fi
}

function _detect_tool_version {
  local tool=$1

  # Try to detect version using tool-specific commands
  case "$tool" in
    hcloud)
      hcloud version 2>/dev/null | grep -o 'hcloud [0-9.]*' | awk '{print $2}' || echo ""
      ;;
    upctl)
      upctl version 2>/dev/null | grep -i "Version" | head -1 | sed 's/.*Version:\s*//' | sed 's/[[:space:]]*$//' || echo ""
      ;;
    aws)
      aws --version 2>/dev/null | cut -d' ' -f1 | sed 's/aws-cli\///' || echo ""
      ;;
    nu | nushell)
      nu -v 2>/dev/null | head -1 || echo ""
      ;;
    nickel)
      nickel --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo ""
      ;;
    sops)
      sops -v 2>/dev/null | head -1 | awk '{print $2}' || echo ""
      ;;
    age)
      age --version 2>/dev/null | head -1 | sed 's/^v//' || echo ""
      ;;
    k9s)
      k9s version 2>/dev/null | grep -o "Version\s*v[0-9.]*" | sed 's/.*v//' || echo ""
      ;;
    *)
      # Fallback: try tool version
      $tool version 2>/dev/null | head -1 || echo ""
      ;;
  esac
}

function _normalize_version {
  local version=$1
  # Remove any 'v' prefix and return just major.minor
  echo "$version" | sed 's/^v//' | cut -d. -f1-2
}

function _should_install_tool {
  local tool=$1
  local target_version=$2
  local force_update=$3

  # Always install if --update flag is used
  if [ "$force_update" = "yes" ] ; then
    return 0
  fi

  # Get installed version
  local installed_version=$(_detect_tool_version "$tool")

  # If tool not installed, install it
  if [ -z "$installed_version" ] ; then
    return 0
  fi

  # Normalize versions for comparison (handle 0.109.0 vs 0.109)
  local norm_installed=$(_normalize_version "$installed_version")
  local norm_target=$(_normalize_version "$target_version")

  # If installed version equals target version, skip installation
  if [ "$norm_installed" = "$norm_target" ] ; then
    printf "%s\t%s\t%s\n" "$tool" "$installed_version" "already up to date"
    return 1
  fi

  # If versions differ, install (update)
  return 0
}

function _try_install_provider_tool {
  local tool=$1
  local options=$2
  local force_update=$3

  # Look for the tool in provider nickel/version.ncl files (Nickel is single source of truth)
  for prov in $(ls $PROVIDERS_PATH 2>/dev/null | grep -v "^_" )
  do
    if [ -r "$PROVIDERS_PATH/$prov/nickel/version.ncl" ] ; then
      # Evaluate Nickel file to JSON and extract version data (single source of truth)
      local nickel_file="$PROVIDERS_PATH/$prov/nickel/version.ncl"
      local nickel_output=""
      local tool_version=""
      local tool_name=""

      # Evaluate Nickel to JSON and capture output
      nickel_output=$(nickel export --format json "$nickel_file" 2>/dev/null)

      # Extract tool name and version from JSON
      tool_name=$(echo "$nickel_output" | grep -o '"name": "[^"]*"' | head -1 | sed 's/"name": "//;s/"$//')
      tool_version=$(echo "$nickel_output" | grep -o '"current": "[^"]*"' | head -1 | sed 's/"current": "//;s/"$//')

      # If this is the tool we're looking for
      if [ "$tool_name" == "$tool" ] && [ -n "$tool_version" ] ; then
        # Check if installation is needed
        if ! _should_install_tool "$tool" "$tool_version" "$force_update" ; then
          return 0
        fi

        if [ -x "$PROVIDERS_PATH/$prov/bin/install.sh" ] ; then
          # Set environment variables for the provider's install.sh script
          # Provider scripts have different interfaces, so set env vars for all of them
          if [ "$prov" = "upcloud" ] ; then
            # UpCloud expects: tool name as param, UPCLOUD_UPCTL_VERSION env var for version
            export UPCLOUD_UPCTL_VERSION="$tool_version"
            $PROVIDERS_PATH/$prov/bin/install.sh "$tool_name" $options
          elif [ "$prov" = "hetzner" ] ; then
            # Hetzner expects: version as param (from nickel/version.ncl)
            $PROVIDERS_PATH/$prov/bin/install.sh "$tool_version" $options
          elif [ "$prov" = "aws" ] ; then
            # AWS format - set env var and pass tool name
            export AWS_AWS_VERSION="$tool_version"
            $PROVIDERS_PATH/$prov/bin/install.sh "$tool_name" $options
          else
            # Generic: try version as parameter first
            $PROVIDERS_PATH/$prov/bin/install.sh "$tool_version" $options
          fi
          return 0
        fi
      fi
    fi
  done
  return 1
}

function _on_tools {
  local tools_list=$1
  shift  # Remove first argument

  # Check if --update flag is present
  local force_update="no"
  for arg in "$@"
  do
    if [ "$arg" = "--update" ] ; then
      force_update="yes"
      break
    fi
  done

  [ -z "$tools_list" ] || [[ "$tools_list" == -* ]] && tools_list=${TOOL_TO_INSTALL:-all}
  case $tools_list in
   "all")
      _install_tools "all" "$@"
      _install_providers "all" "$@"
      ;;
   "providers" | "prov" | "p")
      _install_providers "$@"
    ;;
    *)
      for tool in $tools_list
      do
        [[ "$tool" == -* ]] && continue
        # First try to find and install as provider tool
        if _try_install_provider_tool "$tool" "" "$force_update" ; then
          continue
        fi
        # Otherwise try core system tools
        _install_tools "$tool" "$@"
      done
  esac
}

set -o allexport
## shellcheck disable=SC1090
[ -n "$PROVISIONING_ENV" ] && [ -r "$PROVISIONING_ENV" ] && source "$PROVISIONING_ENV"
[ -r "../env-provisioning" ] && source ../env-provisioning
[ -r "env-provisioning" ] && source ./env-provisioning
#[ -r ".env" ] && source .env set
set +o allexport

export PROVISIONING=${PROVISIONING:-/usr/local/provisioning}

if [ -r "$(dirname "$0")/../versions" ] ; then
  . "$(dirname "$0")"/../versions
elif [ -r "$(dirname "$0")/versions" ] ; then
  . "$(dirname "$0")"/versions
fi

export CMDS_PROVISIONING=${CMDS_PROVISIONING:-"tree"}
PROVIDERS_PATH=${PROVIDERS_PATH:-"$PROVISIONING/extensions/providers"}

if [ -z "$1" ] ; then
  CHECK_ONLY="yes"
  _on_tools all
else
  [ "$1" == "-h" ] && echo "$USAGE" && shift
  [ "$1" == "check" ] && CHECK_ONLY="yes" && shift
  [ -n "$1" ] && cd /tmp && _on_tools "$@"
fi
exit 0
