#!/bin/bash
SOURCE="$0"
source /usr/lib/elive-tools/functions 2>/dev/null || true
EL_REPORTS="1"
el_make_environment 2>/dev/null || true
. gettext.sh 2>/dev/null || true
TEXTDOMAIN="elive-assistant"
export TEXTDOMAIN
tmpdir="/tmp/.$(basename $SOURCE)-$USER/$$"
MODELS_FILE="/etc/elive-assistant/models"
#set -x

# get user configuration variables
for file in ~/.bash_profile ~/.profile ; do
    if [[ -s "$file" ]] ; then
        eval "$( grep --color=never -a -P '^(?!.*(PATH|SHELL|SH)=)[[:space:]]*((export[[:space:]]+)?[[:alnum:]_]+=(".*"|'"'"'.*'"'"'|.*[^[:space:]]))' "$file" )"
    fi
done

# personal place for keys
source ~/.config/elive-dev/settings 2>/dev/null || true
# common place for key
source ~/.env 2>/dev/null || true

# debug
#set -e

#trap "exit_ok" EXIT
trap "exit_error" 1 3 5 6 14 15 ERR
exit_error(){
    if [[ -e "${tmpdir}/audio.ogg" ]] ; then
        el_notify soft "gtk-dialog-error" "Fail" "Request failed"
        sleep 1
        ( mpv --really-quiet "${tmpdir}/audio.ogg" 1>/dev/null 2>&1 & )
    fi
    stop_notification

    el_error "on func ${FUNCNAME[1]} - $(caller)"
    exit
}

ctrl_c(){
    stop_notification
    exit 1
}
trap ctrl_c INT

stop_notification(){
    if [[ -s "$(dirname $tmpdir )/notification_id" ]] ; then
        notification_id="$( cat "$(dirname $tmpdir)/notification_id" )"
        if [[ -n "$notification_id" ]] ; then
            notify-send -i audio-input-microphone Listening "Finished" -t 10 -r $notification_id -e
            #( killall notification-daemon ; /usr/lib/notification-daemon/notification-daemon & )
        fi
    fi
}

# Wrapper for curl to retry on TLS/SSL connection errors
curl(){
    local retries=5
    local count=0
    local exit_code
    local tmp_err
    tmp_err="$(mktemp)"

    while (( count < retries )) ; do
        command curl "$@" 2>"$tmp_err"
        exit_code=$?

        if [[ $exit_code -eq 0 ]] ; then
            rm -f "$tmp_err"
            return 0
        fi

        # Check for TLS/SSL connection errors (exit code 35 or specific error messages)
        if [[ $exit_code -eq 35 ]] || grep -qsiE "TLS connect error|unexpected eof while reading|SSL routines" "$tmp_err" ; then
            count=$((count + 1))
            el_debug "Curl failed with TLS/SSL error (exit code $exit_code). Retrying ($count/$retries) in 2 seconds..."
            sleep 2
        else
            # For other errors, do not retry
            cat "$tmp_err" >&2
            rm -f "$tmp_err"
            return $exit_code
        fi
    done

    cat "$tmp_err" >&2
    rm -f "$tmp_err"
    return $exit_code
}



#XDG_CACHE_HOME="${HOME}/.cache"
#cache_dir="${XDG_CACHE_HOME}/$( basename "$SOURCE" )"
files_generated="$( xdg-user-dir DOCUMENTS )/elive-assistant"


# useful variables / user data:
username="$( awk -v user="$USER" -v FS=":" '{if ($1 == user) print $5}' /etc/passwd )"
if echo -e "$username" | grep -qsiE "(^Elive|user)" ; then
    username="$USER"
fi


# is input from a pipe?
if [[ ! -t 0 ]] ; then
    is_stdin=1
    stdin_message="$( cat 2>&1 | dos2unix | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" )"
fi


#
# wrappers to allow both GUI and CLI
#

message_title="$( printf "$( eval_gettext "Supra-Intelligent Assistant for Elive" )" "" )"
message_maybelater="$( printf "$( eval_gettext "Later" )" "" )"
message_disable="$( printf "$( eval_gettext "Disable" )" "" )"


guitool="yad --on-top --image=robot --center "

# END wrappers


variables_fixes(){
    # formal or informal tones (if used)
    case "$TEXT_TONE" in
        formal)    TEXT_TONE="prefer_more" ; ;;
        informal)  TEXT_TONE="prefer_less" ; ;;
    esac
    TEXT_TONE="${TEXT_TONE:="default"}"
}

# Functions
model_switch(){
    local model_target="${1}"
    local provider_target="${2}"

    # If provider is not specified, try to infer it from model name or defaults
    if [[ -z "$provider_target" ]] ; then
        case "$model_target" in
            gpt-*|tts-*|whisper-*|dall-e-*) provider_target="openai" ;;
            deepseek-*) provider_target="deepseek" ;;
            "accounts/fireworks/"*) provider_target="fireworks" ;;
            claude-*) provider_target="anthropic" ;;
            gemini-*) provider_target="google" ;;
            yi-*) provider_target="z.ai" ;;
            mistral-*) provider_target="mistral" ;;
            llama-*) provider_target="groq" ;;
            *) provider_target="${provider_wanted:-deepseek}" ;;
        esac
    fi

    # Retrieve provider metadata from models file
    local provider_data
    provider_data=$(jq -c --arg pid "$provider_target" '.providers[] | select(.id == $pid)' "$MODELS_FILE" 2>/dev/null)
    if [[ -z "$provider_data" ]]; then
        el_error "Provider '$provider_target' not found in $MODELS_FILE"
        exit 1
    fi

    # Set API key from environment variable defined in models file
    local api_env
    api_env=$(echo "$provider_data" | jq -r '.api_key_env // empty')
    if [[ -n "$api_env" ]]; then
        apikey="${!api_env}"
    else
        apikey=""
    fi

    # Determine the type of endpoint needed (chat, transcription, speech, images)
    local api_type
    case "$model_target" in
        whisper*|"whisper"*)
            api_type="transcriptions"
            ;;
        tts*|"tts"*|eleven*|"eleven"*)
            api_type="speech"
            ;;
        dall-e*|"dall-e"*)
            api_type="images"
            ;;
        deepl*)
            api_type="translate"
            ;;
        *)
            api_type="chat"
            ;;
    esac

    # Retrieve the correct base URL from the provider data
    local base_url
    base_url=$(echo "$provider_data" | jq -r --arg bt "$api_type" '.base_urls[$bt] // empty' 2>/dev/null)
    # Fallback to chat URL if the specific URL is missing (e.g. provider may not have speech)
    if [[ -z "$base_url" ]] && [[ "$api_type" != "chat" ]]; then
        base_url=$(echo "$provider_data" | jq -r '.base_urls["chat"] // empty' 2>/dev/null)
    fi

    # Allow overriding the base URL for deepL via configuration
    if [[ "$provider_target" == "deepl" ]] && [[ -n "${conf_deepl_endpoint:-}" ]]; then
        base_url="$conf_deepl_endpoint"
    fi

    model_url="$base_url"

    # Set voice_id for providers that use it (ElevenLabs, etc.)
    voice_id=$(echo "$provider_data" | jq -r '.voice_id // ""' 2>/dev/null)

    # If model_target is empty, get default model for provider and api_type
    if [[ -z "$model_target" ]] && [[ -n "$provider_target" ]]; then
        model_target=$(jq -r --arg pid "$provider_target" --arg bt "$api_type" '.providers[] | select(.id == $pid) | (.default_models[$bt] // .default_models.chat // empty)' "$MODELS_FILE" 2>/dev/null)
    fi

    model="$model_target"
    provider_wanted="$provider_target"

    # Override model for audio/whisper based on provider if not manually set
    if [[ -z "${model_manually_set:-}" ]] && [[ "$model_target" == "whisper"* ]]; then
        case "$provider_wanted" in
            groq)
                model="whisper-large-v3"
                ;;
            # Add other provider‑specific audio model names here
            # openai) model="whisper-1" ;; # already default
            *)
                # Keep default (whisper‑1 or whatever was passed)
                ;;
        esac
    fi
}

auto_select_provider_model(){
    local mode="$1"
    local tasktype
    case "$mode" in
        listen|audio) tasktype="transcription" ;;
        speech|speak|sr) tasktype="tts" ;;
        paint) tasktype="image" ;;
        *) tasktype="chat" ;;
    esac

    if [[ -n "$provider_wanted" ]]; then
        local env_name
        env_name=$(jq -r --arg pid "$provider_wanted" '.providers[] | select(.id == $pid) | .api_key_env' "$MODELS_FILE" 2>/dev/null)
        if [[ -n "${!env_name}" ]]; then
            model=$(jq -r --arg pid "$provider_wanted" --arg mtype "$tasktype" '.providers[] | select(.id == $pid) | (.default_models[$mtype] // .default_models.chat)' "$MODELS_FILE" 2>/dev/null)
            if [[ -n "$model" && "$model" != "null" ]]; then
                return 0
            fi
        fi
    fi

    local order
    order=$(jq -r --arg tk "$tasktype" '.tasks[$tk].order[]?' "$MODELS_FILE" 2>/dev/null)
    if [[ -z "$order" ]]; then
        case "$tasktype" in
            transcription) order="groq openai" ;;
            tts) order="groq openai elevenlabs" ;;
            image) order="openai" ;;
            *) order="deepseek openai groq anthropic google fireworks mistral openrouter together z.ai kimi perplexity" ;;
        esac
    fi
    for pid in $order; do
        if [[ "$pid" == "local" ]]; then
            provider_wanted="local"
            model=""
            return 0
        fi
        local env_name
        env_name=$(jq -r --arg pid "$pid" '.providers[] | select(.id == $pid) | .api_key_env' "$MODELS_FILE" 2>/dev/null)
        if [[ -n "${!env_name}" ]]; then
            provider_wanted="$pid"
            model=$(jq -r --arg pid "$pid" --arg mtype "$tasktype" '.providers[] | select(.id == $pid) | (.default_models[$mtype] // .default_models.chat)' "$MODELS_FILE" 2>/dev/null)
            if [[ -z "$model" || "$model" == "null" ]]; then
                continue
            fi
            return 0
        fi
    done
    el_error "No available provider with valid API key for task $mode"
    exit 1
}






