#!/bin/bash

script_link='https://codeberg.org/Application-Maker/pacman-mirrors-helper/raw/branch/main/pacman-mirrors-helper.sh'

# Default configuration
pacman_config_file='/etc/pacman.conf'
mirrorlist_folder='/etc/pacman.d'

# ANSI color codes for colored output
NONE='\e[0m'      # Reset color
LRED='\e[1;31m'   # Warning/Error
LGREEN='\e[1;32m' # Success
LBLUE='\e[1;34m'  # Question/Additional information

# List of repository names to choose from
repolist=("Arch" "ArchArm" "ArchLinuxCN" "Artix" "AthenaOS" "BlackArch" "CachyOS" "Chaotic-AUR" "EndeavourOS" "RebornOS")
mirrorlists=("https://archlinux.org/mirrorlist/?protocol=http&protocol=https&use_mirror_status=on" "https://raw.githubusercontent.com/archlinuxarm/PKGBUILDs/master/core/pacman-mirrorlist/mirrorlist" "https://raw.githubusercontent.com/archlinuxcn/mirrorlist-repo/master/archlinuxcn-mirrorlist" "https://gitea.artixlinux.org/packages/artix-mirrorlist/raw/branch/master/mirrorlist" "https://raw.githubusercontent.com/Athena-OS/athena/main/packages/os-specific/system/athena-mirrorlist/athena-mirrorlist" "https://www.blackarch.org/blackarch-mirrorlist" "https://raw.githubusercontent.com/CachyOS/CachyOS-PKGBUILDS/master/cachyos-mirrorlist/cachyos-mirrorlist" "https://raw.githubusercontent.com/chaotic-aur/pkgbuild-chaotic-mirrorlist/main/mirrorlist" "https://raw.githubusercontent.com/endeavouros-team/PKGBUILDS/master/endeavouros-mirrorlist/endeavouros-mirrorlist" "https://raw.githubusercontent.com/RebornOS-Developers/rebornos-mirrorlist/main/reborn-mirrorlist")

# List of arch regions
declare -rA regions=(["Albania"]="AL" ["Australia"]="AU" ["Austria"]="AT" ["Azerbaijan"]="AZ" ["Bangladesh"]="BD" ["Belarus"]="BY" ["Belgium"]="BE" ["Brazil"]="BR" ["Bulgaria"]="BG" ["Cambodia"]="KH" ["Canada"]="CA" ["Chile"]="CL" ["China"]="CN" ["Colombia"]="CO" ["Croatia"]="HR" ["Czechia"]="CZ" ["Denmark"]="DK" ["Ecuador"]="EC" ["Estonia"]="EE" ["Finland"]="FI" ["France"]="FR" ["Georgia"]="GE" ["Germany"]="DE" ["Greece"]="GR" ["Hong Kong"]="HK" ["Hungary"]="HU" ["Iceland"]="IS" ["India"]="IN" ["Indonesia"]="ID" ["Iran"]="IR" ["Israel"]="IL" ["Italy"]="IT" ["Japan"]="JP" ["Kazakhstan"]="KZ" ["Kenya"]="KE" ["Latvia"]="LV" ["Lithuania"]="LT" ["Luxembourg"]="LU" ["Mauritius"]="MU" ["Mexico"]="MX" ["Moldova"]="MD" ["Morocco"]="MA" ["Nepal"]="NP" ["Netherlands"]="NL" ["New Caledonia"]="NC" ["New Zealand"]="NZ" ["North Macedonia"]="MK" ["Norway"]="NO" ["Paraguay"]="PY" ["Poland"]="PL" ["Portugal"]="PT" ["Romania"]="RO" ["Russia"]="RU" ["Réunion"]="RE" ["Saudi Arabia"]="SA" ["Serbia"]="RS" ["Singapore"]="SG" ["Slovakia"]="SK" ["Slovenia"]="SI" ["South Africa"]="ZA" ["South Korea"]="KR" ["Spain"]="ES" ["Sweden"]="SE" ["Switzerland"]="CH" ["Taiwan"]="TW" ["Thailand"]="TH" ["Türkiye"]="TR" ["Ukraine"]="UA" ["United Arab Emirates"]="AE" ["United Kingdom"]="GB" ["United States"]="US" ["Uzbekistan"]="UZ" ["Vietnam"]="VN")
sorted_region_names=("Albania" "Australia" "Austria" "Azerbaijan" "Bangladesh" "Belarus" "Belgium" "Brazil" "Bulgaria" "Cambodia" "Canada" "Chile" "China" "Colombia" "Croatia" "Czechia" "Denmark" "Ecuador" "Estonia" "Finland" "France" "Georgia" "Germany" "Greece" "Hong Kong" "Hungary" "Iceland" "India" "Indonesia" "Iran" "Israel" "Italy" "Japan" "Kazakhstan" "Kenya" "Latvia" "Lithuania" "Luxembourg" "Mauritius" "Mexico" "Moldova" "Morocco" "Nepal" "Netherlands" "New Caledonia" "New Zealand" "North Macedonia" "Norway" "Paraguay" "Poland" "Portugal" "Romania" "Russia" "Réunion" "Saudi Arabia" "Serbia" "Singapore" "Slovakia" "Slovenia" "South Africa" "South Korea" "Spain" "Sweden" "Switzerland" "Taiwan" "Thailand" "Türkiye" "Ukraine" "United Arab Emirates" "United Kingdom" "United States" "Uzbekistan" "Vietnam")



