#!/usr/bin/env bash
#
set -e

help-table() {
    local -a rows
    local max_cmd=7 max_opt=6 max_desc=11

    rows+=("Command|Option|Description")
    rows+=("build-image|[output]|Build Docker image with buildx.")
    rows+=("help|[grep]|Show help for commands.")
    rows+=("init-zensical-docs||Initialize Zensical documentation build dependencies.")
    rows+=("dev-zensical-docs||Run Zensical documentation development server.")
    rows+=("build-zensical-docs||Build the Zensical documentation website.")
    rows+=("serve-zensical-docs||Serve the Zensical documentation build folder.")
    rows+=("clone-taskfile||Clone or pull the taskfile.build repo.")
    rows+=("lint||Lint code with shellcheck.")
    rows+=("run-container||Run Docker image.")
    rows+=("set-git-origin||Change git origin to ssh.")
    rows+=("update-install-script||Update scripts.txt with bin commands.")
    rows+=("update-task-template-help||Update task.template help rows from library.md.")
    rows+=("check-help-width||Show help lines that exceed terminal width.")
    rows+=("version||Show version of required tools.")

    for row in "${rows[@]}"; do
        IFS='|' read -r cmd opt desc <<< "$row"
        (( ${#cmd} > max_cmd )) && max_cmd=${#cmd}
        (( ${#opt} > max_opt )) && max_opt=${#opt}
        (( ${#desc} > max_desc )) && max_desc=${#desc}
    done

    printf '| %-*s | %-*s | %-*s |\n' \
        "$max_cmd" "Command" \
        "$max_opt" "Option" \
        "$max_desc" "Description"

    printf '|-%*s-|-%*s-|-%*s-|\n' \
        "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
        "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
        "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"

    local first_row=true
    for row in "${rows[@]:1}"; do
        IFS='|' read -r cmd opt desc <<< "$row"
        if [[ "$cmd" =~ ^===.*===$ ]]; then
            group_name="${cmd/===/}"
            group_name="${group_name/===/}"

            if [ "$first_row" = true ]; then
                first_row=false
            else
                printf '|-%*s-|-%*s-|-%*s-|\n' \
                    "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
                    "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
                    "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
            fi

            local left_pad=$(( (max_cmd - ${#group_name}) / 2 ))
            local right_pad=$(( max_cmd - left_pad - ${#group_name} ))
            printf '| %*s%s%*s | %*s | %*s |\n' \
                "$left_pad" "" \
                "$group_name" \
                "$right_pad" "" \
                "$max_opt" "" \
                "$max_desc" ""

            printf '|-%*s-|-%*s-|-%*s-|\n' \
                "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
                "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
                "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
        else
            first_row=false
            printf '| %-*s | %-*s | %-*s |\n' \
                "$max_cmd" "$cmd" \
                "$max_opt" "$opt" \
                "$max_desc" "$desc"
        fi
    done
}

help() {
    local grep_pattern
    echo
    if [[ -n "$1" ]]; then
        grep_pattern="$1"
        help-table | grep -i "$grep_pattern" | column -t -s'|'
    else
        echo "task <command> [options]"
        echo
        echo "commands:"
        echo
        help-table
    fi
    echo
}

check-help-width() {
    local term_width
    term_width=$(tput cols 2>/dev/null || echo 80)

    echo "Terminal width: $term_width"
    echo

    local max_line_width=0
    local -a overflow_lines=()

    while IFS= read -r line; do
        local line_width=${#line}
        if (( line_width > max_line_width )); then
            max_line_width=$line_width
        fi
        # Skip table structure lines (separators, headers)
        [[ "$line" =~ ^\|[-]+.* ]] && continue
        [[ "$line" =~ ^\|[[:space:]]*Command[[:space:]]*\| ]] && continue
        # Skip group header rows where option and description are empty
        local -a fields
        IFS='|' read -ra fields <<< "$line"
        local opt_field="${fields[2]:-}"
        local desc_field="${fields[3]:-}"
        [[ -z "${opt_field// /}" && -z "${desc_field// /}" ]] && continue

        if (( line_width > term_width )); then
            overflow_lines+=("$line")
        fi
    done < <(help-table)

    echo "Max table line width: $max_line_width"

    if (( ${#overflow_lines[@]} > 0 )); then
        echo
        echo "Warning: The following commands exceed terminal width ($term_width):"
        for line in "${overflow_lines[@]}"; do
            printf '  %s\n' "$line"
        done
        return 1
    else
        echo "All commands fit within terminal width."
        return 0
    fi
}

clone-taskfile(){
    if [[ ! -d "$HOME/taskfile.build" ]]; then
        echo -e '\033[38;5;214mGit\033[0m: Clone taskfile repo'
        git clone https://git.taskfile.build "$HOME/taskfile.build"
    else
        echo -e '\033[38;5;214mGit\033[0m: Pull taskfile repo'
        git -C "$HOME/taskfile.build" pull
    fi
}

if [[ -f ".env" ]]; then
    # shellcheck source=/dev/null
    source ".env"
fi

if [[ -d "$HOME/taskfile.build/bin" ]]; then
    for file in "$HOME/taskfile.build/bin/"*; do
        if [[ -f "$file" && -x "$file" ]]; then
            # shellcheck disable=SC1090
            source "$file"
        fi
    done
fi

# Static vars

BUILDX_PLATFORM="linux/amd64"
CONTAINER_REGISTRY="mintsystem"
CONTAINER_TAG="taskfile.build:latest"
DOCKERFILE="image/Dockerfile"

# Export variables for external use
#
export BUILDX_PLATFORM
export CONTAINER_REGISTRY
export CONTAINER_TAG
export DOCKERFILE

# Project commands

set-git-origin(){
    echo "Change git origin."
    git remote set-url origin git@codeberg.org:Mint-System/taskfile.build.git
}

run-container() {
    container_name="taskfile-build"
    image_tag="mintsystem/taskfile.build:latest"

    docker pull "$image_tag"
    docker rm -f "$container_name"
    # docker volume rm "$container_name"

    docker run -p 8080:80 -d \
        --name "$container_name" \
        -e GIT_URL="https://git.taskfile.build" \
        -e TASK_COMMANDS="init-zensical-docs,build-zensical-docs" \
        -e CADDY_ROOT="site" \
        -v "$container_name":/app \
        "$image_tag"
    open-url "http://localhost:8080"
}

lint() {
    mapfile -t bin_scripts < <(find bin -maxdepth 1 -type f)
    shellcheck task task.template "${bin_scripts[@]}"
}

version() {
    echo "uv: $(uv --version)"
    echo "shellcheck: $(shellcheck --version)"
}

update-install-script() {
    echo "Update scripts.txt with available bin commands."
    find bin -maxdepth 1 -type f ! -name 'install*' ! -name 'uninstall*' -exec basename {} \; | sort > scripts.txt
    echo "scripts.txt updated successfully."
}

update-task-template-help() {
    local library_file="docs/library.md"
    local template_file="task.template"

    if [[ ! -f "$library_file" ]]; then
        echo "Error: $library_file not found."
        exit 1
    fi

    if [[ ! -f "$template_file" ]]; then
        echo "Error: $template_file not found."
        exit 1
    fi

    local rows_temp
    rows_temp=$(mktemp)
    local in_section=false
    local current_section=""

    while IFS= read -r line || [[ -n "$line" ]]; do
        local marker_pattern='^<!-- ===.*=== -->$'
        if [[ "$line" =~ $marker_pattern ]]; then
            if [[ "$in_section" == false ]]; then
                in_section=true
                current_section="${line#<!-- ===}"
                current_section="${current_section%=== -->}"
                printf '    rows+=("===%s===| | ")\n' "$current_section" >> "$rows_temp"
            else
                in_section=false
                current_section=""
            fi
        elif [[ "$in_section" == true && "$line" == rows+=* ]]; then
            printf '    %s\n' "$line" >> "$rows_temp"
        fi
    done < "$library_file"

    local output_temp
    output_temp=$(mktemp)
    local in_rows_block=false

    while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ "$in_rows_block" == false ]]; then
            printf '%s\n' "$line" >> "$output_temp"
            if [[ "$line" =~ ^[[:space:]]*rows\+=\(\"Command\|Option\|Description\"\) ]]; then
                in_rows_block=true
                cat "$rows_temp" >> "$output_temp"
            fi
        else
            if [[ "$line" =~ ^[[:space:]]*for[[:space:]]+row[[:space:]]+in ]]; then
                in_rows_block=false
                printf '\n%s\n' "$line" >> "$output_temp"
            fi
        fi
    done < "$template_file"

    mv "$output_temp" "$template_file"
    chmod +x "$template_file"
    rm -f "$rows_temp"

    echo "Updated $template_file from $library_file."

    local -a bin_scripts=()
    for file in bin/*; do
        if [[ -f "$file" && -x "$file" ]]; then
            bin_scripts+=("$(basename "$file")")
        fi
    done

    local -a row_commands=()
    while IFS= read -r line; do
        if [[ "$line" =~ rows\+=\(\"([^\"|]+) ]]; then
            local cmd="${BASH_REMATCH[1]}"
            if [[ ! "$cmd" =~ ^===.*===$ && "$cmd" != "Command" ]]; then
                row_commands+=("$cmd")
            fi
        fi
    done < "$library_file"

    local -a missing=()
    for script in "${bin_scripts[@]}"; do
        local found=false
        for cmd in "${row_commands[@]}"; do
            if [[ "$cmd" == "$script" ]]; then
                found=true
                break
            fi
        done
        if [[ "$found" == false ]]; then
            missing+=("$script")
        fi
    done

    local -a orphaned=()
    for cmd in "${row_commands[@]}"; do
        local found=false
        for script in "${bin_scripts[@]}"; do
            if [[ "$cmd" == "$script" ]]; then
                found=true
                break
            fi
        done
        if [[ "$found" == false ]]; then
            orphaned+=("$cmd")
        fi
    done

    if [[ ${#missing[@]} -gt 0 ]]; then
        echo
        echo "Warning: The following bin scripts do not have help rows in $library_file:"
        printf '  %s\n' "${missing[@]}"
    fi

    if [[ ${#orphaned[@]} -gt 0 ]]; then
        echo
        echo "Warning: The following help rows do not have a corresponding bin script:"
        printf '  %s\n' "${orphaned[@]}"
    fi
}

if [[ "$1" == *","* ]]; then
    # Split comma-separated commands
    IFS=',' read -ra COMMANDS <<< "$1"
    for cmd in "${COMMANDS[@]}"; do
        # Trim whitespace
        cmd=$(echo "$cmd" | xargs)
        if declare -f "$cmd" > /dev/null; then
            "$cmd"
        else
            echo "Unknown command: $1"
            help "$@"
            exit 1
        fi
    done
elif declare -f "$1" > /dev/null; then
    "$1" "${@:2}"
else
    case "$1" in
        *)
            echo "Unknown command: $1"
            help
            exit 1
            ;;
    esac
fi
