#!/usr/bin/env bash

# Basic logging utility functions that can be used by scripts to log
# to files.  This helps keep console clean for TUI's.  Will log
# messages to all registered files, which allows each script to
# declare their own logging file, but also print logs into parent
# processes files, for ease of discovery.
#
# Supports warning and error flags.
#
# Errors and warnings get logged to the file and to the console.
#
#
# EXAMPLE SETUP:
#
# source $SCRIPTS/hypr/logging
# THIS=$(basename ${BASH_SOURCE[0]})
#
# # Setup logging file and label.
# setup-logging "/tmp/$THIS.log" $THIS
#
# function log() {
#   logging log --source ${BASH_SOURCE[0]} "$@"
# }
#
# log "My log message."
# log --warning "My warning message."
# log --error "My error message."
#
set -e
set -o nounset
set -o pipefail

LOG_DIR=${LOG_DIR:-/tmp/logs}
LOG_INVOCATION_ID=${LOG_INVOCATION_ID:-""}
LOG_LABEL=${LOG_LABEL:-""}
# Run in dry run mode, which just prints to the console and does
# not log to the files.
LOG_ENABLE_DRY_RUN=${LOG_ENABLE_DRY_RUN:-"0"}
SCRIPTS=${SCRIPTS:-$HOME/.local/scripts}

warn_flag="0"
error_flag="0"

__msg() {
  if [[ -z "$*" ]]; then
    echo -e "\e[31m[ERROR]:\e[0m No logs were supplied."
    exit 1
  fi
  if [[ $warn_flag == "1" ]]; then
    echo -e "\e[33m[WARN]:\e[0m $*"
  elif [[ $error_flag == "1" ]]; then
    echo -e "\e[31m[ERROR]:\e[0m $*"
  else
    echo "$@"
  fi
}

__ensure_setup() {
  if [[ -z $LOG_FILE ]] || [[ -z $LOG_INVOCATION_ID ]] || [[ -z $LOG_LABEL ]] || [[ -z $LOG_DIR ]]; then
    echo -e "\e[31m[ERROR]:\e[0m Logging is not properly setup."
    echo "Perhaps you didn't call 'setup-logging' first."
    print_logger_env && exit 1
  fi
  if [[ ! -d $LOG_DIR ]]; then
    mkdir -p "$LOG_DIR"
  fi
}

logging() {
  __ensure_setup

  # Reset flags
  log_flag="0"
  echo_flag="0"
  warn_flag="0"
  error_flag="0"
  source_file=""
  args=()

  while [[ $# -gt 0 ]]; do
    if [[ $1 == "-w" ]] || [[ $1 == "--warn" ]] || [[ $1 == "--warning" ]]; then
      warn_flag="1"
    elif [[ $1 == "-e" ]] || [[ $1 =~ ^--error ]]; then
      error_flag="1"
    elif [[ $1 =~ ^--echo ]]; then
      echo_flag="1"
    elif [[ $1 == "-s" ]] || [[ $1 =~ ^--source ]]; then
      shift
      source_file="$1"
    elif [[ $1 == "log" ]]; then
      log_flag="1"
    else
      args+=("$1")
    fi
    shift
  done

  if [[ -z $source_file ]]; then
    echo -e "\e[31m[ERROR]:\e[0m Must supply the source file the logs originate from."
    exit 1
  fi

  if [[ -z "${args[*]}" ]]; then
    echo -e "\e[31m[ERROR]:\e[0m No log message supplied."
    exit 1
  fi

  msg="$(__msg "${args[@]}")"

  if [[ $LOG_ENABLE_DRY_RUN == "0" ]] && [[ $log_flag == "1" ]]; then
    # Loop over log files logging message to each file.
    for i in "${!LOG_FILE[@]}"; do
      local file=${LOG_DIR}/${LOG_FILE[i]}
      local id=$LOG_INVOCATION_ID
      local label=${LOG_LABEL[i]:-"$LOG_LABEL"}
      local time
      time=$(date '+%D %H:%M:%S')

      if [[ -z $file ]] || [[ -z $id ]] || [[ -z $label ]]; then
        echo "Logging not properly setup."
        exit 1
      fi

      local prefix="[id: $id][time: $time][source: \e[32m$source_file\e[0m][\e[34m$label\e[0m] :"
      local m="$prefix $msg"
      echo -e "$m" >>"$file"
    done

    # Also log errors and warning messages to the console.
    if [[ $error_flag == "1" ]] || [[ $warn_flag == "1" ]]; then
      echo -e "[id: $id]$msg"
    elif [[ $warn_flag == "1" ]]; then
      echo -e "[id: $id]$msg"
    elif [[ $echo_flag == "1" ]]; then
      echo -e "$msg"
    fi
  else
    # Dry run mode, so just log to the console
    echo -e "\e[34m[DRY RUN]:\e[0m $msg"
  fi
}

setup-logging() {
  label=""
  file=""

  if [[ "${#@}" == "1" ]]; then
    file="/tmp/$1.log"
    label="$1"
  else
    file="$1"
    label="$2"
  fi

  if [[ -z "$file" ]]; then
    echo -e "\e[31m[ERROR]:\e[0m Must supply a log file."
    exit 1
  fi

  if [[ -z "$label" ]]; then
    echo -e "\e[31m[ERROR]:\e[0m Must supply a logger label."
    exit 1
  fi

  # Only add files that aren't already in the LOG_FILE.
  if [[ ! $LOG_FILE =~ $file ]]; then
    LOG_FILE+=("$file")
  fi

  LOG_INVOCATION_ID=${LOG_INVOCATION_ID:-$RANDOM}

  if [[ -n $LOG_LABEL ]] && [[ ! $LOG_LABEL =~ $label ]]; then
    LOG_LABEL+=("${LOG_LABEL[@]}=>$label")
  elif [[ ! ${LOG_LABEL[*]} =~ $label ]]; then
    LOG_LABEL+=("$label")
  fi

  export LOG_DIR
  export LOG_FILE
  export LOG_LABEL
  export LOG_INVOCATION_ID
}

print_logger_env() {
  echo "LOG_FILE: ${LOG_FILE[*]}"
  echo "LOG_INVOCATION_ID: $LOG_INVOCATION_ID"
  echo "LOG_LABEL: ${LOG_LABEL[*]}"
}

export -f setup-logging
export -f logging
export -f print_logger_env