result_show_compare(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local message answer
    message="$1"
    answer="$2"

    message="$( echo -e "$message" )"

    if ((is_typing_wanted)) ; then
        return
    fi

    if ((is_interactive)) || ! ((is_gui_wanted)) ; then
        echo -e "${el_c_n}" 1>&2
        wdiff -w "$(tput bold;tput setaf 1)" -x "$(tput sgr0)" -y "$(tput bold;tput setaf 2)" -z "$(tput sgr0)" <( printf "%s\n" "$message" ) <( printf "%s\n" "$answer" ) 1>&2 || true
        echo -e "${el_c_gr}-------------------------------------------------------------------------------${el_c_n}\n" 1>&2
    else
        if $guitool --question --title="$message_title" --text="$( eval_gettext "Visualize differences?" )" ; then
            meld <( printf "%s\n" "$message" | tr '\n' ' ' | sed -e 's|\.|.\n|g' -e 's|,|,\n|g' -e 's|;|;\n|g' ) <( printf "%s\n" "$answer" | tr '\n' ' ' | sed -e 's|\.|.\n|g' -e 's|,|,\n|g' -e 's|;|;\n|g' ) &
            LC_ALL=C sleep 1.8

            urxvt -hold -title "$( eval_gettext "Close the terminal when finished" )"  -e bash -c "wdiff -w \"\$(tput bold;tput setaf 1)\" -x \"\$(tput sgr0)\" -y \"\$(tput bold;tput setaf 2)\" -z \"\$(tput sgr0)\" <( printf \"%s\" \"$message\" ) <( printf \"%s\" \"$answer\" ) || true" &

            wait
        fi
    fi
}

type_with_xdotool(){
    local text_to_type="$1"
    local delay="$2"
    local xkb_layout_bak

    text_to_type="$( printf "%s\n" "$text_to_type" | sed -e '/./,$!d' -e '1s/^[[:space:]]*//' )"

    if ! el_dependencies_check "xdotool|setxkbmap" ; then
        el_dependencies_install "xdotool|x11-xkb-utils"
    fi

    local current_rules current_model current_layout current_variant current_options restore_cmd
    # Capture current setxkbmap settings
    current_rules=$(setxkbmap -query | awk '/^rules:/ {print $2}')
    current_model=$(setxkbmap -query | awk '/^model:/ {print $2}')
    current_layout=$(setxkbmap -query | awk '/^layout:/ {print $2}')
    current_variant=$(setxkbmap -query | awk '/^variant:/ {print $2}')
    current_options=$(setxkbmap -query | awk '/^options:/ {print $2}')

    # Temporarily set the desired layout for typing
    setxkbmap us -variant altgr-intl &>/dev/null
    xdotool type --delay "$delay" "$text_to_type"

    # Restore original setxkbmap settings
    restore_cmd="setxkbmap"
    [[ -n "$current_rules" ]] && restore_cmd+=" -rules $current_rules"
    [[ -n "$current_model" ]] && restore_cmd+=" -model $current_model"
    [[ -n "$current_layout" ]] && restore_cmd+=" -layout $current_layout"
    [[ -n "$current_variant" ]] && restore_cmd+=" -variant $current_variant"
    [[ -n "$current_options" ]] && restore_cmd+=" -option $current_options"

    el_info "Restoring keymap:  $restore_cmd"
    eval "$restore_cmd" &>/dev/null

    # Restore xmodmap if the user has a custom configuration
    if [[ -f "$HOME/.Xmodmap" ]] ; then
        el_info "Restoring Xmodmap:  xmodmap ~/.Xmodmap"
        xmodmap "$HOME/.Xmodmap" &>/dev/null
    fi
}

result_show_plain(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer
    answer="$1"

    # only type, no other results
    if ((is_typing_wanted)) ; then
        # speak result too
        #( el_speak_text -f "$answer" & )

        # type result
        type_with_xdotool "$answer" "4"
        return
    fi

    if ((is_stdout_wanted)) ; then
        echo -e "$answer"
    else
        if ! ((is_interactive)) || ((is_gui_wanted)) ; then
            echo -e "$answer" | xclip -i -selection clipboard

            # update notification with a message of copied
            if [[ -s "$(dirname $tmpdir )/notification_id" ]] ; then
                notification_id="$( cat "$(dirname $tmpdir)/notification_id" )"
                if [[ -n "$notification_id" ]] ; then
                    local message_notification_copied
                    message_notification_copied="$( printf "$( eval_gettext "Result copied" )" "" )"
                    verify_notification_system
                    notify-send -i audio-input-microphone "${message_notification_copied}" "$answer" -t 3400 -r $notification_id -e
                fi
            fi
        else
            # default
            echo -e "$answer"
        fi
    fi

}

result_show_dialog_copy(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer
    answer="$1"

    if ((is_stdout_wanted)) || ((is_typing_wanted)) ; then
        result_show_plain "$answer"
        return
    fi

    local message_copy
    message_copy="$( printf "$( eval_gettext "Copy" )" "" )"
    local message_copied
    message_copied="$( printf "$( eval_gettext "Copied" )" "" )"
    local message_done
    message_done="$( printf "$( eval_gettext "Done" )" "" )"
    local message_speak
    message_speak="$( printf "$( eval_gettext "Listen" )" "" )"
    local message_conversate
    message_conversate="$( printf "$( eval_gettext "Converse" )" "" )"
    local message_tryagain
    message_tryagain="$( printf "$( eval_gettext "New answer" )" "" )"

    #if false ; then
    if ((is_interactive)) || ! ((is_gui_wanted)) ; then
        printf "%s\n" "$answer"
    else
        #if printf "%s\n" "$answer" | $guitool --width=620 --height="450" --text-info --title="$message_title" --ok-label="$message_copy" --cancel-label="$message_done" ; then
        printf "%s\n" "$answer" | yad --on-top --image=robot \
            --width=600 --height=400 --center \
            --text-info --no-markup --wrap \
            --title="$message_title - $mode mode" \
            --button=gtk-close --button="${message_tryagain}"!reload:13 --button="${message_speak}!stock_volume:12" --button="${message_conversate}!chat":10 --button=gtk-copy:11  --escape-ok
        returned=$?
        case "$returned" in
            10)
                echo -e "$message_complete_g" | xclip -i -selection clipboard
                local message_paste_full
                message_paste_full="$( printf "$( eval_gettext "Paste the generated request into the website to run it again." )" "" )"

                el_notify soft "gtk-dialog-info" "$message_copied" "$message_paste_full"
                web-launcher "https://chat.openai.com/chat"
                # wait some time before to exit from this tool because otherwise the copy-paste is lost
                is_wait_needed=1
                ;;
            11)
                # copy to clipboard
                echo -e "$answer" | xclip -i -selection clipboard
                is_wait_needed=1
                ;;
            12)
                # copy to clipboard
                echo -e "$answer" | el_speak_text -f
                ;;
            13)
                # new answer
                if ((is_gui_wanted)) ; then
                    $SOURCE --gui "$mode" "$message_original"
                else
                    $SOURCE "$mode" "$message_original"
                fi
                exit
                ;;
            0|*)
                true
                ;;
        esac
    fi
}

verify_notification_system(){
    local has_notifications=0

    if ! pidof "notification-daemon" 1>/dev/null 2>&1 ; then
        notification-daemon-restarter
        LC_ALL=C sleep 0.3
    fi

    if notify-send -t 1 "Elive Assistant" "Checking notification system..." &>/dev/null ; then
        has_notifications=1
    else
        if command -v notification-daemon-restarter &>/dev/null ; then
            el_debug "Notification system check failed. Attempting to restart notification daemon using notification-daemon-restarter..."
            notification-daemon-restarter
            if notify-send -t 1 "Elive Assistant" "Checking notification system..." &>/dev/null ; then
                has_notifications=1
            else
                sleep 1
                if notify-send -t 500 "Elive Assistant" "Checking notification system..." &>/dev/null ; then
                    has_notifications=1
                fi
            fi
        else
            has_notifications=0
        fi
    fi

    if [[ "$has_notifications" -eq 0 ]] ; then
        local error_msg
        error_msg="$( printf "$( eval_gettext "The notification system is not available, not running, or overloaded. A working notification service is required for this feature in GUI mode." )" "" )"
        el_error_wrapper "$error_msg"
        exit 1
    fi
}

