#!/bin/bash
#
# betterzip - Command-line interface for BetterZip
#
# Usage:
#   betterzip compress [--preset NAME] [--format FORMAT] [--password PASS] FILE...
#   betterzip extract  [--preset NAME] [--password PASS] [--destination DIR] FILE...
#   betterzip test     FILE...
#   betterzip list     FILE
#   betterzip info     FILE
#   betterzip open     FILE
#   betterzip help
#
# Install via BetterZip menu > "Install Command-Line Tool…"
#

set -euo pipefail

APP_NAME="BetterZip"

usage() {
    cat <<'USAGE'
betterzip - Command-line interface for BetterZip

USAGE:
  betterzip compress [OPTIONS] FILE...    Create an archive from files
  betterzip extract  [OPTIONS] FILE...    Extract an archive
  betterzip test     FILE...              Test archive integrity
  betterzip list     FILE                 List archive contents
  betterzip info     FILE                 Show archive summary info
  betterzip open     FILE                 Open archive in BetterZip GUI
  betterzip help                          Show this help

COMPRESS OPTIONS:
  --preset NAME       Use a named save preset
  --format FORMAT     Archive format (zip, tar, tgz, tbz, txz, tzst, tbr, 7z, rar, dmg)
  --password PASS     Encrypt the archive with this password
  --destination DIR   Where to create the archive
  --individual        Create a separate archive for each input file

EXTRACT OPTIONS:
  --preset NAME       Use a named extract preset
  --password PASS     Password for encrypted archives
  --destination DIR   Destination folder for extracted files

EXAMPLES:
  betterzip compress file1.txt file2.txt
  betterzip compress --preset "My Preset" *.pdf
  betterzip compress --format 7z --password secret project/
  betterzip extract archive.zip
  betterzip extract --destination ~/Desktop archive.rar
  betterzip test backup.zip
  betterzip list archive.7z
  betterzip info archive.zip
USAGE
}

# Convert format name to AppleScript enum
format_to_applescript() {
    case "$1" in
        zip)    echo "zip" ;;
        tar)    echo "tar" ;;
        tgz)    echo "tgz" ;;
        tbz)    echo "tbz" ;;
        txz)    echo "txz" ;;
        tzst)   echo "tzst" ;;
        tbr)    echo "tbr" ;;
        7z)     echo "sevenzip" ;;
        rar)    echo "rar" ;;
        dmg)    echo "dmg" ;;
        iso)    echo "iso" ;;
        xar)    echo "xar" ;;
        *)      echo ""; return 1 ;;
    esac
}

