#!/bin/bash


## Documentation
##########################################
# This script handles many identity



## Initialisation
##########################################

# Get the way the script is called
IDM_PATH=${_:-_none}
IDM_ARGS=${0-}

# Ensure we are running strict mode
set -euo pipefail

# Detect how this script was started
if [[ $IDM_PATH == $IDM_ARGS ]]; then
  IDM_CTX=executed
else
  IDM_CTX=sourced
  IDM_SOURCED_ARGS=${@:-_none}
fi

# Versionning infos
IDM_AUTHORS='mrjk'
IDM_VERSION='0.1 (beta)'
IDM_DATE='03/01/18'
IDM_LICENSE='MIT'
IDM_SCRIPT_NAME=idmgr



IDM_DIR_ROOT=${IDM_DIR_ROOT:-$( realpath "$(dirname $0)/../" )}


# Main initialisation settings
idm_init ()
{
  export EDITOR=${EDITOR:-vim}

  # App variables
  IDM_CONFIG_DIR=${XDG_CONFIG_HOME:-~/.config}/idmgr

  IDM_DIR_ID=${IDM_DIR_ID:-$IDM_CONFIG_DIR/id}
  IDM_DIR_LIB=${IDM_DIR_LIB:-$IDM_DIR_ROOT/lib}
  
  # Create directories
  mkdir -p $IDM_CONFIG_DIR $IDM_DIR_ID

  IDM_MOD_FILES=$(idm_mod_files)
  IDM_MOD_ORDER=$(idm_mod_list)

  # Load modules
  for i in $IDM_MOD_FILES ; do
    source $i
  done
}


idm_core_help ()
{
  echo ""
  echo "  idmgr - Identity Manager for your shell"
  echo ""
  echo "Introduction:"
  echo "  Identity Manager can manage your ssh keys, your pass,"
  echo "  your pgp keys, kerberos and many other related identity item."
  echo ""
  echo "Core commands:"
  printf "  %-20s: %s\n" "enable <id>" "Enable (and start) id"
  printf "  %-20s: %s\n" "disable <id>" "Disable id"
  printf "  %-20s: %s\n" "kill <id>" "Kill id and its associated processes"
  echo 

  for i in ${IDM_MOD_ORDER//:/ } ; do
    local val="idm_${i}_help"
    echo -n "$i: "
    ${val} 2>/dev/null || \
      {
        true
        echo "No help"
      }
    echo ""
  done

  echo "License:"
  echo "  $IDM_VERSION, $IDM_DATE"
  echo "  $IDM_LICENSE, $IDM_AUTHORS"

}



## Utils
##########################################


idm_log ()
{
  local level=$1
  shift || true
  local msg="$@"

  # Take from stdin if no message ...
  [[ "$msg" = - ]] && msg=$( cat < /dev/stdin )

  local color=
  local reset='\033[0m'
  case $level in
    ERR)
      color='\033[0;31m'
      ;;
    WARN|TIP)
      color='\033[0;33m'
      ;;
    NOTICE)
      color='\033[0;32m'
      ;;
    INFO)
      color='\033[0;37m'
      ;;
    DEBUG)
      color='\033[0;31m'
      ;;
    RUN)
      color='\033[0;34m'
      ;;
    CODE)
      echo "$msg"
      return
      ;;
    DUMP)
      color='\033[0;36m'
      echo -e "$color$msg$reset" | sed 's/^/  /'
      return
      ;;
    PREFIX)
      color='\033[0;34m'
      ;;
  esac

  if [[ -n "$level" ]]; then
    printf "$color%6s$reset: %s\n" "$level" "$msg" >&2
  else
    echo "Error while log output msg: $message"
  fi
}