result_show_notification(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer title
    title="$1"
    shift
    answer="$1"
    shift

    # only type, no other results
    if ((is_typing_wanted)) ; then
        # speak result too
        #( el_speak_text -f "$answer" & )

        # type result
        type_with_xdotool "$answer" "8"
        return
    fi

    if ((is_stdout_wanted)) ; then
        echo -e "$answer"
    else
        if ((is_interactive)) || ! ((is_gui_wanted)) ; then
            echo -e "$answer"
        else
            verify_notification_system
            el_notify normal robot "$title" "$answer"
        fi
    fi
}

result_show_link(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer url
    url="$1"
    shift
    answer="$1"
    shift

    # only type, no other results
    if ((is_typing_wanted)) ; then
        # speak result too
        #( el_speak_text -f "$answer" & )

        # type result
        type_with_xdotool "$url" "8"
        return
    fi

    if ((is_stdout_wanted)) ; then
        echo -e "answer: $answer"
        echo -e "link: $url"
    else
        if ((is_interactive)) || ! ((is_gui_wanted)) ; then
            echo -e "$link"
        else
            echo -e "$link" | xclip -i -selection clipboard
            el_notify soft "robot" "Description" "${answer}"
        fi
    fi

    # open the link in your web browser
    web-launcher "$url"
}

result_speech(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer
    answer="$1"
    shift

    if ! el_dependencies_check "mpv" ; then
        el_dependencies_install "mpv"
    fi

    if ((is_stdout_wanted)) ; then
        echo -e "$answer"
    else
        mpv --really-quiet "${answer}" 1>/dev/null 2>&1
        rm -f "$answer"
    fi
}

result_file_get(){
    if ((is_intermediate_step)) ; then
        return
    fi

    local answer
    answer="$1"
    shift

    if ! el_dependencies_check "mpv" ; then
        el_dependencies_install "mpv"
    fi

    if ((is_stdout_wanted)) ; then
        echo -e "$answer"
    else
        if ((is_interactive)) || ! ((is_gui_wanted)) ; then
            echo -e "$answer"
        else
            el_info "file saved in: "$( dirname "$answer" )""
            thunar "$(dirname "$answer" )"
        fi
    fi
}

