#!/bin/sh
##FIX FOR IGNORE DIRS DEFAULT
#find . -name "$1" -not -path "./tmp/*" -not -path "./github log data/*"



# fname: find files or directories by name with options
# Usage: fname [options] [directory] [name-pattern]

helpFile="$HOME/lbin/fname.txt"

show_help() {
    err_msg=$1  # optional

    if [ -n "$err_msg" ]; then
        printf 'Error: %s\n\n' "$err_msg" >&2
    fi

    if [ -f "$helpFile" ]; then
        cat "$helpFile"
    else
        cat <<EOF
Usage:
  fname [options] [directory] [name-pattern]

Arguments:
  directory      Directory to search from (default: current directory).
  name-pattern   Name or substring to match (case-insensitive by default).

Type selection:
  -f             Files only (default).
  -d             Directories only.
  -a             Both files and directories.

Name matching:
  -x             Exact name match.
  (default)      Substring match (*pattern*), case-insensitive.

Extension filter (files only):
  -e EXT         Only files with extension EXT (e.g., -e txt -> *.txt).

Other filters and format:
  -n             Files with no extension (ignores -e and name-pattern).
  -l             Long listing (ls -l style).
  -j             JSON output (array of paths).

Sorting:
  -s mode        newest | oldest | size | name

Verbosity:
  -q             Quiet: only matching paths (and JSON if -j).
  -v             Verbose: show search description and summary (default).

Examples:
  fname src controller
  fname -d . config
  fname -n /tmp
  fname -e php src user
  fname -e txt .
  fname -s newest -l . txt
EOF
    fi

    if [ -n "$err_msg" ]; then
        printf '\nError: %s\n' "$err_msg" >&2
        exit 1
    else
        exit 0
    fi
}

error() {
    show_help "$1"
}

# No arguments: just show usage (not an error)
if [ "$#" -eq 0 ]; then
    show_help ""
fi

# OS-specific stat flags
case "$(uname 2>/dev/null)" in
    Darwin)
        STAT_MTIME='stat -f %m'
        STAT_SIZE='stat -f %z'
        ;;
    Linux)
        STAT_MTIME='stat -c %Y'
        STAT_SIZE='stat -c %s'
        ;;
    *)
        error "Unsupported OS: $(uname 2>/dev/null)"
        ;;
esac

# Defaults
fileType="f"
allTypes=0
longListing=0
exactMatch=0
noExt=0
jsonOut=0
sortMode=""
verbose=1
extFilter=""

# Options
while getopts "hdflnxjs:aqve:" opt; do
    case "$opt" in
        h) show_help "" ;;
        d) fileType="d" ;;
        f) fileType="f" ;;
        a) allTypes=1 ;;
        l) longListing=1 ;;
        n) noExt=1 ;;
        x) exactMatch=1 ;;
        j) jsonOut=1 ;;
        s) sortMode=$OPTARG ;;
        q) verbose=0 ;;
        v) verbose=1 ;;
        e) extFilter=$OPTARG ;;
        \?) error "Invalid option: -$OPTARG" ;;
    esac
done
shift $((OPTIND - 1))

# Positionals
dirName="."
fileName=""

if [ $# -eq 1 ]; then
    if [ -d "$1" ]; then
        dirName=$1
    else
        fileName=$1
    fi
elif [ $# -ge 2 ]; then
    dirName=$1
    fileName=$2
fi

if [ ! -d "$dirName" ]; then
    error "Directory not found: $dirName"
fi

# Description for human messages
search_desc_type="files"
[ "$fileType" = "d" ] && search_desc_type="directories"
[ "$allTypes" -eq 1 ] && search_desc_type="files and directories"

if [ "$noExt" -eq 1 ]; then
    search_pattern="with no extension"
else
    if [ "$exactMatch" -eq 1 ]; then
        if [ -n "$fileName" ]; then
            search_pattern="named exactly '$fileName'"
        else
            search_pattern="with any name (exact mode, no name given)"
        fi
    else
        if [ -n "$fileName" ]; then
            search_pattern="with names containing '$fileName'"
        else
            search_pattern="with any name"
        fi
    fi
fi

if [ -n "$extFilter" ] && [ "$noExt" -ne 1 ]; then
    search_pattern="$search_pattern and extension '.$extFilter'"
fi

case "$sortMode" in
    newest) search_sort="sorted by newest modified first" ;;
    oldest) search_sort="sorted by oldest modified first" ;;
    size)   search_sort="sorted by size (largest first)" ;;
    name)   search_sort="sorted alphabetically by name" ;;
    "")     search_sort="unsorted (find order)" ;;
    *)      error "Unknown sort mode: $sortMode" ;;