# Resolve a path to absolute
abspath() {
    if [[ "$1" == /* ]]; then
        echo "$1"
    else
        echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
    fi
}

# Build an AppleScript file list from arguments
build_file_list() {
    local result="{"
    local first=true
    for f in "$@"; do
        local abs
        abs=$(abspath "$f")
        if [ "$first" = true ]; then
            first=false
        else
            result+=", "
        fi
        result+="POSIX file \"$abs\""
    done
    result+="}"
    echo "$result"
}

cmd_compress() {
    local preset=""
    local format=""
    local password=""
    local destination=""
    local individual=false
    local files=()

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --preset)     preset="$2"; shift 2 ;;
            --format)     format="$2"; shift 2 ;;
            --password)   password="$2"; shift 2 ;;
            --destination) destination="$2"; shift 2 ;;
            --individual) individual=true; shift ;;
            -*)           echo "Unknown option: $1" >&2; exit 1 ;;
            *)            files+=("$1"); shift ;;
        esac
    done

    if [ ${#files[@]} -eq 0 ]; then
        echo "Error: No files specified." >&2
        exit 1
    fi

    local file_list
    file_list=$(build_file_list "${files[@]}")

    # Build the options record
    local options_parts=()
    if [ -n "$format" ]; then
        local as_format
        as_format=$(format_to_applescript "$format") || { echo "Error: Unknown format '$format'" >&2; exit 1; }
        options_parts+=("format:$as_format")
    fi
    if [ -n "$password" ]; then
        options_parts+=("password:\"$password\", encryption method:strong")
    fi
    if [ -n "$destination" ]; then
        local abs_dest
        abs_dest=$(abspath "$destination")
        options_parts+=("destination:POSIX file \"$abs_dest\"")
    fi
    if [ "$individual" = true ]; then
        options_parts+=("create individual archives:true")
    fi

    local script="tell application \"$APP_NAME\"\n"
    script+="  queued archive $file_list"
    if [ -n "$preset" ]; then
        script+=" with preset \"$preset\""
    fi
    if [ ${#options_parts[@]} -gt 0 ]; then
        local opts_str
        opts_str=$(IFS=", "; echo "${options_parts[*]}")
        script+=" with options {$opts_str}"
    fi
    script+=" show ui yes\n"
    script+="end tell"

    echo -e "$script" | osascript
    echo "Compression queued."
}

cmd_extract() {
    local preset=""
    local password=""
    local destination=""
    local files=()

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --preset)      preset="$2"; shift 2 ;;
            --password)    password="$2"; shift 2 ;;
            --destination) destination="$2"; shift 2 ;;
            -*)            echo "Unknown option: $1" >&2; exit 1 ;;
            *)             files+=("$1"); shift ;;
        esac
    done

    if [ ${#files[@]} -eq 0 ]; then
        echo "Error: No files specified." >&2
        exit 1
    fi

    local file_list
    file_list=$(build_file_list "${files[@]}")

    local options_parts=()
    if [ -n "$password" ]; then
        options_parts+=("password:\"$password\"")
    fi
    if [ -n "$destination" ]; then
        local abs_dest
        abs_dest=$(abspath "$destination")
        options_parts+=("destination:POSIX file \"$abs_dest\"")
    fi

    local script="tell application \"$APP_NAME\"\n"
    script+="  queued unarchive $file_list"
    if [ -n "$preset" ]; then
        script+=" with preset \"$preset\""
    fi
    if [ ${#options_parts[@]} -gt 0 ]; then
        local opts_str
        opts_str=$(IFS=", "; echo "${options_parts[*]}")
        script+=" with options {$opts_str}"
    fi
    script+=" show ui yes\n"
    script+="end tell"

    echo -e "$script" | osascript
    echo "Extraction queued."
}

cmd_test() {
    if [ $# -eq 0 ]; then
        echo "Error: No files specified." >&2
        exit 1
    fi

    local file_list
    file_list=$(build_file_list "$@")

    local script
    script="tell application \"$APP_NAME\"
  set groupId to queued test $file_list show ui yes
  repeat
    set p to progress groupId
    if p >= 100 then exit repeat
    delay 0.5
  end repeat
  set result to output groupId
end tell"

    local output
    output=$(osascript -e "$script" 2>&1) || true

    if [ -z "$output" ] || [ "$output" = "{}" ] || [ "$output" = "" ]; then
        echo "All archives passed integrity test."
        exit 0
    else
        echo "Test completed. Failed archives: $output"
        exit 1
    fi
}

cmd_list() {
    if [ $# -ne 1 ]; then
        echo "Error: Specify exactly one archive file." >&2
        exit 1
    fi

    local abs
    abs=$(abspath "$1")

    osascript -e "tell application \"$APP_NAME\" to list POSIX file \"$abs\""
}

cmd_info() {
    if [ $# -ne 1 ]; then
        echo "Error: Specify exactly one archive file." >&2
        exit 1
    fi

    local abs
    abs=$(abspath "$1")

    # Use list command to get items, then count
    local items
    items=$(osascript -e "tell application \"$APP_NAME\" to list POSIX file \"$abs\"" 2>/dev/null) || {
        echo "Error: Could not read archive." >&2
        exit 1
    }

    local count=0
    if [ -n "$items" ]; then
        count=$(echo "$items" | tr ',' '\n' | wc -l | tr -d ' ')
    fi

    echo "Archive: $(basename "$1")"
    echo "Items:   $count"
}

cmd_open() {
    if [ $# -ne 1 ]; then
        echo "Error: Specify exactly one archive file." >&2
        exit 1
    fi

    local abs
    abs=$(abspath "$1")

    open -a "$APP_NAME" "$abs"
}

# Main dispatch
if [ $# -eq 0 ]; then
    usage
    exit 0
fi

command="$1"
shift

case "$command" in
    compress|c)  cmd_compress "$@" ;;
    extract|x)   cmd_extract "$@" ;;
    test|t)      cmd_test "$@" ;;
    list|l)      cmd_list "$@" ;;
    info|i)      cmd_info "$@" ;;
    open|o)      cmd_open "$@" ;;
    help|--help|-h) usage ;;
    *)
        echo "Unknown command: $command" >&2
        echo "Run 'betterzip help' for usage." >&2
        exit 1
        ;;
esac