chatgpt_ask(){
    local message error_type extra url data_file
    message="$1"
    extra="$2"

    if [[ ! -d "$tmpdir" ]] ; then
        mkdir -p "$tmpdir"
        el_add_on_exit rm -rf "$tmpdir"
    fi
    data_file="${tmpdir}/curl_data.json"

    if [[ -z "$message" ]] ; then
        el_error "Empty message / no file provided"
        return
    fi
    if [[ -z "$apikey" ]] ; then
        apikey="$OPENAI_API_KEY"
    fi
    # save the message in a global variable:
    message_complete_g="$message"

    # escape quotation marks
    escaped_prompt="${message}"
    #escaped_prompt="${escaped_prompt//\"/\\\"}"
    #escaped_prompt="${escaped_prompt//\"/}"
    #escaped_prompt="${escaped_prompt//\'/\\\'}"
    #escaped_prompt="${escaped_prompt//\'/}"
    #escaped_prompt="$(echo "$message" | sed "s/'/\\\'/g")"
    # escape the \ char
    escaped_prompt="${escaped_prompt//\\/\\\\}"
    # escape the ` char
    #escaped_prompt="${escaped_prompt//`/\\\\`}"
    escaped_prompt="$(echo "$escaped_prompt" | sed -e 's|"|\\\"|g' -e 's|\*|%%%|g' | tr -s " " | awk '{printf "%s\\n", $0}' )"

    #printf "%s" "$escaped_prompt"

    if ((is_interactive)) ; then
        el_debug "Model: $model\nFull Request:\n$message_complete_g"
    fi

    [[ "$EL_DEBUG" -ge 4 ]] && set -x

    # Automatically select provider if not manually specified
    if [[ -z "$provider_wanted" ]] ; then
        auto_select_provider_model "$mode"
    fi

    # Determine provider and model based on task if not manually set
    if [[ -z "$model_manually_set" ]] ; then
        case "$mode" in
            "l"|"listen"|"audio")
                model_switch "whisper-1" "$provider_wanted"
                ;;
            "paint")
                model_switch "dall-e-3" "$provider_wanted"
                ;;
            "speech"|"speak"|"sr"|"speechrecord")
                model_switch "tts-1" "$provider_wanted"
                ;;
            "deepl")
                model_switch "deepl" "deepl"
                ;;
            *)
                # Default text logic
                local target_provider="${provider_wanted:-deepseek}"
                local quality_key
                if [[ "$is_smartest_wanted" -eq 1 ]]; then
                    quality_key="smartest"
                elif [[ "$is_smarter_wanted" -eq 1 ]]; then
                    quality_key="smarter"
                else
                    quality_key="smart"
                fi
                local target_model
                target_model=$(jq -r --arg pid "$target_provider" --arg qk "$quality_key" '.providers[] | select(.id == $pid) | (.model_variants[$qk] // .default_models["chat-"+$qk] // .default_models.chat // null)' "$MODELS_FILE" 2>/dev/null)
                if [[ -z "$target_model" || "$target_model" == "null" ]]; then
                    # fallback to current model or default
                    target_model="$model"
                fi
                model_switch "${target_model:-$model}" "$target_provider"
                ;;
        esac
    else
        # If model was manually set, just ensure environment is updated for that model
        model_switch "$model" "$provider_wanted"
    fi

    # Thinking mode logic
    local thinking_json=""
    if ((is_thinking_wanted)) ; then
        # Default effort to high if not set
        reasoning_effort_wanted="${reasoning_effort_wanted:-high}"
        thinking_json=', "reasoning_effort": "'$reasoning_effort_wanted'", "extra_body": {"thinking": {"type": "enabled"}}'
        # Thinking mode does not support temperature
        temperature_json=""
    else
        temperature_json=', "temperature": '$temperature''
    fi

    # request to OpenAI API
    case "$model" in
        "davinci-"*|"text-davinci-"*|"code-davinci-"*)
            [[ -z "$model_url" ]] && model_url="https://api.openai.com/v1/completions"

            {
                echo -n '{"model": "'"$model"'", "prompt": "'
                echo -n "${escaped_prompt}"
                echo -n '", "max_completion_tokens": '${max_completion_tokens}', "user": "'"$machine_id"'"'${temperature_json}${thinking_json}'}'
            } > "$data_file"
            response="$( curl -LsS -m 120  "$model_url" \
                -H 'Content-Type: application/json' \
                -H "Authorization: Bearer $apikey" \
                -d "@$data_file" | jq --indent 2 --monochrome-output )"

            answer_g="$( echo "$response" | jq -r '.choices[].text' 2>/dev/null  )"
            finish_reason="$( echo "$response" | jq -r '.choices[].finish_reason' 2>/dev/null )"
            ;;

        "deepseek"*|"groq"*)
            [[ -z "$model_url" ]] && model_url="https://api.deepseek.com/chat/completions"
            if [[ "$model" = "groq/"* ]] ; then
                model="${model#groq/}"
            fi

            {
                echo -n '{"messages": [{"content": "'
                echo -n "${escaped_prompt}"
                echo -n '", "role": "user"}], "model": "'"$model"'", "stream": false'${temperature_json}${thinking_json}'}'
            } > "$data_file"
            response="$( curl -LsS -m 120  "$model_url" \
                -H 'Content-Type: application/json' \
                -H "Authorization: Bearer $apikey" \
                -d "@$data_file" | jq --indent 2 --monochrome-output )"
                      # "temperature": '$temperature'

            answer_g="$( echo "$response" | jq -r '.choices[].message.content' 2>/dev/null  )"
            # If thinking mode is used, we might want to capture reasoning_content too,
            # but for now we just ensure the main content is retrieved.
            reasoning_g="$( echo "$response" | jq -r '.choices[].message.reasoning_content' 2>/dev/null )"
            if [[ "$reasoning_g" != "null" ]] && [[ -n "$reasoning_g" ]] ; then
                el_debug "Reasoning: $reasoning_g"
            fi
            finish_reason="$( echo "$response" | jq -r '.choices[].finish_reason' 2>/dev/null )"
            ;;

        "gpt-"*)
            [[ -z "$model_url" ]] && model_url="https://api.openai.com/v1/chat/completions"

            {
                echo -n '{"model": "'"$model"'", "messages": [{"role": "user", "content": "'
                echo -n "${escaped_prompt}"
                echo -n '"}], "max_completion_tokens": '${max_completion_tokens}', "user": "'"$machine_id"'"'${temperature_json}${thinking_json}'}'
            } > "$data_file"
            response="$( curl -LsS -m 120  "$model_url" \
                -H 'Content-Type: application/json' \
                -H "Authorization: Bearer $apikey" \
                -d "@$data_file" | jq --indent 2 --monochrome-output )"

            answer_g="$( echo "$response" | jq -r '.choices[].message.content' 2>/dev/null  )"
            finish_reason="$( echo "$response" | jq -r '.choices[].finish_reason' 2>/dev/null )"
            ;;

        "whisper"|"whisper-"*)
            if [[ "$provider_wanted" = "fireworks" ]] ; then
                response="$( curl -LsS -m 120 --url "$model_url" -Ls --request POST -H "Authorization: Bearer $apikey" \
                    -F "file=@${message}" \
                    -F "model=${model}" \
                    -F "temperature=0" \
                    -F "vad_model=silero" )"
            elif [[ "$provider_wanted" = "local" ]] ; then
                # local whisper transcription
                if ! command -v whisper &>/dev/null ; then
                    el_error "whisper command not found; please install openai-whisper"
                    answer_g=""
                    message="local whisper not installed"
                else
                    answer_g="$( whisper "$message" --model tiny --language "$extra" 2>&1 )"
                fi
                # skip further processing
                break
            elif [[ -n "$extra" ]] ; then
                response="$( curl -LsS -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: multipart/form-data' -H "Authorization: Bearer $apikey" \
                    -F "file=@${message}" \
                    -F "model=${model}" \
                    -F "language=${extra}" )"

                if [[ "${PIPESTATUS[0]}" != 0 ]] ; then
                    sleep 1
                    response="$( curl -LsS -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: multipart/form-data' -H "Authorization: Bearer $apikey" \
                        -F "file=@${message}" \
                        -F "model=${model}" \
                        -F "language=${extra}" )"
                fi
            else
                # autodetect language spoken
                response="$( curl -LsS -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: multipart/form-data' -H "Authorization: Bearer $apikey" \
                    -F "file=@${message}" \
                    -F "model=${model}" )"

                if [[ "${PIPESTATUS[0]}" != 0 ]] ; then
                    sleep 1
                    response="$( curl -LsS -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: multipart/form-data' -H "Authorization: Bearer $apikey" \
                        -F "file=@${message}" \
                        -F "model=${model}" )"
                fi
            fi

            #echo "$response"
            #exit
                #-F "response_format=text" \
            # we don't need to continue to fix the result or anything, so let's just return from here
            #return

            #answer_g="$response"
            answer_g="$( echo "$response" | jq -r '.text' 2>/dev/null  )"
            finish_reason="$( echo "$response" | jq -r '.choices[].finish_reason' 2>/dev/null )"

            if [[ -z "$answer_g" ]] || [[ "$answer_g" = "null" ]] ; then
                message="$( echo "$response" | jq -r '.error.message' 2>/dev/null )"
            fi

            ;;

        "deepl")

            # with forced language to listen from:
            model_url="$conf_deepl_endpoint"
            apikey="$DEEPL_TOKEN"

            if [[ -n "$LANG_SOURCE" ]] ; then
                { echo -n '{"text":["'; echo -n "${escaped_prompt}"; echo -n '"],"formality":"'"${TEXT_TONE}"'","target_lang":"'"${extra}"'","source_lang":"'"${LANG_SOURCE//_*}"'"}' ; } > "$data_file"
            else
                { echo -n '{"text":["'; echo -n "${escaped_prompt}"; echo -n '"],"formality":"'"${TEXT_TONE}"'","target_lang":"'"${extra}"'"}' ; } > "$data_file"
            fi
            response="$( curl -sSL -m 120 -X POST "$model_url" -H 'Content-Type: application/json' -H "Authorization: DeepL-Auth-Key $apikey" -d "@$data_file" | jq --indent 2 --monochrome-output )"

            if [[ "${PIPESTATUS[0]}" != 0 ]] ; then
                sleep 2
                if [[ -n "$LANG_SOURCE" ]] ; then
                    { echo -n '{"text":["'; echo -n "${escaped_prompt}"; echo -n '"],"formality":"'"${TEXT_TONE}"'","target_lang":"'"${extra}"'","source_lang":"'"${LANG_SOURCE//_*}"'"}' ; } > "$data_file"
                else
                    { echo -n '{"text":["'; echo -n "${escaped_prompt}"; echo -n '"],"formality":"'"${TEXT_TONE}"'","target_lang":"'"${extra}"'"}' ; } > "$data_file"
                fi
                response="$( curl -sSL -m 120 -X POST "$model_url" -H 'Content-Type: application/json' -H "Authorization: DeepL-Auth-Key $apikey" -d "@$data_file" | jq --indent 2 --monochrome-output )"
            fi

            answer_g="$( echo "$response" | jq -r '.translations[].text' 2>/dev/null  )"

            detected_source_language="$( echo "$response" | jq -r '.translations[].detected_source_language' 2>/dev/null  )"
            if [[ -n "$detected_source_language" ]] ; then
                el_debug "Source language: $detected_source_language"
            fi

            if [[ -z "$answer_g" ]] ; then
                message="$( echo "$response" | jq -r '.message' 2>/dev/null )"
            fi

            # fixes
            if [[ "$answer_g" = "$message" ]] ; then
                unset answer_g
                message="Nothing translated? Same result obtained"
            fi


            ;;

        "dall-e-"*)
            [[ -z "$model_url" ]] && model_url="https://api.openai.com/v1/images/generations"

            {
                echo -n '{"model":"'"$model"'","prompt":"'
                echo -n "${escaped_prompt}"
                echo -n '","n":1,"size":"1792x1024","user":"'"$machine_id"'"}'
            } > "$data_file"
            response="$( curl -sSL -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: application/json' -H "Authorization: Bearer $apikey" \
            -d "@$data_file" | jq --indent 2 --monochrome-output )"


            if [[ "${PIPESTATUS[0]}" != 0 ]] ; then
                sleep 1
                {
                    echo -n '{"model":"'"$model"'","prompt":"'
                    echo -n "${escaped_prompt}"
                    echo -n '","n":1,"size":"1792x1024","user":"'"$machine_id"'","temperature":'$temperature'}'
                } > "$data_file"
                response="$( curl -sSL -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: application/json' -H "Authorization: Bearer $apikey" \
                -d "@$data_file" | jq --indent 2 --monochrome-output )"
            fi

            answer_g="$( echo "$response" | jq -r '.data[].revised_prompt' 2>/dev/null  )"
            url="$( echo "$response" | jq -r '.data[].url' 2>/dev/null  )"
            #finish_reason="$( echo "$response" | jq -r '.data[].finish_reason' 2>/dev/null )"
            ;;

        *"tts"*|"canopylabs/"*)
            # For TTS: openai (tts-1), groq (canopylabs/orpheus-*)
            # Ensure model_url is set based on provider
            if [[ "$provider_wanted" == "groq" ]]; then
                model_url="https://api.groq.com/openai/v1/audio/speech"
                apikey="${GROQ_API_KEY}"
                voice="${voice:-alloy}"
            else
                [[ -z "$model_url" ]] && model_url="https://api.openai.com/v1/audio/speech"
                if [[ "$model_url" = *"openai.com"* ]] ; then
                    voice="${voice:="shimmer"}"
                fi
            fi

            file="${files_generated}/speech/voice_${voice}_$(date +%F__%H:%M:%S).mp3"
            [[ ! -d "$( dirname "$file" )" ]] && mkdir -p "$(dirname "$file" )"

            {
                echo -n '{"model":"'"$model"'","input":"'
                echo -n "${escaped_prompt}"
                echo -n '","voice":"'"${voice}"'","user":"'"$machine_id"'"}'
            } > "$data_file"
            response="$( curl -sSL -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: application/json' -H "Authorization: Bearer $apikey" \
            -d "@$data_file" --output "$file" )"

            if [[ "${PIPESTATUS[0]}" != 0 ]] ; then
                sleep 1
                {
                    echo -n '{"model":"'"$model"'","input":"'
                    echo -n "${escaped_prompt}"
                    echo -n '","voice":"'"${voice}"'","user":"'"$machine_id"'"}'
                } > "$data_file"
                response="$( curl -sSL -m 120  --url "$model_url" -Ls --request POST -H 'Content-Type: application/json' -H "Authorization: Bearer $apikey" \
                -d "@$data_file" --output "$file" )"
            fi

            if [[ "$( file -b --mime-type "$file" )" = "application/json" ]] ; then
                response="$( cat "$file" )"
            else
                answer_g="${file}"
            fi
            ;;

        "eleven"*)
            # ElevenLabs TTS
            file="${files_generated}/speech/voice_${voice:-elevenlabs}_$(date +%F__%H:%M:%S).mp3"
            [[ ! -d "$( dirname "$file" )" ]] && mkdir -p "$(dirname "$file" )"
            # get voice_id from models file or fallback to env or default
            voice_id="${ELEVENLABS_VOICE_ID:-$(jq -r --arg pid "$provider_wanted" '.providers[] | select(.id == $pid) | .voice_id // "cgSgspJ2msm6clMCkdW9"' "$MODELS_FILE" 2>/dev/null)}"
            tts_url="${model_url}/${voice_id}?output_format=opus_48000_96"
            body_content='{"text":"'${escaped_prompt}'","model_id":"'${model}'"}'
            response=$(curl -sSL -m 120 -X POST "$tts_url" \
                -H "xi-api-key: $apikey" \
                -H "Content-Type: application/json" \
                -H "Accept: audio/mpeg" \
                -d "$body_content" --output "$file" 2>&1)
            if [[ "$( file -b --mime-type "$file" )" = "application/json" ]] ; then
                response="$( cat "$file" )"
                el_error "elevenlabs error: $(echo "$response" | jq -r '.detail.status'? 2>/dev/null) - $(echo "$response" | jq -r '.detail.message'? 2>/dev/null)"
                rm -f "$file"
                answer_g=""
                message="elevenlabs error"
            else
                answer_g="${file}"
            fi
            ;;
        *)
            el_error "Invalid model"
            ;;

    esac

    [[ "$EL_DEBUG" -ge 4 ]] && set +x

    if [[ "$answer_g" = "null" ]] ; then
        unset answer_g
        if [[ -z "$message" ]] ; then
            message="Empty response"
        fi
    fi

    answer_g="$( echo "$answer_g" | sed -e 's|%%%|*|g' )"

    # try again
    if [[ -z "$answer_g" ]] ; then
        error_type="$( echo "$response" | jq -r '.error.type' 2>/dev/null )"

        # delete useless file
        if [[ "$( file -b --mime-type "$file" )" = "application/json" ]] ; then
            rm -f "$file"
        fi

        if [[ "$error_type" == "insufficient_quota" ]] ; then
            if $guitool --question --text="$( eval_gettext "Quota exceeded. Check billing details with the service provider or create a new account to continue." )" ; then
                web-launcher "https://platform.openai.com/"
                if $guitool --question --text="$( eval_gettext "Reset API settings to associate Elive with a new configuration?" )" ; then
                    el_config_restart
                    $guitool --info --text="$( eval_gettext "Configuration restarted. Re-run the tool to configure with your new account." )"
                    exit
                fi
            else
                exit 1
            fi
        fi

        if [[ -n "$error_type" ]] ; then
            message="$( echo "$response" | jq -r '.error.message' 2>/dev/null )"
        fi

        if [[ -n "$message" ]] ; then
            # ChatGPT is blocked in places like venezuela, handle this:
            if [[ "$message" = "Country, region, or territory not supported" ]] && [[ "$model" = "whisper"* ]] && [[ -n "$GROQ_API_KEY" ]] ; then
                model="whisper"
                model_wanted="groq"

                $FUNCNAME "$@"
                return $?
            else
                el_error_wrapper "$message"
                exit 1
            fi
        fi

        if [[ "$error_type" == "requests" ]] || [[ "$error_type" == "server_error" ]] || [[ "$error_type" = "null" ]] || [[ -z "$answer_g" ]] ; then
            if [[ "$attempts" -ge $attempts_max ]] ; then
                el_error_wrapper "$( eval_gettext "Too many requests. Wait before trying again." )"
                exit 1
            else
                attempts="$(( $attempts + 1 ))"
                if [[ "$error_type" == "requests" ]] ; then
                    el_debug "too many attempts, trying again ($attempts) ..."
                    sleep 5
                    sleep $attempts
                else
                    echo -e "$response" | jq -c
                    el_error "$( echo "$response" | jq )"
                fi

                $FUNCNAME "$@"
                return $?
            fi
        else
            # unknown error, print
            # TODO: include the json to print
            el_error_wrapper "problem fetching results: $error_type"
            echo "$response" | jq --indent 2 --color-output 1>&2
            exit 1
        fi
    fi

    # unfinished?
    if [[ -n "$finish_reason" ]] && [[ "$finish_reason" != "stop" ]] && [[ "$finish_reason" != "null" ]] ; then
        if [[ -n "$finish_reason" ]] && [[ "$finish_reason" = "length" ]] ; then
            answer_g="$( printf "%s\n...[more]...\n" "$answer_g" )"
        else
            el_warning "finish_reason is not stop: $finish_reason\n$( echo "$response" | jq --indent 2 --color-output )"
        fi
    fi

    # debug
    if [[ "$EL_DEBUG" -ge 4 ]] ; then
        echo -e "Response:" 1>&2
        echo "$response" | jq --indent 2 --color-output 1>&2
    fi


    # fixes
    # delete the first line if contains no useful information
    if echo "$answer_g" | head -1 | grep -qsE "^A?( |,|\.|:|)\s*$" ; then
        answer_g="$( printf "%s\n" "$answer_g" | sed -e '1d' )"
    fi
    if echo "$answer_g" | head -1 | grep -qsE "^A?( |,|\.|:|)\s*$" ; then
        answer_g="$( printf "%s\n" "$answer_g" | sed -e '1d' )"
    fi
    # remove markdown code-block marks:
    if echo "$answer_g" | head -1 | grep -qsiE '^```\w*$' ; then
        answer_g="$( printf "%s\n" "$answer_g" | sed -e '1d' )"
    fi
    if echo "$answer_g" | tail -1 | grep -qsiE '^```$' ; then
        answer_g="$( printf "%s\n" "$answer_g" | sed -e '$d' )"
    fi

    # remove starting commas (or similar) in single-line sentences
    #if [[ "$( echo "$answer_g" | wc -l )" = 1 ]] ; then
        #answer_g="$( printf "%s\n" "$answer_g" | sed -e 's|^,||g' -e 's|^\.||g' -e 's|^ ||g' )"
    #fi
    # cleanups
    answer_g="$( printf "%s\n" "$answer_g" | colors-remove | iconv -f utf-8 -t utf-8 | sed -e 's|—|, |g' -e 's| , |, |g' -e 's|  | |g' -e '/./,$!d' -e '1s/^[[:space:]]*//' )"


    el_debug "Q: $message_original"
    el_debug "A: $answer_g"
}