idm_get ()
{
  local item=$1
  local value=${2-}

  case $item in
    all_id)
      for id in $IDM_DIR_ID/*.env; do
        id=${id%%\.env}
        echo "${id##*/}"
      done 
    ;;

    all_id_files)
      ls $IDM_DIR_ID/*.env || true
    ;;

    id_config)
      if [ -f "$value" ]; then
        echo "id=$value"
        cat $IDM_DIR_ID/$value.env
      else
        return 1
      fi
    ;;

    *)
      idm_log ERR "Cannot get item '$item'"
    ;;
  esac
}

idm_validate ()
{
  local type=$1
  local value=${2-}

  case $type in
    id)
      [ "$value" != '_' ] && \
        [[ "$value" =~ ^[a-zA-Z0-9_-]+$ ]] && return
    ;;
    id_config)
      [[ -f "$IDM_DIR_ID/$value.env" ]] && return
    ;;
    is_enabled)
      [ ! -z "${SHELL_ID}" ] && return
    ;;
    is_disabled)
      [ -z "${SHELL_ID}" ] && return
    ;;
      
    *)
      idm_log ERR "Cannot validate type '$type'"
    ;;
  esac

  return 1
}

# Should be replaced by idm_validate ?
idm_is_enabled ()
{
  local id=${1}
  idm_validate id $id || \
    idm_exit 1 ERR "You need to activate an id first"

}



idm_exit ()
{
  local rc=${1:-0}
  local msg lvl
  
  # Check exit status
  if [ "$#" -eq 3 ]; then
    lvl=${2:-DEBUG}
    msg=${3:-}
  else
    lvl=DEBUG
    msg=${2:-}
  fi

  if [[ "$rc" -ne 0 ]]; then
    idm_log $lvl "$msg (rc=$rc)"
  else
    idm_log $lvl "$msg"
  fi

  # Remove trap
  trap "" INT TERM EXIT

  # Exit for good
  exit $rc
}

idm_exit_trap ()
{
  idm_log DEBUG "Exit trap"
}

idem_reverse_doted_list ()
{
  local list=$1
  awk 'BEGIN{FS=OFS=":"} {s=$NF; for (i=NF-1; i>=1; i--) s = s OFS $i; print s}' <<<"$list"
}


## IDM Internal
##########################################


# Takes a list of files to scan for deps
idm_mod__order ()
{
    export IDM_MOD_FILES="$( xargs <<<$@ )"

    # Generate dependency order
    #result=$(
      for f in $IDM_MOD_FILES; do
        mod_name=${f##*mod_}
        mod_name=${mod_name%\.sh}

        # A bit hackish ...
        IDM_MOD_DEPS=$( grep '^IDM_MOD_DEPS=' $f )
        IDM_MOD_DEPS=${IDM_MOD_DEPS##*=}
        IDM_MOD_DEPS=${IDM_MOD_DEPS//[^a-z0-9 ]}
        IDM_MOD_DEPS="$( tr ' ' '\n' <<<${IDM_MOD_DEPS} )"

        # Output
        echo -e "$( xargs -n1 -I{} echo {} "$mod_name" <<<"$IDM_MOD_DEPS" )"
      done | tsort | xargs
    #)

    #idm_log DEBUG "Dependencies order: $result"
    #echo $result

}

idm_mod_files ()
{
  #p=$IDM_DIR_ROOT/lib ${PATH//:/ }
  p=$IDM_DIR_LIB
  find $p -name 'idmgr_mod_*.sh' | xargs
}

idm_mod_list ()
{
  local mods=

  if [ -z "${@-}" ] ; then
    mods=$( idm_mod__order $(idm_mod_files) )
  else
    mods=$( idm_mod__order ${@} )
  fi

  echo ":${mods// /:}:"
}


## Required functions
##########################################
idm_core_ls ()
{
  local id=${1}

  for i in ${IDM_MOD_ORDER//:/ } ; do
    local val="idm_${i}_ls"
    idm_log NOTICE "List $i"

    ${val} $id 2>/dev/null || \
      {
        true
        idm_log INFO "No listing function for $i"
      }
    echo ""
  done
}


idm_core_enable ()
{
  local id=${1:-${SHELL_ID-}}
  local conf

  # Local checks
  idm_validate id $id || idm_exit 1 ERR "You must provide an id"
  idm_validate id_config $id || idm_exit 1 ERR "Configuration '$id' does not exists"

  # Check if workspace is enabled
  if [ "${SHELL_ID-}" == "$id" ]; then
    idm_exit 0 INFO "Your workspace is already activated"
  elif [ -n "${SHELL_ID-}" ]; then
    idm_exit 0 WARN "Your workspace is already activated with $SHELL_ID"
  fi

  # Retrieve environment config
  conf="$IDM_DIR_ID/$id.env"

  # Notice user
  (
    . $conf

    for i in ${IDM_MOD_ORDER//:/ } ; do
      local val="idm_${i}_enable"
      #idm_log INFO "Loading $i ..."

      ${val} $id 2>/dev/null || \
        {
          true
          idm_log INFO "No enable function for $i"
        }
      echo ""
    done

  ) | idm_log CODE -

  idm_log NOTICE "Identity '$id' is loaded"

}

idm_core_disable ()
{
  local id=${1}
  idm_is_enabled $id

  # Reverse module unloading
  IDM_MOD_ORDER="$( idem_reverse_doted_list $IDM_MOD_ORDER )" 

  # Loop over disable functions
  (
    for i in ${IDM_MOD_ORDER//:/ } ; do
      local val="idm_${i}_disable"
      #idm_log INFO "Disable $i ..."

      ${val} $id 2>/dev/null || \
        {
          true
          idm_log INFO "No disable function for $i"
        }
      echo ""
    done
  ) | idm_log CODE -

  # Inform user
  idm_log NOTICE "Id $id is disabled"
}

idm_core_kill ()
{
  local id=${1}
  idm_is_enabled $id

  # Reverse module killing
  IDM_MOD_ORDER="$( idem_reverse_doted_list $IDM_MOD_ORDER )" 

  # Kill all modules
  (
    for i in ${IDM_MOD_ORDER//:/ } ; do
      local val="idm_${i}_kill"
      #idm_log INFO "Disable $i ..."

      ${val} $id 2>/dev/null || \
        {
          true
          idm_log INFO "No kill function for $i"
        }
      echo ""
    done
  ) | idm_log CODE -

  # Inform user
  idm_log NOTICE "Id $id is safely killed"

}


## Extended functions
##########################################

idm_core_mods()
{
  local id=${1-}
  idm_mod_list
}

idm_core_sourced_words()
{
  local id=${1-}
  local words=

  # When we are asking to output source words
  words="enable disable kill shell quit q $(idm_get all_id | xargs)"

  echo ":${words// /:}:"

}

idm_core_shell ()
{
  IDM_SRC_WORDS=$( $IDM_DIR_ROOT/bin/idmgr sourced_words )

  echo "IDM_DIR_ROOT='$IDM_DIR_ROOT'"
  echo "IDM_SRC_WORDS='$IDM_SRC_WORDS'"
  echo "IDM_BIN=${IDM_BIN:-$IDM_DIR_ROOT/bin/idmgr}"

  tail -n +2  $IDM_DIR_ROOT/shell/bash.sh
}

idm_core_completion ()
{
  cat $IDM_DIR_ROOT/comp/pass.sh
  cat $IDM_DIR_ROOT/comp/yadm.sh
  cat $IDM_DIR_ROOT/comp/ssh.sh
  cat $IDM_DIR_ROOT/comp/gpg.sh
  cat $IDM_DIR_ROOT/comp/idmgr.sh
}


## Entry points
##########################################


idm_exit_trap() {
    rc=$?
    [[ $rc -ne 0 ]] && idm_log ERR "The script exited with exit code: $rc"
    exit $rc
}

trap "idm_exit_trap" INT TERM EXIT


idm_menu_main () 
{
  #set -x

  local menu=
  local action=
  local id=
  local opt=
  local shell_id=${SHELL_ID-:_}

  idm_init

  # Three way parsing
  if [ "$#" -eq 0 ]; then

    if [ -z "${SHELL_ID-}" ]; then
      # Not activated, show all ids
      menu=id
      action=ls
      id=_
    else
      # Activated, show all id settings
      menu=core
      action=ls
      id=$shell_id
    fi

  else

    # Check id constraint
    if idm_validate id_config $1 ; then
      menu=core
      action=enable
      id=$1
    elif idm_validate id_config ${2-} ; then
      menu=core
      action=ls
      id=$2
      shift 2 && opt=${@} || true
    elif idm_validate id_config ${3-} ; then
      menu=$1
      action=$2
      id=$3
      shift 3 && opt=${@} || true

    # Check mod contraint
    elif [[ "${IDM_MOD_ORDER}" =~ :$1: ]]; then
        menu=$1
        action=${2:-ls}
        id=$shell_id
        shift 2 && opt=${@} || true

    # Free form
    else
      if [ "$#" -eq 1 ]; then
        menu=core
        action=${1}
        id=$shell_id
      # elif [ "$#" -eq 2 ]; then
      #   menu=${1}
      #   action=${2}
      #   id=$shell_id
      #   shift 2 && opt=${@} || true
      else
        menu=${1}
        action=${2}
        id=$shell_id
        shift 2 && opt=${@} || true
        
      fi
    fi
  fi

  # Aliases
  case $action in
    quit|q)
      action=disable
      ;;
  esac

  # Dispatch
  #idm_log DEBUG "menu=$menu action=$action id=$id opt=$opt"
  if  [ "$( type -t idm_${menu}_${action} )" = function ]; then
    idm_${menu}_${action} $id $opt
    return $?
  elif  [ "$( type -t idm_${menu} )" = function ]; then
    idm_${menu} ${action} $id $opt
    return $?
  fi

  idm_log DEBUG "menu=$menu action=$action id=$id opt=$opt"
  idm_exit 1 "Command not matched"


}



## Main
##########################################


idm_menu_main $@





# OLD PIEECES OF CODE


    # echo "export MANPAGER=less"
    # #echo "export VIMINIT=let \$MYVIMRC='$XDG_CONFIG_HOME/vim/vimrc' \| source \$MYVIMRC"
    # #echo "export VIMINIT='let \$MYVIMRC="$XDG_CONFIG_HOME/vim/vimrc"'"

    # # Misc
    # echo "export PYENV_ROOT=${XDG_OPT_HOME}/pyenv"
    # echo "export PYTHONUSERBASE=${XDG_OPT_HOME}/python"
    # echo "export PYTHONZ_ROOT=${XDG_OPT_HOME}/pythonz"
    # echo "export PIPSI_BIN_DIR=${XDG_OPT_HOME}/python-venv/bin"

    # echo "export LUA_CPATH=${XDG_OPT_HOME}/lua/?.so"
    # echo "export LUA_PATH=${XDG_OPT_HOME}/lua/?.lua"
    # echo "export LUAROCKS_CONFIG=~/.config/lua-${id}/luarocks.lua"

    # echo "export GEM_HOME=${XDG_OPT_HOME}/ruby"
    # echo "export GEMRC=~/.config/ruby-${id}/gemrc"
    # echo "export GEM_SPEC_CACHE=${XDG_OPT_HOME}/ruby/gem/specs"

    # echo "export COMPOSER_CACHE_DIR=${XDG_OPT_HOME}/composer"
    # echo "export COMPOSER_HOME=${XDG_OPT_HOME}/composer"

    # echo "export NPM_CONFIG_USERCONFIG=~/.config/npmrc"
    # echo "export VAGRANT_HOME=${XDG_OPT_HOME}/vagrant"
    # echo "export GOPATH=${XDG_OPT_HOME}/go"
