#!/bin/sh

BINDIR=$(dirname "$0")
# shellcheck source=config/caifs/lib/caifslib.sh
. "${BINDIR}"/../lib/caifslib.sh


# Expand '*' to all targets in the collections
# Uses get_collection_paths() and sets run_targets via set_run_targets()
expand_wildcard_targets() {
    expanded_targets=""
    collection_paths="$(get_collection_paths)"

    while [ -n "$collection_paths" ]; do
        caifs_collection="${collection_paths%%:*}"

        if [ -d "$caifs_collection" ]; then
            for target_dir in "$caifs_collection"/*/; do
                [ -d "$target_dir" ] || continue
                target=$(basename "$target_dir")

                # Only include if it has a valid caifs structure
                is_valid_caifs_structure "$target_dir" || continue

                # Add to list if not already present
                case " $expanded_targets " in
                    *" $target "*) ;;  # Already in list
                    *) expanded_targets="$expanded_targets $target" ;;
                esac
            done
        fi

        # Loop control
        if [ "$caifs_collection" = "$collection_paths" ]; then
            collection_paths=""
        else
            collection_paths="${collection_paths#*"${caifs_collection}":}"
        fi
    done

    set_run_targets "${expanded_targets# }"  # Trim leading space
}


print_help() {
    cat << 'EOF'
Usage: caifs [--verbose] [--help] [--version] <command> [options] <targets...>

Config And Installers For Software - a cross-platform dotfile manager and installer.

Commands:
  add <targets...>    Run hooks and create symlinks for targets (alias: run)
  rm <targets...>     Remove symlinks and run removal hooks for targets
  status              Show link status of all targets in collections

Add Command options:
  -d, --directory <path>  Collection directory (can be specified multiple times)
  -l, --links             Only create symlinks, skip hooks
  -h, --hooks             Only run hooks, skip symlinks
  -f, --force             Overwrite existing files/links
  -n, --dry-run           Don't do any linking or run hooks, just print the action
  -r, --link-root <path>  Set an alternative to $HOME as the link destination

Remove Command options:
  -d, --directory <path>  Collection directory (can be specified multiple times)
  -l, --links             Only remove symlinks, skip rm hooks
  -h, --hooks             Only run rm hooks, skip symlink removal
  -n, --dry-run           Don't do any linking or run hooks, just print the action
  -r, --link-root <path>  Set an alternative to $HOME as the link destination

Status Command options:
  -d, --directory <path>  Collection directory (can be specified multiple times)
  -r, --link-root <path>  Set an alternative to $HOME as the link destination

Global options:
  -v, --verbose           Enable debug output
  -h, --help              Show this help message
  --version               Show version number and exit

Environment variables:
  CAIFS_COLLECTIONS       Colon-separated list of collection paths (default: $PWD)
  CAIFS_LINK_ROOT         Destination root for symlinks (default: $HOME)
  CAIFS_VERBOSE           Set to 0 to enable debug output
  CAIFS_RUN_FORCE         Set to 0 to force overwrite existing files/links
  CAIFS_RUN_LINKS         Set to 1 to skip symlinking
  CAIFS_RUN_HOOKS         Set to 1 to skip hooks

Examples:
  caifs add git                       Run git target from current directory
  caifs add git bash -d ~/dotfiles    Run multiple targets from specified collection
  caifs add '*' -d ~/dotfiles         Run all targets from specified collection
  caifs add nvim --links              Only symlink nvim configs, skip hooks
  caifs add docker --hooks            Only run docker hooks, skip symlinks
  caifs add bash --force              Overwrite existing bash config files
  caifs rm git -d ~/dotfiles --links  Remove symlinks for git target
  caifs rm '*' -d ~/dotfiles          Remove all targets from specified collection
  caifs status -d ~/dotfiles          Show link status for all targets
EOF
}

main() {
    log_debug "Parsing Arguments $*"

    if [ $# -eq 0 ]; then
        print_help
        exit 1
    fi

    SUBCOMMAND=$1
    shift

    if [ "$SUBCOMMAND" = "--verbose" ] || [ "$SUBCOMMAND" = "-v" ]; then
        set_verbose 0;
        if [ $# -eq 0 ]; then
            print_help
            exit 1
        fi
        SUBCOMMAND=$1
        shift
    fi
    if [ "$SUBCOMMAND" = "--dry-run" ] || [ "$SUBCOMMAND" = "-n" ]; then
        set_dry_run 0
        SUBCOMMAND=$1
        shift
    fi
    if [ "$SUBCOMMAND" = "--help" ] || [ "$SUBCOMMAND" = "-h" ]; then
        print_help
        exit 0
    fi
    if [ "$SUBCOMMAND" = "--version" ]; then
        echo "$VERSION"
        exit 0
    fi

    case "$SUBCOMMAND" in
        add)
            # Parse 'run' specific options
            SUB_TEMP=$(getopt -o r:nflhd: -l link-root:,dry-run,force,links,hooks,directory: -n "run" -- "$@")
            # shellcheck disable=SC2181
            if [ $? -ne 0 ]; then exit 1; fi
            eval set -- "$SUB_TEMP"

            ARG_CAIFS_COLLECTIONS=""
            while true; do
                case "$1" in
                    -l|--links) set_run_hooks 1; shift ;;
                    -h|--hooks) set_run_links 1; shift ;;
                    # If directory is specified, override any collection paths and just work with this directory
                    -d|--directory)
                        if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                            ARG_CAIFS_COLLECTIONS="${ARG_CAIFS_COLLECTIONS}:$(realpath "$2")"
                        else
                            ARG_CAIFS_COLLECTIONS=$(realpath "$2")
                        fi
                        shift 2
                        ;;
                    -n|--dry-run) set_dry_run 0; shift ;;
                    -r|--link-root) set_link_root "$(realpath "$2")"; shift 2;;
                    -f|--force) set_force 0; shift ;;
                    --) shift; break ;;
                    *) break ;;
                esac
            done

            # All directories have been parsed, so override any pre-set CAIFS_COLLECTIONS entries
            # Otherwise populate the existing CAIFS_COLLECTIONS with standard
            if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                set_collection_paths "$ARG_CAIFS_COLLECTIONS"
            else
                populate_caifs_collections
            fi

            set_run_targets "$*"

            # Expand wildcard to all targets
            if [ "$(get_run_targets)" = "*" ]; then
                expand_wildcard_targets
            fi

            cmd_add "$(get_collection_paths)" "$(get_run_targets)" "$(get_link_root)"
            ;;
        rm)
            SUB_TEMP=$(getopt -o r:nlhd: -l link-root:,dry-run,links,hooks,directory: -n "rm" -- "$@")
            # shellcheck disable=SC2181
            if [ $? -ne 0 ]; then exit 1; fi
            eval set -- "$SUB_TEMP"

            ARG_CAIFS_COLLECTIONS=""
            while true; do
                case "$1" in
                    -d|--directory)
                        if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                            ARG_CAIFS_COLLECTIONS="${ARG_CAIFS_COLLECTIONS}:$(realpath "$2")"
                        else
                            ARG_CAIFS_COLLECTIONS=$(realpath "$2")
                        fi
                        shift 2
                        ;;
                    -l|--links) set_run_hooks 1; shift ;;
                    -h|--hooks) set_run_links 1; shift ;;
                    -n|--dry-run) set_dry_run 0; shift ;;
                    -r|--link-root) set_link_root "$(realpath "$2")"; shift 2;;
                    --) shift; break ;;
                    *) break ;;
                esac
            done

            # All directories have been parsed, so override any pre-set CAIFS_COLLECTIONS entries
            if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                set_collection_paths "$ARG_CAIFS_COLLECTIONS"
            else
                populate_caifs_collections
            fi
            set_run_targets "$*"

            # Expand wildcard to all targets
            if [ "$(get_run_targets)" = "*" ]; then
                expand_wildcard_targets
            fi

            cmd_remove "$(get_collection_paths)" "$(get_run_targets)" "$(get_link_root)"
            ;;
        status)
            SUB_TEMP=$(getopt -o r:d: -l link-root:,directory: -n "status" -- "$@")
            # shellcheck disable=SC2181
            if [ $? -ne 0 ]; then exit 1; fi
            eval set -- "$SUB_TEMP"

            ARG_CAIFS_COLLECTIONS=""
            while true; do
                case "$1" in
                    -d|--directory)
                        if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                            ARG_CAIFS_COLLECTIONS="${ARG_CAIFS_COLLECTIONS}:$(realpath "$2")"
                        else
                            ARG_CAIFS_COLLECTIONS=$(realpath "$2")
                        fi
                        shift 2
                        ;;
                    -r|--link-root) set_link_root "$(realpath "$2")"; shift 2;;
                    --) shift; break ;;
                    *) break ;;
                esac
            done

            if [ -n "$ARG_CAIFS_COLLECTIONS" ]; then
                set_collection_paths "$ARG_CAIFS_COLLECTIONS"
            else
                populate_caifs_collections
            fi

            cmd_status "$(get_collection_paths)" "$(get_link_root)"
            ;;
        *)
            echo "Unknown subcommand: $SUBCOMMAND"
            echo ""
            print_help
            exit 1
            ;;
    esac
}


# command handler for running the remove action
# $1: collection paths to consider
# $2: targets to action
# $3: link root
cmd_remove() {
    collection_paths=$1
    run_targets=$2
    link_root=$3
    log_debug "cmd_remove: begin $*"

    while [ -n "$collection_paths" ]; do
        caifs_collection="${collection_paths%%:*}"

        log_debug "Working with $caifs_collection"

        for target in $run_targets; do
            log_debug "$target"

            remove_target_links "${caifs_collection}" "${target}" "${link_root}"
            run_remove_hook "${caifs_collection}" "$target"

        done
            # Loop control, get the next path to operate on
        if [ "$caifs_collection" = "$collection_paths" ]; then
            # No more paths available. End the loop
            collection_paths=""
        else
            collection_paths="${collection_paths#*"${caifs_collection}":}"
        fi
    done

    log_debug "cmd_remove: end"
}

# Loop over the CAIFS_COLLECTIONS variable, which is colon (:) separated paths to caifs collections
# First target wins, so order in the collection is important.
# $1 collection_paths
# $2 targets - an array of 1 or more targets to add
# $3 link_root
cmd_add() {
    log_debug "cmd_add: begin $*"

    collection_paths="$1"
    run_targets="$2"
    link_root="$3"

    while [ -n "$collection_paths" ]; do

        caifs_collection="${collection_paths%%:*}"

        log_debug "Working with $caifs_collection"

        for target in $run_targets; do
            log_debug "$target"

            run_pre_hook "${caifs_collection}" "$target"

            create_target_links "${caifs_collection}" "${target}" "${link_root}"

            run_post_hook "${caifs_collection}" "$target"
        done

        # Loop control, get the next path to operate on
        if [ "$caifs_collection" = "$collection_paths" ]; then
            # No more paths available. End the loop
            collection_paths=""
        else
            collection_paths="${collection_paths#*"${caifs_collection}":}"
        fi
    done

    log_debug "cmd_add: end"
}

# Display status of all targets in collections
# $1 collection_paths
# $2 link_root
cmd_status() {
    log_debug "cmd_status: begin $*"

    collection_paths=$1
    link_root=$2

    # Detect unicode support, fallback to ASCII
    case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
        *UTF-8*|*utf8*)
            CHECK="✓"; CROSS="✗"; DASH="-"
            ;;
        *)
            CHECK="Y"; CROSS="N"; DASH="-"
            ;;
    esac

    # Color codes for linked column (only if stdout is a terminal)
    if [ -t 1 ]; then
        GREEN='\033[0;32m'
        RED='\033[0;31m'
        NC='\033[0m'
    else
        GREEN=''; RED=''; NC=''
    fi

    # Print header
    printf "%-40s %-20s %-8s %s\n" "COLLECTION" "TARGET" "LINKED" "HOOKS"
    printf "%-40s %-20s %-8s %s\n" "$(printf '%0.s-' $(seq 1 40))" "$(printf '%0.s-' $(seq 1 20))" "------" "-----"

    while [ -n "$collection_paths" ]; do
        caifs_collection="${collection_paths%%:*}"
        collection_name=$(basename "$caifs_collection")

        log_debug "Checking collection: $caifs_collection"

        # Find all targets (directories containing config/ or hooks/ subdirectory)
        if [ -d "$caifs_collection" ]; then
            for target_dir in "$caifs_collection"/*/; do
                [ -d "$target_dir" ] || continue
                target=$(basename "$target_dir")

                log_debug "Checking if target_dir=$target_dir contains a valid structure"
                is_valid_caifs_structure "$target_dir" || continue

                # Check linked status (pad for alignment, color for linked column only)
                if has_config "$target_dir"; then
                    log_debug "target_dir=$target_dir has config files"
                    if is_target_linked "$caifs_collection" "$target" "$link_root"; then
                        linked_status="${GREEN}${CHECK}${NC}       "
                    else
                        linked_status="${RED}${CROSS}${NC}       "
                    fi
                else
                    linked_status="${DASH}       "
                fi

                # Check hooks status (has hooks/*.sh files)
                if has_hooks "$target_dir"; then
                    log_debug "target_dir=$target_dir has hooks"
                    hooks_status="${CHECK}"
                else
                    hooks_status="${CROSS}"
                fi

                printf "%-40s %-20s %b %s\n" "$collection_name" "$target" "$linked_status" "$hooks_status"
            done
        fi

        # Loop control
        if [ "$caifs_collection" = "$collection_paths" ]; then
            collection_paths=""
        else
            collection_paths="${collection_paths#*"${caifs_collection}":}"
        fi
    done
    log_debug "cmd_status : end"
}

main "$@"