mode_listen(){
    if ((is_gui_wanted)) ; then
        verify_notification_system
    fi

    #if ! el_dependencies_check "parec|oggenc" ; then
        #el_dependencies_install "pulseaudio-utils|vorbis-tools"
    #fi
    if ! el_dependencies_check "arecord|oggenc" ; then
        el_dependencies_install "alsa-utils|vorbis-tools"
    fi

    local message_notification_end
    message_notification_end="$( printf "$( eval_gettext "Listening stopped" )" "" )"

    mkdir -p "$tmpdir"
    el_add_on_exit rm -rf "$tmpdir"

    if [[ -s "$(dirname $tmpdir )/notification_id" ]] ; then
        notification_id="$( cat "$(dirname $tmpdir)/notification_id" )"
    fi

    # if already recording, stop the recording and quit so the normal process can continue
    #if pidof -q parec ; then
    if pidof -q arecord ; then
        if pidof -x -q "$(basename $SOURCE)" ; then
            # make sure first the user stopped to speak:
            LC_ALL=C sleep 0.05
            # stop recordings
            #killall parec
            #killall -s TERM arecord
            #kill -s TERM "$(pidof arecord)"
            kill -s TERM $( ps ux | grep -Ev "(timeout|grep) " | grep "arecord --quiet --format=cd" | awk '{print $2}' | tr '\n' ' ' )
            if [[ -n "$notification_id" ]] ; then
                if ! ((is_interactive)) || ((is_gui_wanted)) ; then
                    verify_notification_system
                    notify-send -i audio-input-microphone Listening "${message_notification_end}" -t 500 -r $notification_id -e
                fi
            fi

            # continue with the default call
            exit
        fi
    fi


    if ! ((is_interactive)) || ((is_gui_wanted)) ; then
        local message_notification_description
        message_notification_description="$( printf "$( eval_gettext "Listening to your voice. Run again to stop..." )" "" )"
        local message_notification_button
        message_notification_button="$( printf "$( eval_gettext "Stop listening" )" "" )"

        (
            verify_notification_system
            notification_id="$( notify-send -i audio-input-microphone Listening "Starting service..." -t 1 -p )"
            echo "$notification_id" > "$(dirname $tmpdir)/notification_id"

            result="$( notify-send -i audio-input-microphone Listening "$message_notification_description" --action=stop="${message_notification_button}" -t 0 -w -e -r $notification_id )"
            # if the user clicks in stop, we give the result
            if [[ "$result" = "stop" ]] ; then
                kill -s TERM $( ps ux | grep -Ev "(timeout|grep) " | grep "arecord --quiet --format=cd" | awk '{print $2}' | tr '\n' ' ' ) 2>/dev/null
            fi
        ) &

    else
        echo -e "Record started, to stop it, run again the command:  $(basename $SOURCE) $arguments" 1>&2
        el_info "Recording your voice..."
    fi

    # using pulseaudio, but seems unreliable
    #parec -r --file-format=wav "test.wav"
    #oggenc --quiet -q -1 "test.wav" -o "audio.ogg"

    # using alsa, seems more reliable, and also piped
    timeout 30m  arecord --quiet --format=cd -c 1 -d 3600 - | oggenc --quiet -q -1 -o "$tmpdir/audio.ogg" -

    # close the notification if we stopped with with another process
    #curl -Ls --request POST --url https://api.openai.com/v1/audio/transcriptions --header "Authorization: Bearer $conf_chatgpt_apikey" --header 'Content-Type: multipart/form-data' --form file=@test.mp3 --form model=whisper-1 --form response_format=text

    notification_id="$( cat "$(dirname $tmpdir)/notification_id" )"
    if [[ -n "$notification_id" ]] ; then
        if ! ((is_interactive)) || ((is_gui_wanted)) ; then
            verify_notification_system
            notify-send -i audio-input-microphone Listening "${message_notification_end}" -t 500 -r $notification_id -e
        fi
    fi

    if [[ "$EL_DEBUG" -gt 3 ]] ; then
        mpv --really-quiet "${tmpdir}/audio.ogg" 1>/dev/null 2>&1
    fi

    chatgpt_ask "${tmpdir}/audio.ogg" "$1"

    # show result
    if ! ((is_intermediate_step)) && ((is_gui_wanted)) && ! ((is_stdout_wanted)) ; then
        if [[ -n "$1" ]] ; then
            lang="$1"
            lang="$( sed '/^! layout$/,/^ *$/!d;//d' "/usr/share/X11/xkb/rules/base.lst" | awk -v lang="$lang" '{if ($1 == lang) print $2}' | head -1 )"
            [[ -z "$lang" ]] && lang="${1^^}"

            ( el_notify soft "gtk-ok" "${lang}" "${answer_g}" -r $notification_id -e & )
        else
            ( el_notify soft "gtk-ok" "Result" "${answer_g}" -r $notification_id -e & )
        fi
    fi

}