# Function to hide su's output when running as root
function surun {
    builtin echo "$password" | su root -c "$@" 2>/dev/null
}

# Process arguments for further usage in the script, $@ is the input data to process
function process_args {
    args=("$@")
    for ((i = 0; i < ${#args[@]}; i++)); do
        args[i]=${args[$i],,}   # Convert to lowercase, AA-BB -> aa-bb
        args[i]=${args[$i]//-/} # Remove dashes, aa-bb -> aabb
        echo "${args[$i]}"
    done
}


# An interactive menu for the user, $1 is the title(can be empty), $2 determines if multiple options can be selected, $@ except for $1 and $2 are the options for the multiple choice
function multiple_choice {
    title=$1
    multiple=$2
    options=("${@:3}")
    cursor=0
    shift=0
    choices=()
    selected=false
    prefix=""
    if [ -z "$title" ]; then
        title="Choose an option"
        if [ -n "$multiple" ]; then
            title="$title(s)"
        fi
    fi

    height=$(tput lines)
    width=$(tput cols)
    # Account for header and footer
    height=$((height - 2))

    multiplecontrols=""
    if [ -n "$multiple" ]; then
        multiplecontrols="space or Enter(return) - Choose, "
    fi
    controls="  '/\' - Up, '\/' - Down, ${multiplecontrols}'->' - Proceed"
    if [ "$width" -lt ${#controls} ]; then
        controls=""
    fi

    while ! $selected; do
        clear
        header=$title
        if [ $shift -gt 0 ]; then
            header="/\\" # not '/\' because that messes up syntax highlighting in some editors
        fi
        footer=""
        echo "$header"
        for ((option = shift; option <= $((${#options[@]} - 1)) && option <= $((shift + (height - 1))); option++)); do
            if [ -n "$multiple" ]; then
                prefix=" -"
                for choice in "${choices[@]}"; do
                    if [ "$choice" == "$option" ]; then
                        prefix=" +"
                    fi
                done
            fi

            cursor_sign=" "
            if [ $cursor == "$option" ]; then
                cursor_sign=">"
            fi

            echo "$cursor_sign$prefix ${options[option]}"
        done

        if [ $shift -lt $((${#options[@]} - height)) ]; then
            footer="\/  "
        fi
        echo -n "$footer$controls"

        read -rsn1 key
        case $key in
            A) # UpArrow
                if [ $cursor == $shift ] && [ $cursor -ge 1 ]; then
                    shift=$((shift - 1))
                elif [ $cursor == 0 ]; then
                    if [ $shift == 0 ] && [ ${#options[@]} -gt $height ]; then
                        shift=$((${#options[@]} - height))
                    fi
                    cursor=${#options[@]}
                fi
                cursor=$((cursor - 1))
                ;;
            B) # DownArrow
                # height-1 because curses counts height starting with 1 and arrays start with 0
                if [ $((cursor - shift)) == $((height - 1)) ]; then
                    shift=$((shift + 1))
                    if [ $shift -ge $((${#options[@]} - (height - 1))) ]; then
                        shift=0
                    fi
                fi
                if [ $cursor == $((${#options[@]} - 1)) ]; then
                    cursor=-1
                fi
                cursor=$((cursor + 1))
                ;;

            C) # RightArrow
                if [ -z "$multiple" ] || [ ${#choices[@]} -ge 1 ]; then
                    selected=true # break doesn't work here for some reason
                fi
                ;;
            "") # Space or enter(return)
                if [ -n "$multiple" ]; then
                    found=false
                    for ((i = 0; i <= ${#choices[@]}; i++)); do
                        if [ $cursor == "${choices[i]}" ]; then
                            found=true
                            unset 'choices[i]'
                            choices=("${choices[@]}") # reindex the array
                            break
                        fi
                    done
                    ! "$found" && choices+=("$cursor")
                fi
                ;;
        esac
    done

    if [ -n "$multiple" ]; then
        for ((i = 0; i <= $((${#choices[@]} - 1)); i++)); do
            choices[i]=${options[choices[i]]}
        done
    else
        choices=("${options[cursor]}")
    fi
}

# Help function for printing usage
function print_usage {
    echo "
Usage: pacman-mirrors-helper.sh [repository] [options]

A TUI/CLI tool to manage Arch repository mirrors and configuration.

Supported Repositories:
   ${repolist[*]}

Key Options:
   custom                    Rate mirrors for custom repo
   {repo} limit [n]          Set mirror limit (Default: 30)
   mirrorlist-folder {path}  Custom mirrorlist location
   pacman-config {path}      Custom pacman.conf location
   regions {region(s)}       Specify region codes (e.g., GB,NL)
   {repo} remove             Remove specified repo
   verbose                   Show detailed output

Examples:
   # Rate Arch mirrors with regions
   $0 arch regions US,CA

   # Remove Chaotic-AUR repo
   $0 chaotic-aur remove

   # Rate custom repository mirrors
   $0 custom

Notes:
   - Arguments are case/dash insensitive (--custom = Custom)
   - Order matters: 'remove' applies to preceding repo or all if placed first
   - Interactive menu appears if no arguments provided
   - Automatically uses sudo/su for system modifications

WARNING: Modifies system files! Backup /etc/pacman.conf before use.
"
    exit 1
}



[ ! -t 0 ] && echo -e "${LRED}Some functionality is disabled when running through pipe (... | sh ...)
To use the disabled functionality, save and run the script as a file
Disabled functionality:
  Interactive mode(menus)
  Privilege escalation(modifying files as root)
  Custom repos
  Updates$NONE"

# Check for updates using hash comparison
if [ -t 0 ]; then
    TEMP_FILE=$(mktemp)
    # Extract the domain from the update link, http://example.org/example -> https://example.org
    domain=$(echo $script_link | cut -d '/' -f-3)
    # Check if the domain is reachable
    if curl -so /dev/null -m 2 "$domain"; then
        curl -so "$TEMP_FILE" "$script_link"
        # Check if the returned file is a bash script
        if [ "$(head -1 "$TEMP_FILE")" == '#!/bin/bash' ]; then
            # Compare the hashes, assume that the remote script is newer than the local script if hashes don't match
            if [ "$(md5sum <"$TEMP_FILE")" != "$(md5sum <"$0")" ]; then
                # Check if pacman-mirrors-helper is installed as a package
                if pacman -Qs pacman-mirrors-helper >/dev/null; then
                    echo -e "${LRED}A newer version of pacman-mirrors-helper is available, please install it from the AUR$NONE"
                    aur_update=true
                else
                    read -rp "$(echo -en "${LBLUE}A newer version of pacman-mirrors-helper is available and is already downloaded, would you like to install it? (Y/n) $NONE")" choice
                    if [[ $choice != [nN]* ]]; then
                        # Update the script using an external script that copies the updated script to the original path and runs it
                        copy_script=$(mktemp)
                        # SC2016: Expressions don't expand in single quotes
                        # https://www.shellcheck.net/wiki/SC2016
                        #
                        # SC2028: Echo may not expand escape sequences
                        # https://www.shellcheck.net/wiki/SC2028
                        #
                        # shellcheck disable=SC2016 disable=SC2028
                        {
                            echo -n '
                            #!/bin/bash
                            cp "$1" "$2"
                            script=$2
                            shift 2
                            echo -e "\e[1;34mStarting the updated script\e[0m"
                            $script "$@"
                            ' > "$copy_script"
                        }
                        chmod +x "$copy_script"
                        echo -e "${LBLUE}Writing the update...$NONE"
                        # Run the external script
                        $copy_script "$TEMP_FILE" "$0" "$@"
                        exit $?
                    fi
                fi
            fi
        else
            echo -e "${LRED}Couldn't check for updates: invalid response from $script_link$NONE"
        fi
    else
        echo -e "${LRED}Couldn't check for updates: couldn't connect to $domain$NONE"
    fi
    rm "$TEMP_FILE"
fi

# Process the configuration arguments
declare -A action
declare -A limits
remove_all=false
verbose=false
raw_args=("$@")
mapfile -t args < <(process_args "${raw_args[@]}")
for ((i = 0; i < ${#raw_args[@]}; i++)); do
    case $(process_args "${raw_args[i]}") in
        # Sets the maximum of mirrors to be rated, requires a number as an argument, if no number is given, set to 30
        limit)
            # Check if the next argument is a number
            if [[ ${raw_args[((i + 1))]} == [0-9]* ]]; then
                limits["${raw_args[((i - 1))]}"]=$((${raw_args[((i + 1))]}))
                unset 'args[((i+1))]'
            else
                limits["${raw_args[((i - 1))]}"]=30
            fi
            unset 'args[i]' ;;
        # Sets the folder to put new mirrorlists in, requires a path as an argument
        mirrorlistfolder)
            mirrorlist_folder="${raw_args[((i + 1))]}"
            unset 'args[((i+1))]'
            unset 'args[i]' ;;
        # Sets the file to put new repo configuration in, requires a path as an argument
        pacmanconfig)
            pacman_config_file="${raw_args[((i + 1))]}"
            unset 'args[((i+1))]'
            unset 'args[i]' ;;
        # Sets the regions for Arch mirror rating, requires a comma-separated list of regions as an argument
        regions)
            # Check if the next argument contains ',' or if it's a single region (2 symbols)
            if [[ ${raw_args[((i + 1))]} == *,* || ${#raw_args[((i + 1))]} == 2 ]]; then
                # Check if all regions are valid
                for region in ${raw_args[((i + 1))]//,/ }; do
                    if [[ ! ${regions[*]} =~ ${region^^} ]]; then
                        echo -e "${LRED}Invalid region: $region${NONE}"
                        print_usage
                    fi
                done
                # Capitalize the regions
                raw_args[i+1]=${raw_args[((i + 1))]^^}
                # Add '&country=' to the start of each region, AA,BB -> &country=AA&country=BB
                chosenregions="&country=""${raw_args[((i + 1))]//,/'&country='}"
                unset 'args[((i+1))]'
            else
                echo -e "${LRED}No regions specified!$NONE"
            fi
            unset 'args[i]' ;;
        # Instead of rating mirrors and adding, remove the repo from the pacman configuration file
        remove)
            # If remove is the first argument, assume remove for every repo
            if [ $i == 0 ]; then
                remove_all=true
            else
                if [ "${raw_args[((i - 1))]}" != 'custom' ]; then
                    action["${raw_args[((i - 1))]}"]='remove'
                    args[i-1]=${raw_args[((i-1))]}
                else
                    echo -e "${LRED}Don't use remove with custom, use the original repo name instead.$NONE"
                    unset 'args[((i-1))]'
                fi
            fi
            unset 'args[i]' ;;
        # Increase the verbosity of the output
        verbose)
            verbose=true
            unset 'args[i]' ;;
    esac
done
# Assume that the only arguments left are repos or 'custom'
repos=("${args[@]}")
if $remove_all; then
    for repo in "${repos[@]}"; do
        action["$repo"]='remove'
    done
else
    # Check that all repositories to add are valid
    for repo in "${repos[@]}"; do
        if [[ ! $(process_args "${repolist[*]}") =~ $repo ]] && [ "$repo" != "custom" ] && [ "${action["$repo"]}" != 'remove' ]; then
            echo -e "${LRED}Unknown repository: $repo$NONE"
            print_usage
        fi
    done
fi

# Show an interactive menu if the arguments don't contain any repo names
if [ -z "${repos[*]}" ]; then
    [ ! -t 0 ] && echo -e "${LRED}Interactive mode is not available when running through pipe\nPlease specify a repository by using '... | sh -s {repo}'$NONE" && exit 1
    [ ! "$(clear)" ] && echo -e "${LRED}Interactive menu requires ncurses to be installed. Please specify a repository or install ncurses.$NONE"
    [ "$aur_update" ] && sleep 4
    multiple_choice 'Choose a repo(s)' multiple "${repolist[@]}" "Custom"
    mapfile -t repos < <(process_args "${choices[@]}")
    # Check if regions are specified in the arguments
    if [ -z "${chosenregions[*]}" ]; then
        # If arch selected, select arch region(s)
        for repo in "${!repos[@]}"; do
            if [ "${repos[repo]}" == "arch" ]; then
                multiple_choice 'Choose a region(s)' multiple All "${sorted_region_names[@]}"
                # Arch mirrorlist generator outputs all regions when none are specified
                if ! [[ ${choices[*]} =~ All ]]; then
                    for region in "${choices[@]}"; do
                        chosenregions+="&country=${regions["$region"]}"
                    done
                fi
                break
            fi
        done
    fi
    if $remove_all; then
        action["$repo"]='remove'
    else
        for repo in "${repos[@]}"; do
            # For every chosen repo that is already in the pacman config file, ask whether to remove or to update
            if [ "$repo" != arch ] && [[ $(<"$pacman_config_file") == *$\[repo\]* ]]; then
                multiple_choice "Action for $repo" "Add/update" "Remove"
            fi
            # If remove chosen, set the action for the repo to 'remove'
            if [ "${choices[0]}" == Remove ]; then
                action["$repo"]="remove"
            else
                # Validate that the input for limit is a number or empty
                valid=false
                while ! $valid; do
                    clear
                    read -rp "Limit for $repo (default: None): " limit
                    if [[ "$limit" == [0-9]* ]] || [ "$limit" == '' ]; then
                        valid=true
                        limits["$repo"]=$limit
                    else
                        echo -en "${LRED}This field only accepts numbers!$NONE"
                        sleep 1
                    fi
                done
            fi
        done
    fi
    clear
fi


# Figure out a way to run commands as root, if needed
if [ "$(whoami)" == root ] || touch "$pacman_config_file" && touch -c "$mirrorlist_folder/"; then
    asroot=""
else
    # If sudo works, use it
    if sudo echo -e "${LGREEN}sudo: root access granted.$NONE"; then
        asroot=sudo
    else
        # If sudo doesn't work, use su
        [ ! -t 0 ] && echo -e "${LRED}Privilege escalation is not available when running through pipe\nPlease run as root (... | sudo sh -s ...)$NONE" && exit 1
        read -rsp "$(echo -en "${LBLUE}Root password: $NONE")" password # Prompt for and save the root password for further use
        # Check whether the password works
        if ! surun "echo -e '$LGREEN su: root password accepted.$NONE'"; then
            echo -en "${LRED}Wrong password!$NONE"
            exit 1
        fi
        asroot=surun
    fi
fi

# Check if $pacman_config_file is a file
if [ ! -f "$pacman_config_file" ]; then
    echo -e "${LRED}$pacman_config_file (pacman-config) is not a file!$NONE"
    exit 1
fi

# Check if $mirrorlist_folder is a directory
if [ ! -d "$mirrorlist_folder" ]; then
    echo -e "${LRED}$mirrorlist_folder (mirrorlist-folder) doesn't exist or is not a directory!$NONE"
    exit 1
fi

# The loop which rates and installs mirrors or removes them
for repo in "${repos[@]}"; do
    # Handle custom repos
    # TODO: Make custom repos configurable through the cli
    if [[ $repo == custom ]]; then
        [ ! -t 0 ] && echo -e "${LRED}Custom repos are not available when running through pipe$NONE" && exit 1
        read -rp "$(echo -en "${LBLUE}Custom repository name(database file name without .db): $NONE")" repo
        ! $remove_all && read -rp "$(echo -en "${LBLUE}Custom repository mirrorlist link: $NONE")" mirrorlist_link
    fi

    # Follow the repo documentations
    case $repo in
        athenaos)
            repo_db=athena
            repo_mirrorlist=athena
            ;; # (https://athenaos.org/en/configuration/repositories)
        chaoticaur)
            repo_db=chaotic-aur
            repo_mirrorlist=chaotic
            ;; # (https://aur.chaotic.cx/docs)
        rebornos)
            repo_db=Reborn-OS
            repo_mirrorlist=reborn
            ;; # (https://wiki.rebornos.org/en/howto/add-rebornos-repo)
        *)
            repo_db=$repo
            repo_mirrorlist=$repo
            ;;
    esac
    repo_mirrorlist+="-mirrorlist"

    case ${action["$repo"]} in
        remove)
            # Repository removal
            if [[ "$repo" == arch || "$repo" == archarm || "$repo" == artix ]]; then
                echo -e "${LRED}System repositories removal is disabled for your own safety.$NONE"
                continue
            fi
            # Check if the repository is configured in the pacman config file
            if [[ $(<"$pacman_config_file") == *\[$repo_db\]* ]]; then
                # Remove repo section from the pacman config file
                $asroot sed -i "/\[$repo_db\]/,/^$/d" "$pacman_config_file"
                # Remove corresponding mirrorlist file from the mirrorlist folder
                $asroot rm "$mirrorlist_folder/$repo_mirrorlist"
            else
                # If not configured, show error message and exit
                echo -e "${LRED}There is no $repo repo configured!\nCheck $pacman_config_file to see configured repos.$NONE"
            fi
        ;;
        *)
            # Download mirrorlists
            TEMP_FILE=$(mktemp)
            echo -e "${LBLUE}Downloading mirrorlist for $repo...$NONE"
            # Use custom link if specified
            if [ -n "$mirrorlist_link" ]; then
                url="$mirrorlist_link"
            # Download arch mirrorlist
            elif [ "$repo" == arch ]; then
                # TODO: Make an option to only use https mirrors
                # Download mirrors, which support http or https, sorted by score
                url="https://archlinux.org/mirrorlist/?protocol=http&protocol=https&use_mirror_status=on$chosenregions"
            else
                # Download mirrorlists from urls set in mirrorlists array
                for i in "${!repolist[@]}"; do
                    if [ "$(process_args "${repolist[i]}")" == "$repo" ]; then
                        url="${mirrorlists[i]}"
                        break
                    fi
                done
            fi
            if [ -n "$url" ]; then
                $verbose && echo "Downloading from: $url" >>/dev/stderr
                curl -so "$TEMP_FILE" "$url"
            else
                echo -e "${LRED}Something went wrong while downloading mirrorlists for $repo! (No mirrorlist link determined)$NONE"
                continue
            fi

            # Read mirror URLs from the file, only lines which start with Server =
            mapfile -t mirror_urls < <(
                awk '/^$/ {next} /# Server = / || /#Server = / || /Server = / {
                sub(/# Server = /, "");
                sub(/#Server = /, "");
                sub(/Server = /, "");
                print
            } !/#/ {print $3}' "$TEMP_FILE"
            )

            # Limit mirror amount
            if [[ ${limits["$repo"]} != '' && ${#mirror_urls[@]} -gt ${limits["$repo"]} && ${limits["$repo"]} -gt 0 ]]; then
                mirror_urls=("${mirror_urls[@]:0:$((${limits["$repo"]}*2))}") # For some reason it gives half of the actual value in this context
            fi

            # Test mirror speeds and store them in an associative array
            rated=0
            declare -A mirror_speeds
            mirror_speeds=()
            for mirror in "${mirror_urls[@]}"; do
                # Ignore empty mirrors
                [ -z "$mirror" ] && continue
                # Show a counter for rating mirrors, for some reason just doing ${#mirror_urls[@]} shows twice as much mirrors
                if $verbose; then
                    echo -e "${LBLUE}Testing $mirror for $repo $((rated + 1))/$((${#mirror_urls[@]} / 2))$NONE"
                else
                    echo -en "${LBLUE}Rating mirrors for $repo $rated/$((${#mirror_urls[@]} / 2))\r$NONE"
                fi
                # Test mirror speed using curl
                mirror_speeds[$mirror]="$(curl -so /dev/null -m 10 --connect-timeout 10 -w '%{speed_download}\n' "$mirror") $mirror"
                ((rated++))
            done
            ! $verbose && echo -e "${LBLUE}Rating mirrors for $repo $rated/$((${#mirror_urls[@]} / 2))$NONE"

            # Sort mirrors by speed in descending order
            mapfile -t sorted_mirrors < <(for key in "${!mirror_speeds[@]}"; do echo "${mirror_speeds[$key]} $key"; done | sort -rn | awk '{print $2}')
            # Add 'Server = ' to the start of each mirror and save them to a temp file
            for mirror in "${sorted_mirrors[@]}"; do
                echo "Server = $mirror"
            done >"$TEMP_FILE"

            # Check if the temp file contains any mirrors
            if [[ $(<"$TEMP_FILE") != *"Server = http"* ]]; then
                echo -e "${LRED}An error occurred while rating mirrors for $repo! (No mirrors were found)$NONE"
                exit 1
            fi

            # Add mirrors to the corresponding mirrorlist
            if [[ $repo == arch || $repo == archarm || $repo == artix ]]; then
                # Add mirrors to the default mirrorlist
                $asroot install -m644 "$TEMP_FILE" "$mirrorlist_folder/mirrorlist"
                if [[ $repo == artix && ! $(<"$pacman_config_file") == *\[system\]* ]]; then
                    # Add the artix repos to pacman.conf
                    dbnames=('system' 'world' 'lib32')
                    for dbname in "${dbnames[@]}"; do
                        echo -en "\n[$dbname]\nInclude = $mirrorlist_folder/mirrorlist" | $asroot tee -a "$pacman_config_file" >/dev/null
                    done
                    if [[ $(<"$pacman_config_file") == *\[core\]* ]]; then
                        echo -e "${LRED}Please remove vanilla arch repositories(core, extra, multilib) from your pacman.conf.$NONE"
                    fi
                fi
            else
                # Check if the repository is configured in the pacman config file, and if not, add it
                if [[ ! $(<"$pacman_config_file") == *\[$repo_db\]* ]]; then
                    if [ -t 0 ]; then
                        read -rp "$(echo -en "${LBLUE}Do you want to trust mirrors of $repo without a signature? This may fix some issues. (Y/n) $NONE")" choice # Ask if the user wants 'SigLevel = Never'
                        [[ $choice != [nN]* ]] && Siglevel='\nSigLevel = Never'
                    fi
                    echo -e "\n[$repo_db]\nInclude = $mirrorlist_folder/$repo_mirrorlist$Siglevel" | $asroot tee -a "$pacman_config_file" >/dev/null
                fi
                # Install the rated mirrorlist to the appropriate location
                $asroot install -m644 "$TEMP_FILE" "$mirrorlist_folder/$repo_mirrorlist"
            fi
        ;;
    esac
done

echo -e "${LGREEN}Done!$NONE"