esac

if [ "$verbose" -eq 1 ]; then
    printf 'Searching "%s" for %s %s, %s.\n' \
        "$dirName" "$search_desc_type" "$search_pattern" "$search_sort" >&2
fi

# Build find expression
if [ "$noExt" -eq 1 ]; then
    nl_paths=$(find "$dirName" -type f ! -name "*.*" -print 2>/dev/null)
else
    # name pattern
    if [ "$exactMatch" -eq 1 ]; then
        pattern=${fileName:-*}
    else
        [ -n "$fileName" ] && pattern="*${fileName}*" || pattern="*"
    fi

    # extension pattern
    extGlob=""
    if [ -n "$extFilter" ]; then
        extGlob="*.${extFilter}"
    fi

    if [ "$allTypes" -eq 1 ]; then
        if [ -n "$extGlob" ]; then
            nl_paths=$(find "$dirName" \( -type f -iname "$pattern" -iname "$extGlob" -o -type d -iname "$pattern" \) -print 2>/dev/null)
        else
            nl_paths=$(find "$dirName" \( -type f -o -type d \) -iname "$pattern" -print 2>/dev/null)
        fi
    else
        if [ "$fileType" = "d" ]; then
            nl_paths=$(find "$dirName" -type d -iname "$pattern" -print 2>/dev/null)
        else
            if [ -n "$extGlob" ]; then
                nl_paths=$(find "$dirName" -type f -iname "$pattern" -iname "$extGlob" -print 2>/dev/null)
            else
                nl_paths=$(find "$dirName" -type f -iname "$pattern" -print 2>/dev/null)
            fi
        fi
    fi
fi

# Count
result_count=0
if [ -n "$nl_paths" ]; then
    result_count=$(printf '%s\n' "$nl_paths" | wc -l | tr -d ' ')
fi

if [ "$result_count" -eq 0 ]; then
    if [ "$verbose" -eq 1 ]; then
        printf 'No matches found in "%s" for %s %s, %s.\n' \
            "$dirName" "$search_desc_type" "$search_pattern" "$search_sort" >&2
    fi
    exit 0
fi

# Sorting
if [ -n "$sortMode" ]; then
    case "$sortMode" in
        newest|oldest)
            nl_paths=$(
                printf '%s\n' "$nl_paths" |
                awk -v cmd="$STAT_MTIME" '
                    {
                        c = cmd " \"" $0 "\""
                        if ((c | getline m) > 0) {
                            close(c)
                            if (m ~ /^[0-9]+$/)
                                print m "\t" $0
                        }
                    }
                ' | {
                    if [ '"$sortMode"' = "newest" ]; then
                        sort -rn
                    else
                        sort -n
                    fi
                } | cut -f2
            )
            ;;
        size)
            nl_paths=$(
                printf '%s\n' "$nl_paths" |
                awk -v cmd="$STAT_SIZE" '
                    {
                        c = cmd " \"" $0 "\""
                        if ((c | getline sz) > 0) {
                            close(c)
                            if (sz ~ /^[0-9]+$/)
                                print sz "\t" $0
                        }
                    }
                ' | sort -rn | cut -f2
            )
            ;;
        name)
            nl_paths=$(
                printf '%s\n' "$nl_paths" | sort
            )
            ;;
    esac
fi

# Output matches
if [ "$jsonOut" -eq 1 ]; then
    printf '%s\n' "$nl_paths" |
    awk '
        BEGIN {
            print "["
            sep = ""
        }
        {
            gsub(/\\/,"\\\\", $0)
            gsub(/"/,"\\\"", $0)
            printf "%s\"%s\"", sep, $0
            sep = ","
        }
        END {
            print "]"
        }
    '
elif [ "$longListing" -eq 1 ]; then
    printf '%s\n' "$nl_paths" | while IFS= read -r p; do
        [ -n "$p" ] && ls -ld -- "$p"
    done
else
    printf '%s\n' "$nl_paths"
fi

# Brief summary (stderr) in verbose mode only
if [ "$verbose" -eq 1 ]; then
    printf 'Found %d %s in "%s" %s, %s.\n' \
        "$result_count" "$search_desc_type" "$dirName" "$search_pattern" "$search_sort" >&2
fi