main(){
    # pre {{{
    local message

    # dependencies
    if ! [[ -e "/usr/lib/elive-tools/functions" ]] ; then
        echo -e "Dependency required: Install the package 'elive-tools' first" 1>&2
        exit 1
    fi
    if ! [[ -e "/etc/elive-version" ]] ; then
        if ! el_dependencies_check "jq|wdiff|curl|yad|cowsay" ; then
            el_dependencies_install "jq|wdiff|curl|yad|cowsay"
        fi
        if ! el_dependencies_check "batcat" ; then
            el_dependencies_install "bat"
        fi
    fi

    if ! el_verify_internet fast  2>/dev/null ; then
        local message_no_internet
        message_no_internet="$( printf "$( eval_gettext "Internet connection required to use this tool." )" "" )"

        el_error_wrapper "$message_no_internet"
    fi

    # machine ID
    if grep -qs "boot=live" /proc/cmdline ; then
        machine_id="$( sudo -H bash -c "source /usr/lib/elive-tools/functions ; el_get_machine_id" )"
    else
        if grep -qsF "machine-id: " "/etc/elive-version" 2>/dev/null ; then
            machine_id="$( cat /etc/elive-version | grep "^machine-id: " | sed -e 's|^machine-id: ||g' | tail -1 )"
        else
            if ! el_flag check warning_no_elive_os ; then
                notify-send -e -t 14000 -i bomb "Warning" "This is not an Elive Linux system,\nfeatures may be limited, for example some visual elements."
                el_flag add warning_no_elive_os
            fi
            machine_id="dummy"
        fi
        machine_id="${machine_id}-${USER}"
    fi

    # }}}
    # Usage {{{
    if { [[ -z "$1" ]] && ! ((is_stdin)) ; } || [[ "$1" = "-h" ]] || [[ "$1" = "--help" ]] ; then
        if ((is_interactive)) ; then
            echo -e "Usage: $(basename $SOURCE) mode1[:provider|model][,mode2[:provider|model]...] message"
            echo -e "  GUI: use with --gui if you run it from a terminal but want some GUI features"
            echo -e "  Mode Chaining: Chain multiple requests with commas, specifying model/provider per step with ':'"
            echo -e "                 e.g. $(basename $SOURCE) listen:groq,voice-corrector:deepseek --type"
            echo -e "  You can also use this tool as a pipe, e.g. echo 'hell0 wrold' | $(basename $0) corrector"
            echo -e "  --stdout      Always show results in stdout, useful for getting results from the tool"
            echo -e "  --lang        Force the language to use, useful for translations e.g. 'es' or 'es_ES.UTF-8'"
            echo -e "  --provider FOO Set the provider to use (openai, deepseek, z.ai, kimi, groq, anthropic, google, fireworks, openrouter, mistral, together, perplexity)"
            echo -e "  --model    FOO Set the model to use. Supported models: gpt-5.4-nano, gpt-5.4-mini, gpt-5.4, claude-3-5-sonnet-20240620, gemini-1.5-pro, gemini-1.5-flash, llama-3.1-70b-versatile, deepseek-chat, deepseek-v4-flash, deepseek-v4-pro, whisper-1"
            echo -e "  --max-tokens N Set maximum tokens for completion (default: 4096)"
            echo -e "  --thinking     Enable thinking mode (Chain-of-Thought) for supported models (e.g. DeepSeek)"
            echo -e "  --reasoning-effort EFFORT Set reasoning effort (low, medium, high, max). Default is high."

            echo -e "\nText modifiers:"
            echo -e "  translate:    Translates from any language (even if mixed) to your local language used in your Elive system"
            echo -e "  translate-improved:    Translates from any language (even if mixed) to your local language, with literary improvements"
            echo -e "  translate-en: Translates any text to English, with well-formed sentences and good, elegant English"
            echo -e "  translate-code: Translates a coding-style sentence to the --lang linux code, useful for code translations"
            echo -e "  corrector:    Corrects the grammar any given text (in any language)"
            echo -e "  voice-corrector: Corrects unrecognized technical terms (e.g. SSH, root, Elive, Debian) from spoken text"
            echo -e "  proofread:    Corrects the grammar and slightly improves any given text"
            echo -e "  improver:     Rephrases a text in a deeper, more elegant and literary way, showing different options"
            echo -e "  revise:       Act as a book editor, fixing the grammar, improving clarity, consistency of style, etc.."
            echo -e "  synonyms:     Writes synonyms for a given word, or defines a concept. e.g. 'computer portable' -> laptop"
            echo -e "  rephrase:     Rewrites a text in a different way"
            echo -e "  persuade:     Rewrites a text in a more convincing way"
            echo -e "  organize:     Rewrites your text in a list of points in a logical order"
            echo -e "  summarize:    Summarizes a text, removing useless and unneeded parts"
            echo -e "  simplify:     Simplifies a text as much as possible, making it shorter and concise (lacking emotion)"
            echo -e "  restructure:  Restructure and reorganize a text in a correct order simplifying it without removing any needed information"
            echo -e "  humanize:     Make the sentence to sound more like human"
            echo -e "  nosell:       Remove the selling sounding-like references from a sentence"

            echo -e "\nTools:"
            echo -e "  explain:      Explains a concept, jargon, abbreviation, or anything!"
            echo -e "  ocrfix:       Fixes text like the ones wrongly detected from a scanned document"
            echo -e "  code:         generates a source code from a well defined description, ex: in ruby, show the local weather"

            echo -e "\nCreative:"
            echo -e "  title:        Suggests a good title for a specific purpose"
            echo -e "  titlepost:    Suggests a good engaging (website) post title"
            echo -e "  intro:        Writes an introductory text for an article or similar"
            echo -e "  story:        Narrates a story based on a description"
            echo -e "  idea:         Gives ideas of how to continue a text"
            echo -e "  propose:      Proposes a text with more contents and ideas, like continuing it"
            echo -e "  expand:       Expands a text with more and richer contents"
            echo -e "  product:      Describes a product"
            echo -e "  seo:          Creates a Google-like snippet description to fill in your SEO plugin"
            echo -e "  recipe:       Creates a recipe based on your given ingredients"
            echo -e "  ux:           Designs an UX concept based on the described needs"
            echo -e "  copywriter:   Writes about any topic from your description or details"
            echo -e "  techwriter:   Writes articles related to technology"
            echo -e "  domain:       Gives (web) domain name ideas"

            echo -e "\nTools and Engines:"
            echo -e "  listen:       Special mode to listen your voice input, returning transcribed text as output, second argument can be the language forced to listen from"
            echo -e "  audio:        Transcript in text a given audio file"
            echo -e "  speech:       Speak a text"
            echo -e "  paint:        Paint / design an image based on your description"
            echo -e "  deepl:        Translates from any language (even if mixed) to your local language using the deepl service"

            echo -e "\nSelf-Help:"
            echo -e "  therapist:    Receive help from different topics related to mental health"
            echo -e "  doctor:       Describe a pain or problem in detail to get health directions"
            echo -e "  inspireme:    Get a personalized sentence to inspire you!"
            echo -e "  suggest-book: Describe what type of book you want to read and get a good suggestion list"
            echo -e "  elivehow:     Ask how to do something in Elive (experimental)"

            echo -e "\nFunny:"
            echo -e "  friend:       Speak as if you were talking to a friend"
            echo -e "  girlfriend:   Your personal girlfriend (lol)"
            echo -e "  yoda:         Speak with Yoda from Star Wars"

            echo -e "\nEnvironmental variables:"
            echo -e "   LANG:        this variable defines your local language, or to which language actions should be spoken, like en_US.UTF-8"
            echo -e "   LANG_SOURCE: forces the language input to be recognized as X"
            echo -e "   TEXT_TONE:   this variable defines the tone to write into, like formal or informal (deepl only)"
            #echo -e "\nWIP:"
            #echo -e "  palette-mood: Generate a colorscheme based on your location's actual weather"
            #echo -e "  human-cyber-guidance / project-manager:  based on a specific goal, ask every day (or hour?) about the tasks reached / work done, in order to answer a guidance about how to go faster or suggest the next tasks to do (use a history.txt file where to put the previous answers in order to include all of them in a single shot, max 50 lines)"
            #echo -e "  "
            #echo -e "  "
            #echo -e "  "
            echo -e "\nNotes:"
            echo -e "  - You can write in any language and it will reply in the corresponding one. There is a single answer, but in the GUI mode you can follow a conversation."
            echo -e "  - You can concatenate results, for example:  echo 'h0l4 mznd0, cómo est4s?' | $( basename $SOURCE ) ocrfix | $( basename $SOURCE ) translate-en"
            echo -e "  - You can chain multiple modes directly using commas and specify models per step with ':', for example:  $( basename $SOURCE ) listen:groq,voice-corrector:deepseek --type"
            echo -e "  - If you run the 'listen' mode you can run it again to close the listening, but if you want to use it on a combo script you will need to exit the combo in case was already running in order to not run the rest of the script twice, for example:  bash -c 'killall -qs TERM arecord && exit  ; elive-assistant --gui --stdout listen | elive-assistant --type revise'"
        else
            $guitool --error --title="$message_title" --text="$( eval_gettext "No requests given. Use the tool as:" )\n $SOURCE mode message"
        fi
        exit 1
    fi

    arguments="$@"

    for arg in "$@"
    do
        case "$arg" in
            "--type")
                is_typing_wanted=1
                shift
                ;;
            "--stdout")
                is_stdout_wanted=1
                shift
                ;;
            "--gui")
                is_gui_wanted=1
                shift
                ;;
            "--lang")
                export LANG="$2"
                shift 2
                ;;
            "--provider")
                provider_wanted="$2"
                shift 2
                ;;
            "--model")
                local raw_model="$2"
                if [[ "$raw_model" == *"/"* ]]; then
                    provider_wanted="${raw_model%%/*}"
                    model="${raw_model#*/}"
                    model_manually_set=1
                elif [[ -n "$MODELS_FILE" ]] && jq -e --arg raw "$raw_model" '.providers[] | select(.id == $raw)' "$MODELS_FILE" >/dev/null 2>&1 ; then
                    provider_wanted="$raw_model"
                    model=""
                    model_manually_set=0
                else
                    model="$raw_model"
                    model_manually_set=1
                fi
                shift 2
                ;;
            "--max-tokens")
                max_completion_tokens="$2"
                shift 2
                ;;
            "--thinking"|"--think")
                is_thinking_wanted=1
                shift
                ;;
            "--reasoning-effort")
                reasoning_effort_wanted="$2"
                shift 2
                ;;
        esac
    done


    # install extra gui dependencies if needed, only if we want to use gui
    if ((is_interactive)) && ! ((is_gui_wanted)) ; then
        true
    else
        if ! el_dependencies_check "yad" ; then
            el_dependencies_install "yad"
        fi
    fi

    # get the request definitions
    source /usr/share/elive-assistant/requests/requests


    # attempts to get an answer if something failed
    attempts=0
    attempts_max=4
    # randomness value, default is 0.7, smaller amount is more correct answers and bigger value is more creative (but can lead to errors, value 2 is almost gargbage, like the AI being in drugs), this value can be reconfigured later
    : ${temperature:="0.7${RANDOM:0:1}"}
    : ${max_completion_tokens:=4096}




    mode="$1"
    shift

    if [[ -n "$stdin_message" ]] ; then
        message="$stdin_message"
    else
        #message="$@"
        message="$( echo -e "$@" | dos2unix )"
    fi

    # replace newlines with its escaped code, otherwise it will not work
    if ! [[ -s "$message" ]] ; then
        message_original="$message"
        message="${message//$'\n'/\\n}"
    fi


    # }}}

    variables_fixes

