#!/usr/bin/env bash

# Adapted from https://github.com/basecamp/omarchy/tree/master?tab=readme-ov-file

THIS_FILE=${BASH_SOURCE[0]}
THIS=${THIS:-$(basename "$THIS_FILE")}
LOG_FILE=${LOG_FILE:-"$THIS.log"}
LOG_LABEL=$(basename "$THIS_FILE")

function usage() {
  cat <<EOF
Generates a '.desktop' file for a web application, so that it can act as a stand alone application and 
be launched from an application launcher.

USAGE: $THIS [OPTIONS]

OPTIONS:
  -n | --name <name>:           The name of the application.

  -u | --url <url>:             The url used to launch the application.

  -i | --icon <icon>:           The icon for the application.

  -e | --exec <cmd>:            Custom execution command (optional).

  -m | --mime-types <types>:    MIME-types for the application (optional).

  -f | --file <file>:           Install from a spec in a json file.

  -l | --launch:                Launches in a new terminal window in interactive mode.

  --no-interactive:             Don't proceed to interactive mode. If required properties aren't set, 
                                then error. This is useful if using '--file' and there are parsing errors
                                or missing properties.

  --dry-run:                    Run in dry-run mode, which doesn't generate files or download icons.

  -h | --help:                  Show usage information.

EXAMPLES:

If no options or arguments are supplied, then it will start an interactive session that prompts for the
values.

  $ $THIS

Calling the app with named arguments:

  $ $THIS \\
      --name "My Awesome App" \\
      --url "https://awesome.com" \\
      --icon "https://awesome.com/assets/icon.png"

Using a json file as input:

  $ $THIS --file myapp.json

NOTES: 

The icon option can either be a url where we will download a png from or a local file. Local files
can either be the full path to the file or a file name of an icon located in '~/.local/share/applications/icons/'.

Interactive sessions do not give the option to use a custom execution command or supply the
MIME types, which are less frequently used options.

If using a json spec file, all keys are the same as their option name, except for mime-types use 'mime_types' as the
key in the json object. Although the 'exec' and 'mime_types' are not required in the spec file. A common json spec
file example would look like:

{
  "name": "My Awesome App",
  "url": "https://awesome.com",
  "icon: "https://awesome.com/assets/icon.png"
}

EOF
}

window_class="com.ghostty.install-webapp"
window_padding_x="2"

app_name=""
app_url=""
exec_cmd=""
dry_run="0"
file_mode_flag="0"
icon_ref=""
json_file=""
mime_types=""
launch_flag="0"
interactive_flag="0" # This is an internal flag, to not log some things when launch is used.
interactive_mode=false
no_interactive_flag="0"
SCRIPTS="${SCRIPTS:-$HOME/.local/scripts}"
XDG_DATA_HOME=${XDG_DATA_HOME}