ensure_model_for_task(){
    local task_mode="$1"
    local task_type
    case "$task_mode" in
        listen|audio) task_type="transcription" ;;
        speech|speak|sr) task_type="tts" ;;
        paint) task_type="image" ;;
        *) return 0 ;;
    esac
    if [[ -z "$model_manually_set" ]] || [[ "$model_manually_set" -eq 0 ]] || [[ -z "$model" ]]; then
        auto_select_provider_model "$task_mode"
        model_manually_set=1
    fi
    if [[ -z "$model" ]]; then
        if [[ -z "$provider_wanted" ]]; then
            el_error "No model and no provider specified for task $task_mode"
            exit 1
        fi
        model=$(jq -r --arg pid "$provider_wanted" --arg mtype "$task_type" '.providers[] | select(.id == $pid) | (.default_models[$mtype] // .default_models.chat)' "$MODELS_FILE" 2>/dev/null)
        if [[ -z "$model" || "$model" == "null" ]]; then
            el_error "No default model found for provider $provider_wanted task $task_type"
            exit 1
        fi
    fi
    model_switch "$model" "$provider_wanted"
}

    initial_provider_wanted="${provider_wanted:-}"
    initial_model="${model:-}"
    initial_model_manually_set="${model_manually_set:-0}"
    default_temperature="$temperature"
    default_max_completion_tokens="$max_completion_tokens"

    IFS=',' read -ra mode_chain <<< "$mode"
    total_steps="${#mode_chain[@]}"
    current_step=0

    for raw_step_mode in "${mode_chain[@]}" ; do
        current_step=$(( current_step + 1 ))
        if (( current_step < total_steps )) ; then
            is_intermediate_step=1
        else
            is_intermediate_step=0
        fi

        temperature="$default_temperature"
        max_completion_tokens="$default_max_completion_tokens"

        if [[ "$raw_step_mode" == *":"* ]]; then
            step_mode="${raw_step_mode%%:*}"
            step_spec="${raw_step_mode#*:}"

            if [[ "$step_spec" == *"/"* ]]; then
                provider_wanted="${step_spec%%/*}"
                model="${step_spec#*/}"
                model_manually_set=1
            elif [[ -n "$MODELS_FILE" ]] && jq -e --arg raw "$step_spec" '.providers[] | select(.id == $raw)' "$MODELS_FILE" >/dev/null 2>&1 ; then
                provider_wanted="$step_spec"
                model=""
                model_manually_set=0
            else
                provider_wanted="$initial_provider_wanted"
                model="$step_spec"
                model_manually_set=1
            fi
        else
            step_mode="$raw_step_mode"
            provider_wanted="$initial_provider_wanted"
            model="$initial_model"
            model_manually_set="$initial_model_manually_set"
        fi

        mode="$step_mode"
        message_original="$message"

        case "$mode" in
            "l"|"listen")
                # optionally, the extra argument can be the language to use
                ensure_model_for_task "listen"
                mode_listen "$message"
                result_show_plain "$answer_g"
                ;;
            "audio")
                ensure_model_for_task "audio"
                chatgpt_ask "${message}"
                result_show_plain "$answer_g"
                ;;
            "deepl")
                chatgpt_ask "${message}" "${LANG//_*}"
                result_show_plain "$answer_g"
                ;;
            "paint")
                ensure_model_for_task "paint"
                chatgpt_ask "${message}"
                result_show_link "${url}" "${answer_g}"
                ;;
            "speech"|"speak")
                ensure_model_for_task "speech"
                chatgpt_ask "${message}"
                result_speech "${answer_g}"
                ;;
            "sr"|"speechrecord")
                ensure_model_for_task "sr"
                chatgpt_ask "${message}"
                result_file_get "${answer_g}"
                ;;
            "t"|"translate")
                chatgpt_ask "${request_translate_to_local}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "ti"|"translate-improved")
                chatgpt_ask "${request_translate_improved_to_local}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "tc"|"translate-code")
                chatgpt_ask "${request_translate_code}\n\n${message}"
                result_show_plain "$answer_g"
                ;;
            "te"|"translate-en")
                #chatgpt_ask "${request_translate_to_english_well}\n\n${message}"
                chatgpt_ask "${request_translate_to_english}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "c"|"corrector"|"correct"|"correction")
                chatgpt_ask "${request_corrector}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "vc"|"voice-corrector"|"voicecorrector")
                temperature="0.5"
                chatgpt_ask "${request_voice_corrector}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "p"|"proofreader"|"proofreading"|"proofread")
                temperature="0.6"
                chatgpt_ask "${request_proofreader}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "synonyms"|"synonym")
                chatgpt_ask "${request_synonyms}\n\n${message}"
                answer_g="$( echo "$answer_g" | sort -u | sed -e '/^$/d' )"
                result_show_notification "$( eval_gettext "Synonyms or Definitions" )" "$answer_g"
                ;;
            "rephrase")
                temperature="0.6"
                chatgpt_ask "${request_rephrase}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "persuade"|"persuasive")
                chatgpt_ask "${request_persuade}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "i"|"improver"|"improve")
                chatgpt_ask "${request_improver}\n\n${message}"
                # do not use when having more than 1 alrnative result:
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "r"|"revise"|"revisor")
                chatgpt_ask "${request_revise}\n\n${message}"
                # do not use when having more than 1 alrnative result:
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "restructure")
                temperature="0.6"
                chatgpt_ask "${request_restructure}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "s"|"summarize")
                temperature="0.6"
                chatgpt_ask "${request_summarize}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "simplify")
                temperature="0.6"
                chatgpt_ask "${request_simplify}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "humanize")
                temperature="0.9"
                chatgpt_ask "${request_humanize}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "nosell")
                temperature="0.9"
                chatgpt_ask "${request_nosell}\n\n${message}"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "organize")
                chatgpt_ask "${request_organize}\n\n${message}"
                #result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "propose")
                temperature="0.6"
                chatgpt_ask "${request_propose}\n\n${message}"
                # Prepend the word Idea before the idea / concept header:
                if echo "$answer_g" | head -1 | grep -qsiE "^( |,|\.)" ; then
                    answer_g="$( printf "%s\n" "$answer_g" | sed -e 's|^, ||g' -e 's|^ ||g' -e "1s/^/$( eval_gettext "IDEA" ): /" -e '2s/^/\n/' )"
                fi
                result_show_dialog_copy "$answer_g"
                ;;
            "expand")
                temperature="0.6" # we need accurate information, so reduce randomness
                chatgpt_ask "${request_expand}\n\n${message}"
                # Prepend the word Idea before the idea / concept header:
                if echo "$answer_g" | head -1 | grep -qsiE "^( |,|\.)" ; then
                    answer_g="$( printf "%s\n" "$answer_g" | sed -e 's|^, ||g' -e 's|^ ||g' -e "1s/^/$( eval_gettext "IDEA" ): /" -e '2s/^/\n/' )"
                fi
                result_show_dialog_copy "$answer_g"
                ;;
            "idea")
                temperature="0.6" # we need accurate information, so reduce randomness
                chatgpt_ask "${request_idea}\n\n${message}"
                # Prepend the word Idea before the idea / concept header:
                if echo "$answer_g" | head -1 | grep -qsiE "^( |,|\.)" ; then
                    answer_g="$( printf "%s\n" "$answer_g" | sed -e 's|^, ||g' -e 's|^ ||g' -e "1s/^/$( eval_gettext "IDEA" ): /" -e '2s/^/\n/' )"
                fi
                result_show_dialog_copy "$answer_g"
                ;;
            "product")
                chatgpt_ask "${request_product} ${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "seo")
                temperature="0.93"
                if ! echo "$message" | grep -qs ".*#.*#" ; then
                    el_error_wrapper "Your request must be used as 'company # keyword # description', for example:  elive # powerful # elive is a fast and beautiful OS"
                    exit 1
                fi
                local company keyword description
                company="$( echo "$message" | awk -v FS="#" '{print $1}' )"
                read -r company <<< "$company"
                keyword="$( echo "$message" | awk -v FS="#" '{print $2}' )"
                read -r keyword <<< "$keyword"
                description="$( echo "$message" | awk -v FS="#" '{print $3}' )"
                read -r description <<< "$description"

                chatgpt_ask "${request_seo_googlewidget} Company Name: $company\nProduct/Service Description: $description\nKeyword: $keyword"

                result_show_dialog_copy "$answer_g"
                ;;
            "recipe")
                temperature="0.9"
                chatgpt_ask "${request_recipe} ${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "ux")
                temperature="0.6"
                chatgpt_ask "${request_ux}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "explain")
                temperature="0.6"
                chatgpt_ask "${request_explain}\n\n${message}"
                result_show_notification "$( eval_gettext "Explanation:" )" "$answer_g"
                #result_show_dialog_copy "$answer_g"
                ;;
            "ocrfix")
                temperature="0.6"
                chatgpt_ask "${request_ocrfix}\n\n${message}"
                answer_g="$( echo "$answer_g" | sort -u | sed -e 's|^Correction:||g' )"
                result_show_compare "$message" "$answer_g"
                result_show_dialog_copy "$answer_g"
                ;;
            "title")
                temperature="0.9"
                chatgpt_ask "${request_title}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "titlepost")
                temperature="0.9"
                chatgpt_ask "${request_titlepost} ${message}"
                result_show_dialog_copy "Description: ${message} ${answer_g# }"
                ;;
            "intro")
                max_completion_tokens="2048"
                chatgpt_ask "${request_intro} ${message}"
                # Prepend the word Idea before the idea / concept header:
                if echo "$answer_g" | head -1 | grep -qsiE "^( |,|\.)" ; then
                    answer_g="$( printf "%s\n" "$answer_g" | sed -e 's|^, ||g' -e 's|^ ||g' -e "1s/^/$( eval_gettext "IDEA" ): /" -e '2s/^/\n/' )"
                fi
                result_show_dialog_copy "$answer_g"
                ;;
            "story")
                chatgpt_ask "${request_storyteller}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "friend")
                temperature="0.9"
                chatgpt_ask "${request_friend}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "yoda")
                temperature="0.9"
                chatgpt_ask "${request_yoda}\n\n${message}"
                #result_show_notification "$( eval_gettext "Explanation:" )" "$answer_g"
                if ((is_interactive)) && ! ((is_gui_wanted)) ; then
                    if ((is_console)) ; then
                        result_show_dialog_copy "$answer_g"
                    else
                        result_show_dialog_copy "$answer_g" | cowsay -f yoda
                    fi
                else
                    result_show_dialog_copy "$answer_g"
                fi
                ;;
            "therapist")
                chatgpt_ask "${request_therapist}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "doctor")
                temperature="0.5"
                chatgpt_ask "${request_doctor}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "inspireme")
                chatgpt_ask "${request_inspireme}\n\n${message}"
                result_show_notification "$( eval_gettext "Personal dedication" )" "$answer_g"
                ;;
            "suggest-book"|"suggestbook")
                chatgpt_ask "${request_suggestbook}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "elivehow")
                temperature="0.9"
                chatgpt_ask "${request_elivehow} ${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "techwriter")
                chatgpt_ask "${request_techwriter}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "copywriter")
                chatgpt_ask "${request_copywriter}\n\n${message}"
                result_show_dialog_copy "$answer_g"
                ;;
            "domain")
                temperature="0.6"
                chatgpt_ask "${request_domain}\n\n${message}"
                answer_g="$( echo "$answer_g" | sort -u | sed -e '/^$/d' )"
                result_show_dialog_copy "$answer_g"
                ;;
            "girlfriend")
                temperature="1.0"
                chatgpt_ask "${request_girlfriend}\n\n${message}"
                #result_show_notification "$( eval_gettext "Explanation:" )" "$answer_g"
                if ((is_interactive)) && ! ((is_gui_wanted)) ; then
                    if ((is_console)) ; then
                        result_show_dialog_copy "$answer_g"
                    else
                        result_show_dialog_copy "$answer_g" | cowsay -f poison-ivy
                    fi
                else
                    result_show_dialog_copy "$answer_g"
                fi
                ;;

            "code")
                # NOTE: currently working but the results are not reliable, better to use chatgpt directly instead
                if ! echo "$message" | grep -qsiE "^In \w+" ; then
                    el_error_wrapper "Your request must start by the language requested, for example: In ruby, how to calculate..."
                    exit 1
                fi
                temperature="0.6"
                #model="code-davinci-002"
                #chatgpt_ask "\"\"\"\n${request_code}\n\n${message}\n\"\"\""
                #chatgpt_ask "\"\"\"\n${request_code} ${message}\n\"\"\""
                chatgpt_ask "${request_code}\n\n${message}"
                #chatgpt_ask "${request_code} ${message}"
                answer_g="$( echo "$answer_g" | sed -e "/<code>/d" -e "/<\/code>/d" -e "/^\`\`/d" )"
                if ((is_interactive)) && ! ((is_gui_wanted)) ; then
                    lang="$( echo "$message" | cut -d" " -f2 | sed -E -e 's#(,|\.|:|;)\s*$##g' )"
                    lang="${lang,,}"
                    printf "%s\n" "$answer_g" | batcat -p --paging never --color always --language $lang
                else
                    result_show_dialog_copy "$answer_g"
                fi
                ;;



            "test")
                count=0
                # XXX use this value for faster tests: it jumps X number of tests
                #jump_to=34

                el_info "running in tests mode:"

                el_info "\nType your requests on each loop:"
                while read -ru 3 mode
                do
                    echo -e "$mode"
                    count="$(( $count + 1 ))"
                    [[ -n "$jump_to" ]] && [[ "$jump_to" -ge "$count" ]] && continue

                    mode="$( echo "$mode" | awk '{print $1}' )"
                    mode="${mode%:}"

                    read -e message
                    [[ -z "$message" ]] && continue

                    "$SOURCE" "$mode" "$message"

                    while true ; do
                        if el_confirm "Successful?" ; then
                            break
                        else
                            el_info "Trying a new answer..."
                            "$SOURCE" "$mode" "$message"
                        fi
                    done
                done 3<<< "$( "$SOURCE" --help | grep -E "^  [[:alpha:]].*:\s+" | grep -v "GUI:" )"
                ;;
            "--free-mode"|"--free")
                #el_info "using free mode"
                temperature="0.8"
                chatgpt_ask "$message"
                result_show_dialog_copy "$answer_g"
                ;;
            *)
                NOREPORTS=1 el_warning "you have not passed any argument"
                el_info "using free mode instead"
                chatgpt_ask "$mode $message"
                result_show_dialog_copy "$answer_g"
                ;;
        esac

        message="$answer_g"
    done

    if ((is_wait_needed)) ; then
        el_debug "waiting a few seconds to not lose our copied text..."
        sleep 60
    fi

}



el_error_wrapper(){
    local message
    message="$@"
    message="${message//\"/\'}"

    if [[ -z "$message" ]] || [[ "$message" = "null" ]] ; then
        return 1
    fi


    if ((is_interactive)) && ! ((is_gui_wanted)) ; then
        el_error "$message"
    else
        el_error "$message"
        message="${message//</(}"
        message="${message//>/)}"
        $guitool --error --title="$message_title" --text="$message"
    fi
}


#
#  MAIN
#
main "$@"

# vim: set foldmethod=marker :