while [[ $# -gt 0 ]]; do
  if [[ $1 == "-h" ]] || [[ $1 == "--help" ]]; then
    usage && exit 0
  elif [[ $1 == "-n" ]] || [[ $1 == "--name" ]]; then
    shift
    app_name=$1
  elif [[ $1 == "-u" ]] || [[ $1 == "--url" ]]; then
    shift
    app_url=$1
  elif [[ $1 == "-i" ]] || [[ $1 == "--icon" ]]; then
    shift
    icon_ref=$1
  elif [[ $1 == "-e" ]] || [[ $1 == "--exec" ]]; then
    shift
    exec_cmd=$1
  elif [[ $1 == "-m" ]] || [[ $1 == "--mime-types" ]]; then
    shift
    mime_types=$1
  elif [[ $1 == "-f" ]] || [[ $1 == "--file" ]]; then
    file_mode_flag="1"
    shift
    json_file=$1
  elif [[ $1 == "-l" ]] || [[ $1 == "--launch" ]]; then
    launch_flag="1"
  elif [[ $1 == "--interactive" ]]; then
    interactive_flag="1"
  elif [[ $1 == "--no-interactive" ]]; then
    no_interactive_flag="1"
  elif [[ $1 =~ ^--dry ]]; then
    dry_run="1"
  fi
  shift
done

log() {
  logging log --source "$THIS_FILE" "$@"
}

launch() {
  ghostty --class=$window_class --window-padding-x=$window_padding_x \
    --keybind="ctrl+c=quit" \
    -e "${BASH_SOURCE[0]}" --interactive
}

check_properties() {
  if [[ -z $app_name ]] || [[ -z $app_url ]] || [[ -z $icon_ref ]]; then
    return 1
  fi
  return 0
}

load_from_file() {
  if [[ ! -f $1 ]]; then
    log --error "File '$1' is not found or readable." && exit 1
  fi
  file=$(cat $1)
  app_name=$(echo $file | jq -r '.name // ""')
  app_url=$(echo $file | jq -r '.url // ""')
  icon_ref=$(echo $file | jq -r '.icon // ""')
  exec_cmd=$(echo $file | jq -r '.exec // ""')
  mime_types=$(echo $file | jq -r '.mime_types // ""')
}

print_properties() {
  log "\e[33mAPP NAME:\e[0m     $app_name"
  log "\e[33mURL:\e[0m          $app_url"
  log "\e[33mICON:\e[0m         $icon_ref"
  log "\e[33mEXEC:\e[0m         $exec_cmd"
  log "\e[33mMIME:\e[0m         $mime_types"
}

prompt_for_properties() {
  log "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"

  if [[ -z $app_name ]]; then
    app_name=$(gum input --prompt "Name> " --placeholder "My favorite web app")
  fi

  if [[ -z $app_url ]]; then
    app_url=$(gum input --prompt "URL> " --placeholder "https://example.com")
  fi

  if [[ -z $icon_ref ]]; then
    icon_ref=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG!)")
  fi
}

set_icon_ref() {
  # Refer to local icon or fetch remotely from URL
  local icon_dir="$XDG_DATA_HOME/applications/icons"
  local icon_path=""

  # Ensure the icon directory exists (useful if it's the first run.)
  [ ! -d $icon_dir ] && mkdir -p $icon_dir

  if [[ $icon_ref == https://* ]]; then
    icon_path="$icon_dir/$app_name.png"
    log "Downloading icon: $icon_ref"
    if [[ $dry_run == "0" ]]; then
      if curl -sL -o "$icon_path" "$icon_ref"; then
        icon_path="$icon_dir/$app_name.png"
      else
        log --error "Failed to download icon." && exit 1
      fi
    fi
  else
    # Check if the icon path is a file.
    if [ -f $icon_ref ]; then
      icon_path=$icon_ref
    else
      icon_path="$icon_dir/$icon_ref"
    fi
  fi

  icon_ref=$icon_path
}

generate_file() {

  cat >"$1" <<EOF
[Desktop Entry]
Version=1.0
Name=$app_name
Comment=$app_name
Exec=$exec_cmd
Terminal=false
Type=Application
Icon=$icon_ref
StartupNotify=true
EOF

  # Add mime types if provided
  if [[ -n $mime_types ]]; then
    echo "MimeType=$mime_types" >>"$1"
  fi

  chmod +x "$1"
}

################################################################################
# MAIN
################################################################################

# Setup logging file and label
source "$SCRIPTS/hypr/logging"
setup-logging "$LOG_FILE" "$LOG_LABEL"
export LOG_ENABLE_DRY_RUN="$dry_run"

if [[ -z "$XDG_DATA_HOME" ]]; then
  log "XDG_DATA_HOME not set"
  log "using ~/.local/share"
  XDG_DATA_HOME=$HOME/.local/share
fi

if [[ $launch_flag == "1" ]]; then
  launch && exit 0
fi

if [[ $file_mode_flag == "1" ]]; then
  load_from_file $json_file
fi

# Check that all properties are set, prompt for missing values if not.
check_properties
if [[ "$?" == "1" ]]; then

  # Check if the '--no-interactive' flag was passed and exit with error.
  [[ $no_interactive_flag == "1" ]] &&
    log --error "Required properties not set and '--no-interactive' flag was passed." &&
    exit 1

  # Only log this if not in interactive mode.
  [[ $interactive_mode == "0" ]] &&
    log "All required properties not set, prompting for missing properties."

  prompt_for_properties

  # Check properties again after prompting in interactive mode.
  check_properties
  if [[ "$?" == "1" ]]; then
    # Exit if they were not set during interactive mode.
    log --error "You must set app name, app URL, and icon URL!" && exit 1
  fi

  # Set flag that we are in interactive mode.
  interactive_mode=true
fi

desktop_file="$XDG_DATA_HOME/applications/$app_name.desktop"

# Parse the icon ref and download icon, if applicable.
set_icon_ref

# Check that an exec command is set, or default to the 'launch-webapp' script.
if [[ -z $exec_cmd ]]; then
  exec_cmd="$SCRIPTS/hypr/webapp launch $app_url"
fi

log "\e[032mCreating web app:\e[0m $desktop_file"
print_properties

if [[ $dry_run == "0" ]]; then
  generate_file "$desktop_file"
fi

if [[ $interactive_mode == true ]] && [[ $dry_run == "0" ]]; then
  log "You can now find $app_name using the app launcher (SUPER + SPACE)\n"
fi
